@vidispine/vdt-videojs 21.3.0 → 22.1.0-pre.1

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.
Files changed (55) hide show
  1. package/README.md +4 -4
  2. package/deprecated-app/README.md +21 -0
  3. package/{demo.html → deprecated-app/index.html} +1 -1
  4. package/{demo.js → deprecated-app/index.js} +69 -41
  5. package/{samples → deprecated-app/samples}/birds-es.vvt +0 -0
  6. package/{samples → deprecated-app/samples}/birds.vvt +0 -0
  7. package/{tests.js → deprecated-app/tests.js} +87 -54
  8. package/{lib/style.css → dist/index.css} +755 -55
  9. package/dist/index.es.js +2099 -0
  10. package/dist/index.js +2112 -0
  11. package/dist/index.umd.js +2115 -0
  12. package/package.json +29 -85
  13. package/rollup.config.js +54 -0
  14. package/src/constants.js +1 -1
  15. package/src/externalControls.js +14 -18
  16. package/src/helpOverlay.js +12 -16
  17. package/src/helpers.js +3 -3
  18. package/src/index.js +3 -9
  19. package/src/index.stories.mdx +59 -0
  20. package/src/menu.api.stories.mdx +59 -0
  21. package/src/menu.js +23 -19
  22. package/src/{menu.example.md → menu.usage.stories.mdx} +31 -21
  23. package/src/osd.js +24 -18
  24. package/src/player.api.stories.mdx +120 -0
  25. package/src/player.js +157 -108
  26. package/src/player.scss +2 -0
  27. package/src/{player.example.md → player.usage.stories.mdx} +18 -8
  28. package/src/seekBar.js +85 -48
  29. package/src/timeCodeDisplay.js +39 -31
  30. package/src/videoTime.js +4 -4
  31. package/build.js +0 -233
  32. package/lib/index.cjs.js +0 -3240
  33. package/lib/index.esm.js +0 -3227
  34. package/lib/index.umd.js +0 -3243
  35. package/lib/package.json +0 -13
  36. package/lib/src/constants.js +0 -51
  37. package/lib/src/externalControls.js +0 -307
  38. package/lib/src/helpOverlay.js +0 -106
  39. package/lib/src/helpers.js +0 -34
  40. package/lib/src/index.js +0 -13
  41. package/lib/src/menu.js +0 -365
  42. package/lib/src/osd.js +0 -91
  43. package/lib/src/player.js +0 -943
  44. package/lib/src/seekBar.js +0 -833
  45. package/lib/src/timeCodeDisplay.js +0 -253
  46. package/lib/src/videoTime.js +0 -66
  47. package/log.js +0 -24
  48. package/src/_index.stories.js +0 -24
  49. package/src/externalControls.stories.js +0 -6
  50. package/src/helpOverlay.stories.js +0 -6
  51. package/src/menu.classMethods.md +0 -47
  52. package/src/player.classMethods.md +0 -108
  53. package/src/player.stories.js +0 -14
  54. package/src/seekBar.stories.js +0 -8
  55. package/tmp/player.css +0 -1
@@ -0,0 +1,2099 @@
1
+ import mousetrap from 'mousetrap';
2
+ import videojs from 'video.js/dist/alt/video.core.min';
3
+
4
+ var name = "@vidispine/vdt-videojs";
5
+ var version = "22.1.0-pre.1";
6
+ var license = "UNLICENSED";
7
+ var main = "dist/index.js";
8
+ var module = "dist/index.es.js";
9
+ var browser = "dist/index.umd.js";
10
+ var author = "Oto Inša <otoinsa@bertelsmann.de>";
11
+ var scripts = {
12
+ build: "rm -rf dist/ && rollup -c",
13
+ "build:watch": "rm -rf dist/*.js* && rollup -c -w --environment DEVELOPMENT",
14
+ lint: "eslint \"./{src/**/*,*}.{js,md}\"",
15
+ "lint:fix": "yarn lint --fix"
16
+ };
17
+ var dependencies = {
18
+ mousetrap: "^1.6.3",
19
+ "video.js": "^7.6.6"
20
+ };
21
+ var devDependencies = {
22
+ "@babel/core": "^7.17.0",
23
+ "@rollup/plugin-babel": "^5.3.0",
24
+ "@rollup/plugin-commonjs": "^21.0.1",
25
+ "@rollup/plugin-json": "^4.1.0",
26
+ "@rollup/plugin-node-resolve": "^13.1.3",
27
+ "@vidispine/eslint-config-base": "^22.1.0-pre.1",
28
+ "@vidispine/eslint-config-storybook": "*",
29
+ "babel-plugin-transform-runtime": "^6.23.0",
30
+ rollup: "^2.67.0",
31
+ "rollup-plugin-cleanup": "^3.2.1",
32
+ "rollup-plugin-peer-deps-external": "^2.2.4",
33
+ "rollup-plugin-sass": "^1.2.10"
34
+ };
35
+ var browserslist = [
36
+ "since 2017-06"
37
+ ];
38
+ var eslintConfig = {
39
+ "extends": [
40
+ "@vidispine/eslint-config-base",
41
+ "@vidispine/eslint-config-storybook"
42
+ ]
43
+ };
44
+ var prettier = "@vidispine/prettier-config";
45
+ var pkgInfo = {
46
+ name: name,
47
+ version: version,
48
+ license: license,
49
+ "private": false,
50
+ main: main,
51
+ module: module,
52
+ browser: browser,
53
+ author: author,
54
+ scripts: scripts,
55
+ dependencies: dependencies,
56
+ devDependencies: devDependencies,
57
+ browserslist: browserslist,
58
+ eslintConfig: eslintConfig,
59
+ prettier: prettier
60
+ };
61
+
62
+ const defaults = Object.freeze({
63
+ frameRate: 30000 / 1001,
64
+ jumpSeconds: 10,
65
+ inactivityTimeout: 1200,
66
+ audioInactivityTimeout: 0
67
+ });
68
+ const osd = Object.freeze({
69
+ play: 0,
70
+ pause: 1,
71
+ volchange: 2,
72
+ jumpForward: 3,
73
+ jumpBackward: 4
74
+ });
75
+ const settings = Object.freeze({
76
+ volume: 'volume-setting',
77
+ subLang: 'subtitles-language',
78
+ subs: 'subtitles-enabled'
79
+ });
80
+ const classNames = Object.freeze({
81
+ poster: 'has-poster',
82
+ subtitles: 'has-subtitles',
83
+ subtitlesActive: 'subtitles-active',
84
+ onDark: 'on-dark',
85
+ loading: 'loading'
86
+ });
87
+ const colors = Object.freeze({
88
+ gray: '#787878',
89
+ light: 'rgba(255,255,255,.75)'
90
+ });
91
+ const packageInfo = Object.freeze({
92
+ name: pkgInfo.name.split('/')[1],
93
+ vendor: pkgInfo.name.split('/')[0]
94
+ });
95
+ const constants$2 = Object.freeze({
96
+ defaults,
97
+ settings,
98
+ classNames,
99
+ colors,
100
+ osd,
101
+ packageInfo
102
+ });
103
+
104
+ function truncate(input, len) {
105
+ if (input.length > len) return "".concat(input.substring(0, len), "...");
106
+ return input;
107
+ }
108
+ function reactiveSetters() {
109
+ function handleNewProps(props) {
110
+ if (typeof props === 'object') {
111
+ const keys = Object.getOwnPropertyNames(props);
112
+ const proto = Object.getPrototypeOf(this);
113
+ keys.forEach(key => {
114
+ const descriptor = Object.getOwnPropertyDescriptor(proto, key);
115
+ if (descriptor && descriptor.set) {
116
+ this[key] = props[key];
117
+ }
118
+ });
119
+ }
120
+ }
121
+ this.handleNewProps = handleNewProps;
122
+ }
123
+ function sanitize(str) {
124
+ const temp = document.createElement('div');
125
+ temp.textContent = str;
126
+ return temp.innerHTML;
127
+ }
128
+
129
+ const buttonNames = Object.freeze({
130
+ play: 'play-pause',
131
+ start: 'start',
132
+ fb: 'frame-backward',
133
+ ff: 'frame-forward',
134
+ end: 'end'
135
+ });
136
+ const buttons = [
137
+ buttonNames.start, buttonNames.fb, buttonNames.play, buttonNames.ff, buttonNames.end];
138
+ const CSS$2 = "\n .vdt-external-controls{\n display: flex;\n justify-content: center;\n align-items: center;\n }\n\n .control{\n flex: 1;\n flex-grow: 1;\n height: 100%;\n align-contents: center;\n align-self: center;\n text-align: center;\n vertical-align: middle;\n cursor: pointer;\n user-select: none;\n height: 20px;\n padding-top: 10px;\n padding-bottom: 10px;\n }\n\n .control .icon{\n display: inline-block;\n height: 20px;\n white-space: nowrap;\n opacity: .9;\n }\n\n .control:hover .icon{ opacity: 1; }\n\n .control .icon:before{\n opacity: 1;\n flex: 1;\n align-self: center;\n content: \" \";\n display: inline-block;\n margin: 0 auto;\n\n /* triangles */\n width: 0;\n height: 0;\n border-style: solid;\n border-width: 16px 0 16px 23px;\n border-color: transparent transparent transparent ".concat(colors.gray, ";\n }\n\n .vdt-external-controls.").concat(classNames.onDark, " .control .icon:before{\n border-color: transparent transparent transparent ").concat(colors.light, ";\n }\n\n /* specific buttons */\n\n .control.").concat(buttonNames.start, ",\n .control.").concat(buttonNames.end, "{\n height: 20px;\n }\n\n .control.").concat(buttonNames.play, "{\n height: 30px;\n padding-top: 0px;\n padding-bottom: 0px;\n }\n\n .control.").concat(buttonNames.play, " .icon{\n height: 30px;\n }\n\n .control.").concat(buttonNames.ff, " .icon:before,\n .control.").concat(buttonNames.fb, " .icon:before,\n .control.").concat(buttonNames.end, " .icon:before,\n .control.").concat(buttonNames.start, " .icon:before{\n border-width: 10px 0 10px 16px;\n }\n\n .control.").concat(buttonNames.fb, " .icon,\n .control.").concat(buttonNames.start, "{\n transform: rotate(180deg);\n }\n\n .control.").concat(buttonNames.end, " .icon:after,\n .control.").concat(buttonNames.start, " .icon:after{\n content: \" \";\n width: 4px;\n height: 20px;\n display: inline-block;\n background: ").concat(colors.gray, ";\n }\n\n .vdt-external-controls.").concat(classNames.onDark, " .control.").concat(buttonNames.end, " .icon:after,\n .vdt-external-controls.").concat(classNames.onDark, " .control.").concat(buttonNames.start, " .icon:after{\n background: ").concat(colors.light, ";\n }\n\n /* stateful styles */\n .control.").concat(buttonNames.play, ".playing .icon:before{\n height: 30px;\n width: 6px;\n border-width: 0px 8px 0px 8px;\n border-color: transparent ").concat(colors.gray, " transparent ").concat(colors.gray, ";\n }\n\n .vdt-external-controls.").concat(classNames.onDark, " .control.").concat(buttonNames.play, ".playing .icon:before{\n border-color: transparent ").concat(colors.light, " transparent ").concat(colors.light, ";\n }\n\n .vdt-external-controls.").concat(classNames.loading, " .control.").concat(buttonNames.play, "{\n border-color: transparent #f00 transparent #f00;\n }\n");
139
+ class ExternalControls {
140
+ constructor(props) {
141
+ this.props = props;
142
+ reactiveSetters.apply(this);
143
+ this.state = {
144
+ playing: false,
145
+ onDark: false,
146
+ loading: false
147
+ };
148
+ this.shadowRoot = document.createElement('div');
149
+ this.shadowRoot.className = 'vdt-external-controls-wrapper';
150
+ this.container = document.createElement('div');
151
+ this.container.className = 'vdt-external-controls';
152
+ if (this.props.target) {
153
+ this.props.target.appendChild(this.shadowRoot);
154
+ this.shadow = this.shadowRoot.attachShadow({
155
+ mode: 'open'
156
+ });
157
+ this.style = document.createElement('style');
158
+ this.style.type = 'text/css';
159
+ this.style.appendChild(document.createTextNode(CSS$2));
160
+ this.shadow.appendChild(this.style);
161
+ this.shadow.appendChild(this.container);
162
+ }
163
+ this.handleNewProps(props);
164
+ this.bindEvents();
165
+ this.draw();
166
+ }
167
+ draw() {
168
+ this.buttons = {};
169
+ const excludes = this.props.excludes || [];
170
+ buttons.forEach(buttonName => {
171
+ if (excludes.indexOf(buttonName) < 0) {
172
+ const btn = document.createElement('div');
173
+ btn.className = "control ".concat(buttonName);
174
+ btn.innerHTML = '<span class="icon"></span>';
175
+ btn.onclick = () => {
176
+ this.handlePress(buttonName);
177
+ };
178
+ this.buttons[buttonName] = btn;
179
+ this.container.appendChild(btn);
180
+ }
181
+ });
182
+ }
183
+ bindEvents() {
184
+ if (this.props.player && this.props.player.videojs) {
185
+ const {
186
+ player
187
+ } = this.props;
188
+ player.videojs.on('play', () => {
189
+ this.playing = true;
190
+ });
191
+ player.videojs.on('pause', () => {
192
+ this.playing = false;
193
+ });
194
+ player.videojs.on('stop', () => {
195
+ this.playing = false;
196
+ });
197
+ }
198
+ }
199
+ handlePress(buttonName) {
200
+ if (this.props.player) {
201
+ const {
202
+ player
203
+ } = this.props;
204
+ if (player.osd) player.osd.disable();
205
+ switch (buttonName) {
206
+ case buttonNames.start:
207
+ player.toStart();
208
+ break;
209
+ case buttonNames.fb:
210
+ player.triggerHotkey('left');
211
+ break;
212
+ case buttonNames.play:
213
+ player.triggerHotkey('k');
214
+ break;
215
+ case buttonNames.ff:
216
+ player.triggerHotkey('right');
217
+ break;
218
+ case buttonNames.end:
219
+ player.toEnd();
220
+ break;
221
+ }
222
+ if (player.osd) player.osd.enable();
223
+ } else {
224
+ switch (buttonName) {
225
+ case buttonNames.start:
226
+ if (this.props.onPrev) this.props.onPrev();
227
+ break;
228
+ case buttonNames.fb:
229
+ if (this.props.onFB) this.props.onFB();
230
+ break;
231
+ case buttonNames.play:
232
+ if (this.playing) {
233
+ if (this.props.onPause) this.props.onPause();
234
+ } else if (this.props.onPlay) this.props.onPlay();
235
+ break;
236
+ case buttonNames.ff:
237
+ if (this.props.onFF) this.props.onFF();
238
+ break;
239
+ case buttonNames.end:
240
+ if (this.props.onNext) this.props.onNext();
241
+ break;
242
+ }
243
+ }
244
+ }
245
+ set playing(bool) {
246
+ if (bool) {
247
+ this.state.playing = true;
248
+ this.buttons[buttonNames.play].classList.add('playing');
249
+ } else {
250
+ this.state.playing = false;
251
+ this.buttons[buttonNames.play].classList.remove('playing');
252
+ }
253
+ }
254
+ get playing() {
255
+ return this.state.playing;
256
+ }
257
+ set onDark(bool) {
258
+ if (bool) {
259
+ this.state.onDark = true;
260
+ this.container.classList.add(classNames.onDark);
261
+ } else {
262
+ this.state.onDark = false;
263
+ this.container.classList.remove(classNames.onDark);
264
+ }
265
+ }
266
+ get onDark() {
267
+ return this.container.classList.contains(classNames.onDark);
268
+ }
269
+ set loading(bool) {
270
+ if (bool) {
271
+ this.state.loading = true;
272
+ this.container.classList.add(classNames.loading);
273
+ } else {
274
+ this.state.loading = false;
275
+ this.container.classList.remove(classNames.loading);
276
+ }
277
+ }
278
+ get loading() {
279
+ return this.state.loading;
280
+ }
281
+ }
282
+ ExternalControls.buttonNames = buttonNames;
283
+
284
+ function _defineProperty(obj, key, value) {
285
+ if (key in obj) {
286
+ Object.defineProperty(obj, key, {
287
+ value: value,
288
+ enumerable: true,
289
+ configurable: true,
290
+ writable: true
291
+ });
292
+ } else {
293
+ obj[key] = value;
294
+ }
295
+ return obj;
296
+ }
297
+
298
+ class HelpOverlay {
299
+ constructor(props) {
300
+ this.props = props;
301
+ this.state = {
302
+ active: this.props.active || false
303
+ };
304
+ this.container = document.createElement('div');
305
+ this.container.className = 'vdt-help-overlay';
306
+ this.container.innerHTML = '';
307
+ this.container.onclick = () => {
308
+ this.hide();
309
+ };
310
+ if (this.props.target) this.props.target.appendChild(this.container);
311
+ this.draw();
312
+ }
313
+ draw() {
314
+ let tableRowMarkup = '';
315
+ const rows = [['⎵', 'Play / Pause'], ['⇧', 'Increase volume '], ['⇩', 'Decrease volume'], ['⇨', 'Step frame forward'], ['⇦', 'Step frame backward'], ['M', 'Mute / Unmute'], ['J', 'Seek 10 seconds backward'], ['K', 'Play / Pause'], ['L', 'Seek 10 seconds forward'], ['C', 'Toggle subtitles'], ['N', 'Toggle settings menu'], ['H', 'Toggle this help overlay']];
316
+ const halfpointIndex = Math.round(rows.length / 2);
317
+ const columns = [rows.slice(0, halfpointIndex), rows.slice(halfpointIndex, rows.length)];
318
+ for (let i = 0; i < halfpointIndex; i += 1) {
319
+ tableRowMarkup += "\n <tr>\n <td><div>".concat(columns[0][i][0], "</div></td>\n <td>- ").concat(columns[0][i][1], "</td>\n\n <td>").concat(columns[1][i] ? "<div>".concat(columns[1][i][0], "</div>") : '', "</td>\n <td>").concat(columns[1][i] ? "- ".concat(columns[1][i][1]) : '', "</td>\n </tr>\n ");
320
+ }
321
+ this.container.innerHTML = "\n <table>\n <tbody>\n <tr>\n <td class=\"vdt-help-contents\">\n <table><tbody>".concat(tableRowMarkup, "</tbody></table>\n </td>\n </tr>\n </tbody>\n </table>\n ");
322
+ }
323
+ show() {
324
+ if (!this.state.active) {
325
+ this.container.classList.add('active');
326
+ this.state.active = true;
327
+ mousetrap.bind('esc', () => {
328
+ this.hide();
329
+ });
330
+ if (this.props.onToggle) this.props.onToggle(true);
331
+ }
332
+ }
333
+ hide() {
334
+ if (this.state.active) {
335
+ this.container.classList.remove('active');
336
+ this.state.active = false;
337
+ mousetrap.unbind('esc');
338
+ if (this.props.onToggle) this.props.onToggle(false);
339
+ }
340
+ }
341
+ toggle() {
342
+ if (this.state.active) {
343
+ this.hide();
344
+ } else {
345
+ this.show();
346
+ }
347
+ }
348
+ }
349
+
350
+ function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
351
+ function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$1(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
352
+ const constants$1 = Object.freeze({
353
+ className: 'vdt-menu',
354
+ menuClassName: 'menu',
355
+ subLvlClassName: 'sub-level',
356
+ menuItemClassName: 'menu-item',
357
+ animationLength: 300,
358
+ defaults: {
359
+ bgColor: 'rgba(0,0,0,.8)',
360
+ textColor: '#999',
361
+ textColorDark: '#333',
362
+ textColorLight: 'rgba(255,255,255)',
363
+ bgColorLight: 'rgba(255,255,255,.2)',
364
+ bgColorSelected: 'rgba(255,255,255,.1)'
365
+ }
366
+ });
367
+ const CSS$1 = () => "\n .".concat(constants$1.menuClassName, "{\n position: relative;\n height: 100%;\n width: 200%;\n // min-width: 200px;\n right: 0px;\n bottom: 0px;\n background: ").concat(constants$1.defaults.bgColor, ";\n z-index: 1px;\n transform: translate3d(100%, 0px, 0px);\n transition: all 300ms ease-in-out;\n box-shadow: 0px 0px 30px rgb;\n -webkit-backdrop-filter: blur(10px);\n overflow: hidden;\n }\n\n .").concat(constants$1.menuClassName, " .level-1{\n float: left;\n }\n\n .").concat(constants$1.menuClassName, " div h3{\n padding: 16px 24px;\n font-size: 12px;\n border-bottom: 1px solid ").concat(constants$1.defaults.bgColorLight, "\n }\n\n .").concat(constants$1.menuClassName, " .level-2 h3{\n cursor: pointer;\n position: relative;\n }\n .").concat(constants$1.menuClassName, " .level-2 h3:before{\n content: '\u25C2';\n margin-left: -12px;\n width: 12px;\n display: inline-block;\n position: absolute;\n margin-top: -1px;\n }\n\n .").concat(constants$1.menuClassName, " .level-1,\n .").concat(constants$1.menuClassName, " .level-2{\n transform: translate3d(0px, 0px, 0px);\n transition: all 300ms ease-in-out;\n width: 50%;\n height: 100%;\n float: left;\n }\n\n .").concat(constants$1.menuClassName, ".sub-level .level-1,\n .").concat(constants$1.menuClassName, ".sub-level .level-2{\n transform: translate3d(-100%, 0px, 0px);\n }\n\n .").concat(constants$1.menuClassName, " table{\n border-collapse: collapse;\n width: 100%;\n }\n\n .").concat(constants$1.menuClassName, ".open{\n transform: translate3d(0px, 0px, 0px);\n }\n\n .").concat(constants$1.menuClassName, " tr{\n cursor: pointer;\n height: 40px;\n }\n\n .").concat(constants$1.menuClassName, " tr.selected{\n background: ").concat(constants$1.defaults.bgColorSelected, ";\n }\n\n\n .").concat(constants$1.menuClassName, " tr td{\n color: white;\n font-size: 15px;\n vertical-align: middle;\n }\n\n \n .").concat(constants$1.menuClassName, " tr:hover td{\n color: white;\n background: ").concat(constants$1.defaults.bgColorLight, ";\n }\n\n .").concat(constants$1.menuClassName, " tr td.title{\n padding-top: 12px;\n padding-bottom: 12px;\n font-weight: 300;\n font-family: Helvetica, Arial, sans-serif; \n }\n\n .").concat(constants$1.menuClassName, " tr td.caret{\n font-size: 12px;\n }\n\n .").concat(constants$1.menuClassName, " tr td:first-child{\n padding-left: 24px;\n }\n\n .").concat(constants$1.menuClassName, " tr td:last-child{\n padding-right: 16px;\n }\n\n .").concat(constants$1.menuClassName, " tr.has-items td:last-child{\n text-align: right;\n }\n\n .").concat(constants$1.menuClassName, " tr.has-items td.subtitle{\n font-size: 12px;\n text-align: right;\n color: ").concat(constants$1.defaults.textColorLight, ";\n }\n");
368
+ class MenuDisplayItem {
369
+ constructor(props) {
370
+ this.props = props;
371
+ const classNames = [constants$1.menuItemClassName, this.props.items && 'has-items', this.props.selected && 'selected'];
372
+ this.container = document.createElement('tr');
373
+ this.container.className = classNames.join(' ');
374
+ if (this.props.target) this.props.target.appendChild(this.container);
375
+ this.container.onclick = event => {
376
+ if (this.props.onClick) this.props.onClick(event);
377
+ };
378
+ this.render();
379
+ }
380
+ render() {
381
+ if (this.props.items) {
382
+ this.container.innerHTML = "\n <td class=\"title\"></td>\n <td class=\"subtitle\"></td>\n <td class=\"caret\">\u25B7</td>\n ";
383
+ } else {
384
+ this.container.innerHTML = "\n <td class=\"title\"></td>\n ";
385
+ }
386
+ const titleNode = this.container.getElementsByClassName('title')[0];
387
+ const subtitleNode = this.props.items ? this.container.getElementsByClassName('subtitle')[0] : false;
388
+ titleNode.textContent = this.props.title;
389
+ if (subtitleNode) subtitleNode.textContent = this.props.subtitle;
390
+ }
391
+ }
392
+ class Menu {
393
+ constructor(props) {
394
+ this.props = props;
395
+ this.container = document.createElement('div');
396
+ this.container.className = "".concat(constants$1.className);
397
+ this.state = {
398
+ opened: false,
399
+ selectedItem: 0,
400
+ items: [],
401
+ subItems: []
402
+ };
403
+ this.mount();
404
+ this.draw();
405
+ }
406
+ mount() {
407
+ if (this.props.target) {
408
+ this.container.style.position = 'absolute';
409
+ this.container.style.bottom = this.container.style.right = '0px';
410
+ this.container.style.height = '100%';
411
+ this.container.style.overflow = 'hidden';
412
+ this.container.style.width = '30%';
413
+ this.container.style.minWidth = '250px';
414
+ this.container.style.maxWidth = '320px';
415
+ this.shadow = this.container.attachShadow({
416
+ mode: 'open'
417
+ });
418
+ if (this.props.target) this.props.target.appendChild(this.container);
419
+ this.menuContainer = document.createElement('div');
420
+ this.menuContainer.className = 'menu';
421
+ this.shadow.appendChild(this.menuContainer);
422
+ this.style = document.createElement('style');
423
+ this.style.type = 'text/css';
424
+ this.style.innerHTML = CSS$1();
425
+ this.shadow.appendChild(this.style);
426
+ }
427
+ }
428
+ draw() {
429
+ const mainDiv = document.createElement('div');
430
+ mainDiv.className = 'level-1';
431
+ this.mainLevel = document.createElement('table');
432
+ mainDiv.innerHTML = '<h3>Settings</h3>';
433
+ mainDiv.appendChild(this.mainLevel);
434
+ const subDiv = document.createElement('div');
435
+ subDiv.className = 'level-2';
436
+ this.subLvlTitle = document.createElement('h3');
437
+ this.subLvlTitle.onclick = () => {
438
+ this.up();
439
+ };
440
+ subDiv.appendChild(this.subLvlTitle);
441
+ this.secondaryLevel = document.createElement('table');
442
+ subDiv.appendChild(this.secondaryLevel);
443
+ this.menuContainer.appendChild(mainDiv);
444
+ this.menuContainer.appendChild(subDiv);
445
+ this.props.items.forEach(menuItem => {
446
+ const activeTitle = menuItem.items[menuItem.getActiveIndex(menuItem.items)].title || '';
447
+ const displayItem = new MenuDisplayItem(_objectSpread$1({
448
+ target: this.mainLevel,
449
+ subtitle: activeTitle,
450
+ onClick: () => {
451
+ this.handleSupItemSelected(menuItem);
452
+ }
453
+ }, menuItem));
454
+ this.state.items.push(displayItem);
455
+ });
456
+ }
457
+ open() {
458
+ this.render();
459
+ this.menuContainer.classList.add('open');
460
+ if (this.props.onOpen) this.props.onOpen();
461
+ this.state.opened = true;
462
+ if (!this.handleClicksWhenMenuOpened) {
463
+ this.handleClicksWhenMenuOpened = function (event) {
464
+ if (this.state.opened) {
465
+ event.stopPropagation();
466
+ const isClickInside = this.container.contains(event.target);
467
+ if (!isClickInside) {
468
+ this.close();
469
+ }
470
+ }
471
+ }.bind(this);
472
+ }
473
+ this.handleClicksWhenMenuOpenedTimeout = setTimeout(() => {
474
+ document.addEventListener('click', this.handleClicksWhenMenuOpened);
475
+ mousetrap.bind('esc', this.close.bind(this));
476
+ }, constants$1.animationLength);
477
+ }
478
+ close() {
479
+ this.menuContainer.classList.remove('open');
480
+ this.state.opened = false;
481
+ if (this.handleClicksWhenMenuOpenedTimeout) clearTimeout(this.handleClicksWhenMenuOpenedTimeout);
482
+ document.removeEventListener('click', this.handleClicksWhenMenuOpened);
483
+ if (this.props.onClose) this.props.onClose();
484
+ mousetrap.unbind('esc');
485
+ }
486
+ toggle() {
487
+ if (this.state.opened) {
488
+ this.close();
489
+ } else {
490
+ this.open();
491
+ }
492
+ }
493
+ up() {
494
+ this.menuContainer.classList.remove(constants$1.subLvlClassName);
495
+ this.render();
496
+ }
497
+ handleSupItemSelected(item) {
498
+ this.subLvlTitle.textContent = item.title;
499
+ this.renderSubItems(item.items, item.getActiveIndex(item.items));
500
+ this.menuContainer.classList.add(constants$1.subLvlClassName);
501
+ }
502
+ renderSubItems(items) {
503
+ let activeIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
504
+ this.secondaryLevel.innerHTML = '';
505
+ const subItemRefs = [];
506
+ items.forEach((menuItem, index) => {
507
+ const displayItem = new MenuDisplayItem(_objectSpread$1(_objectSpread$1({
508
+ target: this.secondaryLevel
509
+ }, menuItem), {}, {
510
+ onClick: event => {
511
+ this.handleSubItemSelected(event, menuItem);
512
+ },
513
+ selected: index === activeIndex
514
+ }));
515
+ subItemRefs.push(displayItem);
516
+ });
517
+ this.state.subItems = subItemRefs;
518
+ }
519
+ handleSubItemSelected(event, item) {
520
+ this.close();
521
+ if (item.onClick) item.onClick(event, item);
522
+ this.up();
523
+ }
524
+ get opened() {
525
+ return this.state.opened;
526
+ }
527
+ set opened(bool) {
528
+ this.state.opened = bool;
529
+ }
530
+ render() {
531
+ this.state.items.forEach((menuDisplayItem, index) => {
532
+ const {
533
+ items
534
+ } = this.props;
535
+ const activeIndex = items[index].getActiveIndex(items[index].items);
536
+ const activeTitle = items[index].items[activeIndex].title;
537
+ menuDisplayItem.props.subtitle = activeTitle;
538
+ menuDisplayItem.render();
539
+ });
540
+ }
541
+ }
542
+
543
+ class OSD {
544
+ constructor(props) {
545
+ this.props = props;
546
+ this.state = {
547
+ enabled: true
548
+ };
549
+ this.container = document.createElement('div');
550
+ this.container.className = 'vdt-player-osd';
551
+ this.container.innerHTML = "\n <table>\n <tbody>\n <td>\n <div class=\"osd-feedback-icon\"></div>\n </td>\n </tbody>\n </table>\n ";
552
+ this.iconEl = this.container.getElementsByClassName('osd-feedback-icon')[0];
553
+ if (this.props.target) this.props.target.appendChild(this.container);
554
+ }
555
+ feedback(feedbackType) {
556
+ let details = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
557
+ if (!this.state.enabled) {
558
+ return;
559
+ }
560
+ let className;
561
+ switch (feedbackType) {
562
+ case osd.pause:
563
+ className = 'pause';
564
+ break;
565
+ case osd.play:
566
+ className = 'play';
567
+ break;
568
+ case osd.volchange:
569
+ const currentVolumeLevel = details.volume;
570
+ let perceivedVolumeLevel;
571
+ if (currentVolumeLevel === 0) {
572
+ perceivedVolumeLevel = 'null';
573
+ } else if (currentVolumeLevel > 0.65) {
574
+ perceivedVolumeLevel = 'high';
575
+ } else if (currentVolumeLevel < 0.35) {
576
+ perceivedVolumeLevel = 'low';
577
+ } else {
578
+ perceivedVolumeLevel = 'mid';
579
+ }
580
+ className = "volchange ".concat(perceivedVolumeLevel);
581
+ break;
582
+ case osd.jumpForward:
583
+ className = 'forward-jump';
584
+ break;
585
+ case osd.jumpBackward:
586
+ className = 'backward-jump';
587
+ break;
588
+ }
589
+ if (this.osdTimeout) {
590
+ this.container.classList.remove('active');
591
+ clearTimeout(this.osdTimeout);
592
+ }
593
+ this.container.classList.add('active');
594
+ this.osdTimeout = setTimeout(() => {
595
+ this.container.classList.remove('active');
596
+ }, 200);
597
+ this.iconEl.className = "osd-feedback-icon ".concat(className);
598
+ }
599
+ disable() {
600
+ this.state.enabled = false;
601
+ }
602
+ enable() {
603
+ this.state.enabled = true;
604
+ }
605
+ }
606
+
607
+ const constants = Object.freeze({
608
+ className: 'vdt-seekbar',
609
+ defaults: {
610
+ bgColor: '#fafafa',
611
+ fillColor: '#f00',
612
+ regionColor: '#f6e58d',
613
+ markerWidth: 16,
614
+ regionAccentColor: '#00a8ff',
615
+ transitionTime: 300
616
+ },
617
+ markerTypes: {
618
+ in: 'in',
619
+ out: 'out'
620
+ }
621
+ });
622
+ const CSS = (bgColor, fillColor) => "\n .".concat(constants.className, "{\n user-select: none;\n background: ").concat(colors.light, ";\n height: 6px;\n width: 100%;\n position: relative;\n cursor: pointer;\n z-index: 5;\n }\n\n .").concat(classNames.onDark, ".").concat(constants.className, "{\n background: ").concat(colors.gray, ";\n }\n\n .fill, .load-indicator{\n height: 100%;\n display: inline-block;\n position: absolute;\n left: 0px;\n top: 0px;\n background: ").concat(fillColor, ";\n transition: width ").concat(constants.defaults.transitionTime, "ms linear;\n z-index: 2;\n }\n\n .load-indicator{\n background: #e1e1e1;\n z-index: 1;\n opacity: .2;\n transition: none;\n }\n\n .").concat(classNames.onDark, ".").concat(constants.className, " .load-indicator{\n background: white;\n }\n\n .").concat(constants.className, ".dragging .fill{\n transition: none;\n }\n\n\n .marker, .region{\n position: absolute;\n top: 0px;\n height: 100%;\n background: cyan;\n width: 1px;\n display: inline-block;\n z-index: 4;\n }\n\n .marker{\n transition: opacity ").concat(constants.defaults.transitionTime, "ms linear;\n }\n\n .marker{ display: none; }\n .marker.active{ display: initial; }\n\n .marker .handle{\n position: absolute;\n bottom: 10px; right: 0px;\n width: ").concat(constants.defaults.markerWidth, "px;\n height: 28px;\n background: #999;\n border-radius: 3px;\n cursor: col-resize;\n z-index: 3;\n box-sizing: border-box;\n }\n\n .marker.regional .handle{\n background: ").concat(constants.defaults.regionAccentColor, ";\n box-shadow: -2px 1px 1px rgba(0,0,0,.1);\n }\n\n .marker.regional.in .handle{\n box-shadow: 2px 1px 1px rgba(0,0,0,.1);\n }\n\n .marker.in .handle{\n border-bottom-left-radius: 40px;\n }\n\n .marker.out .handle{\n left: 1px;\n right: initial;\n border-bottom-right-radius: 20px;\n }\n\n .handle:active{\n background: ").concat(constants.defaults.regionColor, ";\n }\n\n .region{\n position: absolute;\n width: auto;\n background: ").concat(constants.defaults.regionColor, ";\n opacity: .7;\n }\n\n .region.nav{\n background: transparent;\n opacity: 1;\n border-left: 1px solid cyan;\n }\n\n .region.nav .bubble,\n .region.nav:after{\n content: \" \";\n height: 16px;\n bottom: 10px;\n width: 16px;\n border-radius: 16px;\n background: ").concat(constants.defaults.regionAccentColor, ";\n position: absolute;\n margin-left: -8px;\n transition: transform 200ms ease-out;\n transform: translate3d(0px, 0px, 0px);\n transform-origin: bottom center;\n }\n\n .region.nav .popout{\n width: 160px;\n z-index: 4;\n background: rgba(28, 28, 28, .7);\n color: white;\n font-size: 14px;\n min-height: 100px;\n bottom: 16px;\n left: -80px;\n position: absolute;\n border-radius: 8px;\n -webkit-backdrop-filter: blur(10px);\n font-family: Arial, Helvetica, sans-serif;\n overflow: hidden;\n opacity: 0;\n visibility: hidden;\n }\n\n .region.nav.right-aligned .popout{\n right: 0px;\n left: initial;\n }\n\n .region.nav.left-aligned .popout{\n left: -32px;\n } \n\n .region.nav .popout .title{\n padding: 4px 14px;\n text-align: center;\n color: black;\n background: white;\n }\n\n .region.nav .popout .annotation{\n padding: 8px 12px;\n font-weight: light;\n color: rgba(255,255,255,.9);\n }\n\n .region.nav:after{\n width: 1px;\n left: 8px;\n height: 10px;\n bottom: 0px;\n background: #00a8ff;\n }\n\n .region.nav:hover{\n background: rgba(28, 28, 28, .5);\n }\n\n .region.nav.has-popout:hover .bubble{\n background: rgba(28, 28, 28, .8);\n opacity: 1;\n z-index: 3;\n }\n\n .region.nav:hover:after,\n .region.nav:hover:before{\n background: rgba(28, 28, 28, .8);\n }\n\n .region.nav:hover .popout{\n opacity: 1;\n visibility: visible;\n }\n\n .").concat(constants.className, ".dragging .bubble{\n transform: scale3d(.75,.75,.75);\n }\n\n .").concat(constants.className, ".hide-markers .marker{\n opacity: 0;\n }\n\n");
623
+ class Region {
624
+ constructor(props) {
625
+ const regionDefaultProps = {
626
+ in: null,
627
+ out: null,
628
+ title: '',
629
+ annotation: '',
630
+ el: null
631
+ };
632
+ Object.assign(this, regionDefaultProps, props);
633
+ }
634
+ }
635
+ class SeekBar {
636
+ constructor(props) {
637
+ this.props = props;
638
+ reactiveSetters.apply(this);
639
+ this.container = document.createElement('div');
640
+ this.container.className = "".concat(constants.className, "-wrapper");
641
+ this.state = {
642
+ in: null,
643
+ out: null,
644
+ markersEnabled: this.props.markersEnabled === undefined ? true : this.props.markersEnabled,
645
+ regions: [],
646
+ dragging: false
647
+ };
648
+ this.markers = {};
649
+ if (this.props.target) {
650
+ this.shadow = this.container.attachShadow({
651
+ mode: 'open'
652
+ });
653
+ this.props.target.appendChild(this.container);
654
+ this.style = document.createElement('style');
655
+ this.style.type = 'text/css';
656
+ const bgColor = this.props.bgColor ? this.props.bgColor : constants.defaults.bgColor;
657
+ const fillColor = this.props.fillColor ? this.props.fillColor : constants.defaults.fillColor;
658
+ this.style.innerHTML = CSS(sanitize(bgColor), sanitize(fillColor));
659
+ this.shadow.appendChild(this.style);
660
+ }
661
+ this.bindEvents();
662
+ this.draw();
663
+ this.bindHotkeys();
664
+ const {
665
+ player
666
+ } = this.props;
667
+ if (player) {
668
+ player.videojs.on('loadedmetadata', () => {
669
+ this.handleNewProps(this.props);
670
+ });
671
+ }
672
+ }
673
+ bindEvents() {
674
+ if (this.props.player) {
675
+ const {
676
+ player
677
+ } = this.props;
678
+ player.videojs.on('timeupdate', () => {
679
+ const progressFraction = player.videojs.currentTime() / player.videojs.duration();
680
+ this.fill.style.width = "".concat(100 * progressFraction, "%");
681
+ });
682
+ player.videojs.on('progress', () => {
683
+ this.percentLoaded = player.percentLoaded;
684
+ });
685
+ }
686
+ }
687
+ bindHotkeys() {
688
+ this.bindings = [];
689
+ if (this.props.player) {
690
+ const {
691
+ player
692
+ } = this.props;
693
+ this.bindings = [{
694
+ key: ['i'],
695
+ method: () => {
696
+ if (this.markersEnabled) this.in = player.videojs.currentTime();
697
+ }
698
+ }, {
699
+ key: ['o'],
700
+ method: () => {
701
+ if (this.markersEnabled) this.out = player.videojs.currentTime();
702
+ }
703
+ }, {
704
+ key: ['shift+i'],
705
+ method: () => {
706
+ if (this.markersEnabled) this.navigateToIn();
707
+ }
708
+ }, {
709
+ key: ['shift+o'],
710
+ method: () => {
711
+ if (this.markersEnabled) this.navigateToOut();
712
+ }
713
+ }];
714
+ }
715
+ if (this.props.player) {
716
+ const {
717
+ player
718
+ } = this.props;
719
+ mousetrap.bind('i', () => {
720
+ if (this.markersEnabled) this.in = player.videojs.currentTime();
721
+ });
722
+ mousetrap.bind('o', () => {
723
+ if (this.markersEnabled) this.out = player.videojs.currentTime();
724
+ });
725
+ mousetrap.bind('shift+i', () => {
726
+ if (this.markersEnabled) this.navigateToIn();
727
+ });
728
+ mousetrap.bind('shift+o', () => {
729
+ if (this.markersEnabled) this.navigateToOut();
730
+ });
731
+ }
732
+ this.bindings.forEach(binding => {
733
+ mousetrap.bind(binding.key, e => {
734
+ binding.method(e);
735
+ });
736
+ });
737
+ }
738
+ unbindHotKeys() {
739
+ this.bindings && this.bindings.forEach(binding => {
740
+ mousetrap.unbind(binding.key);
741
+ });
742
+ }
743
+ handleSeek(event) {
744
+ if (this.props.player) {
745
+ const {
746
+ player
747
+ } = this.props;
748
+ if (this.state.regions) {
749
+ let clickedOnRegion = false;
750
+ let preventSeek = false;
751
+ this.state.regions.forEach(region => {
752
+ if (region.el.contains(event.target)) {
753
+ if (this.props.onRegionClick) {
754
+ preventSeek = this.props.onRegionClick(region);
755
+ if (!preventSeek) {
756
+ player.seekTo(region.in);
757
+ }
758
+ } else {
759
+ player.seekTo(region.in);
760
+ }
761
+ clickedOnRegion = true;
762
+ }
763
+ });
764
+ if (clickedOnRegion) return;
765
+ }
766
+ const seekFractionX = (event.clientX - this.bar.getBoundingClientRect().left) / this.bar.offsetWidth;
767
+ const timeToSeek = seekFractionX * player.videojs.duration();
768
+ player.seekTo(timeToSeek);
769
+ }
770
+ }
771
+ handleSeekDrag(mouseDownEvent) {
772
+ const barOffset = this.bar.getBoundingClientRect().left;
773
+ const barWidth = this.bar.getBoundingClientRect().width;
774
+ const duration = this.props.player.videojs.duration();
775
+ let left;
776
+ const drag = e => {
777
+ left = e.clientX - barOffset;
778
+ const pxDragged = Math.abs(mouseDownEvent.clientX - e.clientX);
779
+ if (left >= 0 && left < barWidth && pxDragged > 1) {
780
+ this.dragging = true;
781
+ const fractionFromStart = parseFloat(left) / barWidth;
782
+ const seconds = fractionFromStart * duration;
783
+ this.props.player.videojs.currentTime(seconds);
784
+ }
785
+ e.preventDefault();
786
+ };
787
+ const stopDrag = e => {
788
+ this.dragging = false;
789
+ document.removeEventListener('mousemove', drag, false);
790
+ document.removeEventListener('mouseup', stopDrag, false);
791
+ const fractionFromStart = parseFloat(left) / barWidth;
792
+ const seconds = fractionFromStart * duration;
793
+ this.props.player.videojs.currentTime(seconds);
794
+ e.stopPropagation();
795
+ };
796
+ document.addEventListener('mousemove', drag, false);
797
+ document.addEventListener('mouseup', stopDrag, false);
798
+ }
799
+ draw() {
800
+ this.bar = document.createElement('div');
801
+ this.bar.className = constants.className;
802
+ this.bar.onclick = e => {
803
+ if (!e.target.classList.contains('marker') && !e.target.classList.contains('handle')) {
804
+ this.handleSeek(e);
805
+ }
806
+ };
807
+ this.bar.addEventListener('mousedown', e => {
808
+ e.preventDefault();
809
+ if (!this.dragging) {
810
+ this.handleSeekDrag(e);
811
+ }
812
+ }, false);
813
+ this.fill = document.createElement('div');
814
+ this.fill.className = 'fill';
815
+ this.loadIndicator = document.createElement('div');
816
+ this.loadIndicator.className = 'load-indicator';
817
+ this.region = document.createElement('div');
818
+ this.region.className = 'region';
819
+ this.bar.appendChild(this.fill);
820
+ this.bar.appendChild(this.loadIndicator);
821
+ this.bar.appendChild(this.region);
822
+ this.shadow.appendChild(this.bar);
823
+ this.drawMarkers();
824
+ }
825
+ drawMarkers() {
826
+ if (this.markersEnabled) {
827
+ const markerIn = this.markers[constants.markerTypes.in] = document.createElement('div');
828
+ const markerOut = this.markers[constants.markerTypes.out] = document.createElement('div');
829
+ markerIn.className = "marker ".concat(constants.markerTypes.in);
830
+ markerOut.className = "marker ".concat(constants.markerTypes.out);
831
+ const handleIn = document.createElement('div');
832
+ const handleOut = document.createElement('div');
833
+ handleIn.className = handleOut.className = 'handle';
834
+ handleIn.addEventListener('mousedown', e => {
835
+ e.preventDefault();
836
+ e.stopPropagation();
837
+ this.handleMarkerDrag(markerIn, constants.markerTypes.in, e);
838
+ }, false);
839
+ handleOut.addEventListener('mousedown', e => {
840
+ e.preventDefault();
841
+ e.stopPropagation();
842
+ this.handleMarkerDrag(markerOut, constants.markerTypes.out, e);
843
+ }, false);
844
+ markerIn.appendChild(handleIn);
845
+ markerOut.appendChild(handleOut);
846
+ this.bar.appendChild(markerIn);
847
+ this.bar.appendChild(markerOut);
848
+ }
849
+ }
850
+ updateMarkers(marker) {
851
+ this.markers[marker.type].style.left = "".concat(marker.value * 100, "%");
852
+ this.markers[marker.type].classList.add('active');
853
+ if (this.state.regions) {
854
+ const matchingRegions = [];
855
+ const isRegional = matchingRegions.length > 0;
856
+ if (isRegional) {
857
+ this.markers[marker.type].classList.add('regional');
858
+ } else {
859
+ this.markers[marker.type].classList.remove('regional');
860
+ }
861
+ }
862
+ this.drawInAndOutRegion();
863
+ }
864
+ drawInAndOutRegion() {
865
+ if (this.state.in !== null && this.state.out !== null) {
866
+ this.region.style.left = this.markers[constants.markerTypes.in].style.left;
867
+ this.region.style.right = "".concat(100 - parseFloat(this.markers[constants.markerTypes.out].style.left.replace('%', '')), "%");
868
+ }
869
+ }
870
+ handleMarkerDrag(markerNode, type, mouseDownEvent) {
871
+ const barOffset = this.bar.getBoundingClientRect().left;
872
+ const barWidth = this.bar.getBoundingClientRect().width;
873
+ const duration = this.props.player.videojs.duration();
874
+ let dragHappened = false;
875
+ const drag = e => {
876
+ const pxDragged = Math.abs(mouseDownEvent.clientX - e.clientX);
877
+ const markerOffset = type === constants.markerTypes.in ? constants.defaults.markerWidth / 2 : -(constants.defaults.markerWidth / 2);
878
+ const left = e.clientX - barOffset + markerOffset;
879
+ if (left >= 0 && left < barWidth && pxDragged > 1) {
880
+ dragHappened = this.dragging = true;
881
+ markerNode.style.left = "".concat(left, "px");
882
+ markerNode.classList.add('active');
883
+ const fractionFromStart = parseFloat(markerNode.getBoundingClientRect().left - barOffset) / barWidth;
884
+ const seconds = fractionFromStart * duration;
885
+ this.props.player.videojs.currentTime(seconds);
886
+ }
887
+ e.preventDefault();
888
+ };
889
+ const stopDrag = e => {
890
+ this.dragging = false;
891
+ document.removeEventListener('mousemove', drag, false);
892
+ document.removeEventListener('mouseup', stopDrag, false);
893
+ const fractionFromStart = parseFloat(markerNode.getBoundingClientRect().left - barOffset) / barWidth;
894
+ const seconds = fractionFromStart * duration;
895
+ this[type] = seconds;
896
+ this.props.player.videojs.currentTime(seconds);
897
+ if (!dragHappened) {
898
+ if (type === constants.markerTypes.in) {
899
+ this.navigateToIn();
900
+ } else {
901
+ this.navigateToOut();
902
+ }
903
+ }
904
+ this.props.onDragFinish && this.props.onDragFinish({
905
+ type,
906
+ seconds
907
+ });
908
+ e.stopPropagation();
909
+ };
910
+ document.addEventListener('mousemove', drag, false);
911
+ document.addEventListener('mouseup', stopDrag, false);
912
+ }
913
+ cleanupMarkers() {
914
+ let removedMarker = false;
915
+ if (this.state.in === null) {
916
+ this.markers[constants.markerTypes.in].classList.remove('active');
917
+ removedMarker = true;
918
+ }
919
+ if (this.state.out === null) {
920
+ this.markers[constants.markerTypes.out].classList.remove('active');
921
+ removedMarker = true;
922
+ }
923
+ if (removedMarker) {
924
+ this.region.style.left = 'initial';
925
+ this.region.style.right = 'initial';
926
+ }
927
+ }
928
+ clearMarkers() {
929
+ this.in = null;
930
+ this.out = null;
931
+ }
932
+ setInFromCurrent() {
933
+ this.in = this.props.player.videojs.currentTime();
934
+ }
935
+ setOutFromCurrent() {
936
+ this.out = this.props.player.videojs.currentTime();
937
+ }
938
+ navigateToIn() {
939
+ const {
940
+ player
941
+ } = this.props;
942
+ player.seekTo(this.in);
943
+ }
944
+ navigateToOut() {
945
+ const {
946
+ player
947
+ } = this.props;
948
+ player.seekTo(this.out);
949
+ }
950
+ set in(val) {
951
+ if (!this.state.markersEnabled) return;
952
+ this.state.in = val;
953
+ if (val !== null && val !== undefined) {
954
+ const {
955
+ player
956
+ } = this.props;
957
+ this.updateMarkers({
958
+ type: constants.markerTypes.in,
959
+ value: val / player.videojs.duration()
960
+ });
961
+ if (this.state.out !== null && val > this.state.out) {
962
+ this.out = null;
963
+ }
964
+ this.props.onIn && this.props.onIn(this.state.in);
965
+ } else {
966
+ this.cleanupMarkers();
967
+ }
968
+ }
969
+ get in() {
970
+ return this.state.in;
971
+ }
972
+ set out(val) {
973
+ if (!this.state.markersEnabled) return;
974
+ this.state.out = val;
975
+ if (val !== null && val !== undefined) {
976
+ const {
977
+ player
978
+ } = this.props;
979
+ this.updateMarkers({
980
+ type: constants.markerTypes.out,
981
+ value: val / player.videojs.duration()
982
+ });
983
+ if (this.state.in != null && val < this.state.in) {
984
+ this.in = null;
985
+ }
986
+ this.props.onOut && this.props.onOut(this.state.out);
987
+ } else {
988
+ this.cleanupMarkers();
989
+ }
990
+ }
991
+ get out() {
992
+ return this.state.out;
993
+ }
994
+ get markersEnabled() {
995
+ return this.state.markersEnabled;
996
+ }
997
+ set markersEnabled(bool) {
998
+ if (typeof bool === 'boolean') {
999
+ if (!bool) {
1000
+ this.clearMarkers();
1001
+ this.state.markersEnabled = false;
1002
+ } else {
1003
+ !this.markersEnabled && this.drawMarkers();
1004
+ this.state.markersEnabled = true;
1005
+ }
1006
+ }
1007
+ }
1008
+ set regions(regionArray) {
1009
+ if (this.state.regions.length < 1) {
1010
+ regionArray && regionArray.forEach(region => {
1011
+ this.addRegion(region);
1012
+ });
1013
+ } else {
1014
+ this.state.regions.forEach((region, index) => {
1015
+ this.removeRegion(index);
1016
+ });
1017
+ this.state.regions = [];
1018
+ this.regions = regionArray;
1019
+ }
1020
+ }
1021
+ get regions() {
1022
+ return this.state.regions;
1023
+ }
1024
+ set dragging(bool) {
1025
+ if (bool && !this.state.dragging) {
1026
+ this.state.dragging = true;
1027
+ this.bar.classList.add('dragging');
1028
+ } else if (!bool && this.state.dragging) {
1029
+ this.state.dragging = false;
1030
+ this.bar.classList.remove('dragging');
1031
+ }
1032
+ }
1033
+ get dragging() {
1034
+ return this.state.dragging;
1035
+ }
1036
+ addRegion(regionObj) {
1037
+ if (regionObj.constructor.name === Region.name) {
1038
+ this.state.regions.push(regionObj);
1039
+ this.drawRegion(regionObj);
1040
+ } else {
1041
+ this.addRegion(new Region(regionObj));
1042
+ }
1043
+ }
1044
+ removeRegion(param) {
1045
+ if (typeof param === typeof Region) ; else {
1046
+ this.state.regions[param].el.remove();
1047
+ this.state.regions[param] = null;
1048
+ delete this.state.regions[param];
1049
+ }
1050
+ }
1051
+ drawRegion(region) {
1052
+ const duration = this.props.player.videojs.duration();
1053
+ const inPoint = region.in;
1054
+ const leftPercentage = inPoint / duration * 100;
1055
+ const rightPercentage = (duration - region.out) / duration * 100;
1056
+ const regionEl = document.createElement('div');
1057
+ regionEl.className = 'region nav ';
1058
+ regionEl.innerHTML = '<div class="bubble"></div>';
1059
+ if (region.title && region.annotation) {
1060
+ let alignmentClassName = '';
1061
+ if (leftPercentage > 70) alignmentClassName = 'right-aligned';
1062
+ if (leftPercentage < 30) alignmentClassName = 'left-aligned';
1063
+ regionEl.className += " has-popout ".concat(alignmentClassName);
1064
+ const popout = document.createElement('div');
1065
+ popout.className = 'popout';
1066
+ popout.innerHTML = "\n <div class=\"title\">\n ".concat(sanitize(region.title), "\n </div>\n <div class=\"annotation\">\n ").concat(truncate(sanitize(region.annotation), 72), "\n </div>\n ");
1067
+ regionEl.appendChild(popout);
1068
+ }
1069
+ regionEl.style.left = "".concat(leftPercentage, "%");
1070
+ regionEl.style.right = "".concat(rightPercentage, "%");
1071
+ region.el = regionEl;
1072
+ this.bar.appendChild(regionEl);
1073
+ }
1074
+ set onDark(bool) {
1075
+ if (bool) {
1076
+ this.bar.classList.add(classNames.onDark);
1077
+ } else {
1078
+ this.bar.classList.remove(classNames.onDark);
1079
+ }
1080
+ }
1081
+ get onDark() {
1082
+ return this.container.classList.contains(classNames.onDark);
1083
+ }
1084
+ set percentLoaded(val) {
1085
+ this.loadIndicator.style.width = "".concat(val, "%");
1086
+ }
1087
+ destroy() {
1088
+ this.unbindHotKeys();
1089
+ }
1090
+ toggleMarkers() {
1091
+ if (this.bar.classList.contains('hide-markers')) {
1092
+ this.showMarkers();
1093
+ } else {
1094
+ this.hideMarkers();
1095
+ }
1096
+ }
1097
+ hideMarkers() {
1098
+ this.bar.classList.add('hide-markers');
1099
+ }
1100
+ showMarkers() {
1101
+ this.bar.classList.remove('hide-markers');
1102
+ }
1103
+ }
1104
+ SeekBar.Region = Region;
1105
+
1106
+ const timeCodeNodeNames = ['hours', 'minutes', 'seconds', 'frame'];
1107
+ class TimeCodeDisplay {
1108
+ constructor(props) {
1109
+ this.props = props;
1110
+ this.state = {
1111
+ selectedNode: null
1112
+ };
1113
+ const classNames = ['vdt-timecode', 'vjs-time-control',
1114
+ !this.props.onInput ? 'duration' : ''
1115
+ ];
1116
+ this.container = document.createElement('div');
1117
+ this.container.className = classNames.join(' ');
1118
+ this.nodeNames = [].concat(timeCodeNodeNames);
1119
+ this.nodes = {};
1120
+ this.nodeNames.forEach((nodeName, index) => {
1121
+ const node = document.createElement('input');
1122
+ node.className = nodeName;
1123
+ node.tabIndex = -1;
1124
+ if (this.props.onInput) {
1125
+ node.maxLength = 2;
1126
+ node.onfocus = e => {
1127
+ this.handleInputFocus(e, nodeName);
1128
+ };
1129
+ node.onblur = e => {
1130
+ this.handleInputBlur(e, nodeName);
1131
+ };
1132
+ node.onmouseup = e => {
1133
+ e.preventDefault();
1134
+ e.currentTarget.select();
1135
+ };
1136
+ node.onkeydown = e => {
1137
+ this.handleTimeCodeInput(e, nodeName);
1138
+ };
1139
+ node.onkeyup = e => {
1140
+ this.handleTimeCodeInputOnKeyUp(e, nodeName);
1141
+ };
1142
+ } else {
1143
+ node.readOnly = true;
1144
+ }
1145
+ this.nodes[nodeName] = node;
1146
+ const colon = document.createElement('span');
1147
+ colon.innerHTML = ':';
1148
+ this.container.appendChild(node);
1149
+ index !== this.nodeNames.length - 1 && this.container.appendChild(colon);
1150
+ });
1151
+ if (this.props.target) {
1152
+ if (this.props.appendAfter) {
1153
+ this.props.target.parentNode.insertBefore(this.container, this.props.target.nextSibling);
1154
+ } else {
1155
+ this.props.target.appendChild(this.container);
1156
+ }
1157
+ this.reset();
1158
+ } else {
1159
+ console.error("Unable to instantiate a new ".concat(this.constructor.name, " without target DOM node"));
1160
+ }
1161
+ }
1162
+ update(valuesObj) {
1163
+ const startCount = this.nodeNames.length - 1;
1164
+ let i;
1165
+ for (i = startCount; i > -1; i--) {
1166
+ const key = this.nodeNames[i];
1167
+ if (typeof valuesObj[key] !== 'undefined' && valuesObj[key] !== parseInt(this.nodes[key].value, 10) && key !== this.state.selectedNode) {
1168
+ const str = valuesObj[key].toString();
1169
+ this.nodes[key].value = this.toDigitPairStr(str);
1170
+ }
1171
+ }
1172
+ }
1173
+ reset() {
1174
+ this.update({
1175
+ hours: 0,
1176
+ minutes: 0,
1177
+ seconds: 0,
1178
+ frame: 0
1179
+ });
1180
+ }
1181
+ handleInputFocus(e, nodeName) {
1182
+ this.state.selectedNode = nodeName;
1183
+ this.nodes[nodeName].select();
1184
+ this.props.onInputFocus && this.props.onInputFocus(nodeName);
1185
+ }
1186
+ handleInputBlur(e, nodeName) {
1187
+ this.state.selectedNode = null;
1188
+ this.props.onInputBlur && this.props.onInputBlur(nodeName);
1189
+ }
1190
+ sanitize(input) {
1191
+ const output = input.replace(/[^0-9]/g, '');
1192
+ return output;
1193
+ }
1194
+ toDigitPairStr(strValue) {
1195
+ return strValue.length < 2 ? "0".concat(strValue) : strValue;
1196
+ }
1197
+ handleTimeCodeInput(e, nodeName) {
1198
+ const nodeIndex = this.nodeNames.indexOf(nodeName);
1199
+ switch (e.code) {
1200
+ case 'Enter':
1201
+ this.checkAndNormalizeAllInputs();
1202
+ this.props.onInput && this.props.onInput(e, nodeName, {
1203
+ hours: this.hours,
1204
+ minutes: this.minutes,
1205
+ seconds: this.seconds,
1206
+ frame: this.frame
1207
+ });
1208
+ this[nodeName] = e.currentTarget.value;
1209
+ e.currentTarget.blur();
1210
+ break;
1211
+ case 'Tab':
1212
+ e.preventDefault();
1213
+ if (!e.shiftKey) {
1214
+ if (nodeIndex + 1 <= this.nodeNames.length - 1) {
1215
+ this.nodes[this.nodeNames[nodeIndex + 1]].focus();
1216
+ } else {
1217
+ this.nodes[this.nodeNames[0]].focus();
1218
+ }
1219
+ } else if (nodeIndex - 1 >= 0) {
1220
+ this.nodes[this.nodeNames[nodeIndex - 1]].focus();
1221
+ } else {
1222
+ this.nodes[this.nodeNames[this.nodeNames.length - 1]].focus();
1223
+ }
1224
+ break;
1225
+ }
1226
+ }
1227
+ handleTimeCodeInputOnKeyUp(e, nodeName) {
1228
+ if (this.nodes[nodeName].value.length === 2 && !isNaN(e.key)) {
1229
+ const nodeIndex = this.nodeNames.indexOf(nodeName);
1230
+ if (nodeIndex + 1 <= this.nodeNames.length - 1) {
1231
+ this.nodes[this.nodeNames[nodeIndex + 1]].focus();
1232
+ }
1233
+ }
1234
+ }
1235
+ checkAndNormalizeAllInputs() {
1236
+ this.nodeNames.forEach(nodeName => {
1237
+ if (this.nodes[nodeName].value.length < 1) {
1238
+ this.nodes[nodeName].value = '00';
1239
+ }
1240
+ this.nodes[nodeName].value = this.toDigitPairStr(this.sanitize(this.nodes[nodeName].value));
1241
+ });
1242
+ }
1243
+ set frame(val) {
1244
+ this.nodes.frame.value = this.toDigitPairStr(val);
1245
+ }
1246
+ get frame() {
1247
+ return parseInt(this.nodes.frame.value, 10);
1248
+ }
1249
+ set seconds(val) {
1250
+ this.nodes.seconds.value = this.toDigitPairStr(val);
1251
+ }
1252
+ get seconds() {
1253
+ return parseInt(this.nodes.seconds.value, 10);
1254
+ }
1255
+ set minutes(val) {
1256
+ this.nodes.minutes.value = this.toDigitPairStr(val);
1257
+ }
1258
+ get minutes() {
1259
+ return parseInt(this.nodes.minutes.value, 10);
1260
+ }
1261
+ set hours(val) {
1262
+ this.nodes.hours.value = this.toDigitPairStr(val);
1263
+ }
1264
+ get hours() {
1265
+ return parseInt(this.nodes.hours.value, 10);
1266
+ }
1267
+ getFormatted(key) {
1268
+ return this.toDigitPairStr(this[key].toString());
1269
+ }
1270
+ get smpte() {
1271
+ return {
1272
+ hours: this.hours,
1273
+ minutes: this.minutes,
1274
+ seconds: this.seconds,
1275
+ frame: this.frame
1276
+ };
1277
+ }
1278
+ set smpte(o) {
1279
+ this.update(o);
1280
+ }
1281
+ get smpteString() {
1282
+ return "".concat(this.getFormatted('hours'), ":").concat(this.getFormatted('minutes'), ":").concat(this.getFormatted('seconds'), ":").concat(this.getFormatted('frame'));
1283
+ }
1284
+ set external(bool) {
1285
+ if (bool) {
1286
+ this.container.classList.add('external');
1287
+ } else {
1288
+ this.container.classList.remove('external');
1289
+ }
1290
+ }
1291
+ get external() {
1292
+ return this.container.classList.contains('external');
1293
+ }
1294
+ }
1295
+
1296
+ class VideoTime {
1297
+ constructor(props) {
1298
+ if (props.frameRate === undefined) {
1299
+ delete this;
1300
+ throw new Error('No "frameRate" option provided, refer to docs.');
1301
+ } else {
1302
+ this.props = props;
1303
+ }
1304
+ }
1305
+ secondsToFramesFromStart(seconds) {
1306
+ let frame = seconds * this.props.frameRate;
1307
+ frame = Math.round(frame);
1308
+ return frame;
1309
+ }
1310
+ currentTimeToFramesFromStart(currentTime) {
1311
+ return this.secondsToFramesFromStart(currentTime);
1312
+ }
1313
+ secondsToFrame(seconds) {
1314
+ let frame = this.secondsToFramesFromStart(seconds) % this.props.frameRate;
1315
+ frame = Math.round(frame);
1316
+ return frame;
1317
+ }
1318
+ currentFrame(currentTime) {
1319
+ return Math.round(this.secondsToFrame(currentTime));
1320
+ }
1321
+ frameToCurrentTimeFromStart(frame) {
1322
+ return frame / this.props.frameRate;
1323
+ }
1324
+ smpteToCurrentTimeFromStart(values) {
1325
+ return values.hours * 3600 + values.minutes * 60 + values.seconds + values.frame / this.props.frameRate;
1326
+ }
1327
+ secondsFromStartToSMPTE(secondsFromStart) {
1328
+ let secondsFS = secondsFromStart;
1329
+ const hours = Math.floor(secondsFS / 3600);
1330
+ const minutesWithoutHours = secondsFS / 60 - 60 * hours;
1331
+ secondsFS %= 3600;
1332
+ const minutes = Math.floor(minutesWithoutHours);
1333
+ const seconds = Math.floor(secondsFS % 60);
1334
+ const frame = this.secondsToFrame(secondsFS);
1335
+ return {
1336
+ hours,
1337
+ minutes,
1338
+ seconds,
1339
+ frame
1340
+ };
1341
+ }
1342
+ }
1343
+
1344
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
1345
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
1346
+ const correctMimeTypes = {
1347
+ 'audio/x-aac': 'audio/aac'
1348
+ };
1349
+ const sanitizeMimeType = mimeType => correctMimeTypes[mimeType] || mimeType;
1350
+ class Player {
1351
+ constructor(props) {
1352
+ this.props = props;
1353
+ reactiveSetters.apply(this);
1354
+ const classNames = [
1355
+ 'video-js', 'vjs-big-play-centered', this.props.posterUrl ? constants$2.classNames.poster : '', this.props.subtitles && this.props.subtitles.length > 0 ? constants$2.classNames.subtitles : '', 'init'
1356
+ ];
1357
+ this.externalTimecodes = {
1358
+ duration: [],
1359
+ playback: []
1360
+ };
1361
+ this.timeConverter = new VideoTime({
1362
+ frameRate: this.props.frameRate || constants$2.defaults.frameRate
1363
+ });
1364
+ this.videoEl = document.createElement('video');
1365
+ this.videoEl.crossOrigin = this.props.crossOrigin;
1366
+ this.videoEl.id = 'vdt-video';
1367
+ this.videoEl.className = classNames.join(' ');
1368
+ this.videoEl.controls = true;
1369
+ if (this.props.sources) {
1370
+ if (this.props.sources.length > 1) {
1371
+ this.props.sources.sort(a => a.type.indexOf('mp4') > 0 ? 1 : -1);
1372
+ }
1373
+ this.props.sources.forEach(source => {
1374
+ const aSource = document.createElement('source');
1375
+ aSource.src = source.src;
1376
+ aSource.type = sanitizeMimeType(source.type);
1377
+ this.videoEl.appendChild(aSource);
1378
+ });
1379
+ }
1380
+ this.videoEl.onloadedmetadata = () => {
1381
+ if (this.durationTimeCode) {
1382
+ this.setDurationTimeCode();
1383
+ }
1384
+ if (this.videojs.el_) this.videojs.el_.classList.remove('init');
1385
+ };
1386
+ setTimeout(() => {
1387
+ if (this.videojs.el_) this.videojs.el_.classList.remove('init');
1388
+ }, 2000);
1389
+ if (this.props.subtitles) {
1390
+ this.props.subtitles.forEach(sub => {
1391
+ const subtitleTag = document.createElement('track');
1392
+ subtitleTag.kind = 'subtitles';
1393
+ subtitleTag.srclang = sub.lang;
1394
+ subtitleTag.label = sub.label;
1395
+ if (typeof sub.src === 'string') {
1396
+ subtitleTag.src = sub.src;
1397
+ } else {
1398
+ subtitleTag.src = window.URL.createObjectURL(sub.src);
1399
+ }
1400
+ this.videoEl.appendChild(subtitleTag);
1401
+ });
1402
+ }
1403
+ if (this.props.target) this.props.target.appendChild(this.videoEl);
1404
+ const showDefaultTimeDisplay = this.props.timecode !== true;
1405
+ const controlsEnabled = this.props.controls !== false;
1406
+ const options = this.props.options || {};
1407
+ this.videojs = new videojs('vdt-video', _objectSpread({
1408
+ responsive: true,
1409
+ fluid: true,
1410
+ controls: controlsEnabled,
1411
+ alwaysCaptureHotkeys: false,
1412
+ controlBar: {
1413
+ RemainingTimeDisplay: false,
1414
+ timeDivider: showDefaultTimeDisplay,
1415
+ durationDisplay: showDefaultTimeDisplay,
1416
+ currentTimeDisplay: showDefaultTimeDisplay
1417
+ },
1418
+ inactivityTimeout: this.audioOnly ? constants$2.defaults.audioInactivityTimeout : constants$2.defaults.inactivityTimeout,
1419
+ preload: 'auto',
1420
+ captionsButton: false,
1421
+ subsCapsButton: false,
1422
+ subtitlesButton: false,
1423
+ textTrackSettings: false,
1424
+ html5: {
1425
+ nativeTextTracks: false
1426
+ },
1427
+ languages: {
1428
+ en: {
1429
+ 'captions off': 'off',
1430
+ 'subtitles off': 'off'
1431
+ }
1432
+ }
1433
+ }, options));
1434
+ if (this.props.posterUrl) this.videojs.poster(this.props.posterUrl);
1435
+ this.handleNewProps(this.props);
1436
+ this.videojs.ready(() => {
1437
+ this.bindHotkeys();
1438
+ this.bindEvents();
1439
+ if (this.props.onReady) this.props.onReady(this.videojs);
1440
+ this.restoreSettings();
1441
+ this.setupTimeCode();
1442
+ this.doEsothericWork();
1443
+ this.setupMenu();
1444
+ this.setupCustomSeekbar();
1445
+ });
1446
+ }
1447
+ setupCustomSeekbar() {
1448
+ if (this.props.seekBar) {
1449
+ this.videojs.controlBar.progressControl.disable();
1450
+ this.videojs.controlBar.progressControl.hide();
1451
+ const target = this.container.getElementsByClassName('vjs-control-bar')[0];
1452
+ const props = _objectSpread({
1453
+ target,
1454
+ player: this,
1455
+ onDark: this.props.onDark
1456
+ }, this.props.seekBar);
1457
+ this.seekBar = new SeekBar(props);
1458
+ this.videojs.on('userinactive', () => {
1459
+ if (!this.controlsBelowPlayer) {
1460
+ var _this$seekBar$hideMar, _this$seekBar;
1461
+ if (this.seekBar) (_this$seekBar$hideMar = (_this$seekBar = this.seekBar).hideMarkers) === null || _this$seekBar$hideMar === void 0 ? void 0 : _this$seekBar$hideMar.call(_this$seekBar);
1462
+ } else {
1463
+ var _this$seekBar2, _this$seekBar2$showMa;
1464
+ (_this$seekBar2 = this.seekBar) === null || _this$seekBar2 === void 0 ? void 0 : (_this$seekBar2$showMa = _this$seekBar2.showMarkers) === null || _this$seekBar2$showMa === void 0 ? void 0 : _this$seekBar2$showMa.call(_this$seekBar2);
1465
+ }
1466
+ });
1467
+ this.videojs.on('useractive', () => {
1468
+ if (this.seekBar) this.seekBar.showMarkers();
1469
+ });
1470
+ }
1471
+ }
1472
+ setupTimeCode() {
1473
+ let externalTimecodeInstance = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
1474
+ const props = {
1475
+ onInput: (e, nodeName, values) => {
1476
+ this.seekTo(this.timeConverter.smpteToCurrentTimeFromStart(values));
1477
+ },
1478
+ onInputFocus: () => {
1479
+ this.videojs.options_.inactivityTimeout = 0;
1480
+ this.videojs.cache_.inactivityTimeout = 0;
1481
+ this.videojs.reportUserActivity();
1482
+ this.videojs.pause();
1483
+ },
1484
+ onInputBlur: () => {
1485
+ this.videojs.options().inactivityTimeout = constants$2.defaults.inactivityTimeout;
1486
+ this.videojs.cache_.inactivityTimeout = constants$2.defaults.inactivityTimeout;
1487
+ }
1488
+ };
1489
+ if (externalTimecodeInstance !== null) {
1490
+ externalTimecodeInstance.props = Object.assign(externalTimecodeInstance.props, props);
1491
+ this.externalTimecodes.playback.push(externalTimecodeInstance);
1492
+ } else if (this.props.timecode === true) {
1493
+ if (this.props.controls !== false) {
1494
+ const target = document.getElementsByClassName('vjs-volume-panel')[0];
1495
+ this.timeCode = new TimeCodeDisplay(Object.assign(props, {
1496
+ target,
1497
+ appendAfter: true
1498
+ }));
1499
+ this.durationTimeCode = new TimeCodeDisplay({
1500
+ target: this.timeCode.container,
1501
+ appendAfter: true
1502
+ });
1503
+ }
1504
+ }
1505
+ if (this.timeCode || this.externalTimecodes.playback.length > 0) {
1506
+ this.videojs.on('timeupdate', () => {
1507
+ this.updateTimeCode();
1508
+ });
1509
+ this.videojs.on('play', () => {
1510
+ this.updateTimeCodeFrame();
1511
+ });
1512
+ }
1513
+ }
1514
+ updateTimeCode() {
1515
+ const currentSecond = this.videojs.currentTime();
1516
+ const props = this.timeConverter.secondsFromStartToSMPTE(currentSecond);
1517
+ if (this.timeCode) this.timeCode.update(props);
1518
+ if (this.externalTimecodes.playback.length > 0) {
1519
+ this.externalTimecodes.playback.forEach(timecode => {
1520
+ timecode.update(props);
1521
+ });
1522
+ }
1523
+ }
1524
+ updateTimeCodeFrame() {
1525
+ const frame = this.timeConverter.currentFrame(this.videojs.currentTime());
1526
+ if (this.timeCode) this.timeCode.update({
1527
+ frame
1528
+ });
1529
+ if (this.externalTimecodes.playback.length > 0) {
1530
+ this.externalTimecodes.playback.forEach(timecode => {
1531
+ timecode.update({
1532
+ frame
1533
+ });
1534
+ });
1535
+ }
1536
+ if (!this.videojs.paused()) {
1537
+ this.rafID = requestAnimationFrame(() => {
1538
+ this.updateTimeCodeFrame();
1539
+ });
1540
+ }
1541
+ }
1542
+ setDurationTimeCode() {
1543
+ const durationInSeconds = this.videojs.duration();
1544
+ const props = this.timeConverter.secondsFromStartToSMPTE(durationInSeconds);
1545
+ props.frame = this.timeConverter.secondsToFrame(durationInSeconds);
1546
+ this.durationTimeCode.update(props);
1547
+ this.durationTimeCode.container.classList.add('duration');
1548
+ }
1549
+ bindHotkeys() {
1550
+ this.bindings = [{
1551
+ key: ['space', 'k'],
1552
+ method: e => {
1553
+ if (e.preventDefault) e.preventDefault();
1554
+ this.togglePlayback();
1555
+ }
1556
+ }, {
1557
+ key: 'j',
1558
+ method: () => {
1559
+ if (this.videojs.hasStarted_) this.jumpBackward();
1560
+ }
1561
+ }, {
1562
+ key: 'l',
1563
+ method: () => {
1564
+ if (this.videojs.hasStarted_) this.jumpForward();
1565
+ }
1566
+ }, {
1567
+ key: ['.', 'right'],
1568
+ method: () => {
1569
+ this.frameForward();
1570
+ }
1571
+ }, {
1572
+ key: [',', 'left'],
1573
+ method: () => {
1574
+ this.frameBackward();
1575
+ }
1576
+ }, {
1577
+ key: 'f',
1578
+ method: () => {
1579
+ this.toggleFullscreen();
1580
+ }
1581
+ }, {
1582
+ key: 'm',
1583
+ method: () => {
1584
+ this.toggleMute();
1585
+ }
1586
+ }, {
1587
+ key: 'up',
1588
+ method: e => {
1589
+ if (e.preventDefault) e.preventDefault();
1590
+ this.volumeUp();
1591
+ }
1592
+ }, {
1593
+ key: 'down',
1594
+ method: e => {
1595
+ if (e.preventDefault) e.preventDefault();
1596
+ this.volumeDown();
1597
+ }
1598
+ }, {
1599
+ key: 'h',
1600
+ method: () => {
1601
+ this.help.toggle();
1602
+ }
1603
+ }, {
1604
+ key: 'n',
1605
+ method: () => {
1606
+ if (this.menu) this.menu.toggle();
1607
+ }
1608
+ }];
1609
+ if (this.props.subtitles) {
1610
+ this.bindings.push({
1611
+ key: 'c',
1612
+ method: () => {
1613
+ this.toggleSubs();
1614
+ }
1615
+ });
1616
+ }
1617
+ this.bindings.forEach(binding => {
1618
+ mousetrap.bind(binding.key, e => {
1619
+ binding.method(e);
1620
+ this.onHotkeyPressed(binding.key);
1621
+ });
1622
+ });
1623
+ }
1624
+ unbindHotKeys() {
1625
+ if (this.bindings) this.bindings.forEach(binding => {
1626
+ mousetrap.unbind(binding.key);
1627
+ });
1628
+ }
1629
+ bindEvents() {
1630
+ this.videojs.on('volumechange', () => {
1631
+ this.persistSetting(constants$2.settings.volume, this.volume);
1632
+ });
1633
+ this.videojs.textTracks().on('change', () => {
1634
+ const tracks = this.videojs.textTracks().tracks_;
1635
+ const selected = tracks.filter(track => track.mode === 'showing');
1636
+ const areAnyTxtTracksActive = selected.length > 0;
1637
+ if (areAnyTxtTracksActive) {
1638
+ this.videojs.el_.classList.add(constants$2.classNames.subtitlesActive);
1639
+ this.persistSetting(constants$2.settings.subs, true);
1640
+ this.persistSetting(constants$2.settings.subLang, selected[0].language);
1641
+ } else {
1642
+ this.videojs.el_.classList.remove(constants$2.classNames.subtitlesActive);
1643
+ this.persistSetting(constants$2.settings.subs, false);
1644
+ }
1645
+ });
1646
+ }
1647
+ volumeUp() {
1648
+ this.videojs.controlBar.volumePanel.volumeControl.volumeBar.stepForward();
1649
+ this.osd.feedback(constants$2.osd.volchange, {
1650
+ volume: this.volume
1651
+ });
1652
+ }
1653
+ volumeDown() {
1654
+ this.videojs.controlBar.volumePanel.volumeControl.volumeBar.stepBack();
1655
+ this.osd.feedback(constants$2.osd.volchange, {
1656
+ volume: this.volume
1657
+ });
1658
+ }
1659
+ toggleMute() {
1660
+ if (!this.videojs.muted()) {
1661
+ this.videojs.muted(true);
1662
+ this.osd.feedback(constants$2.osd.volchange, {
1663
+ volume: this.volume
1664
+ });
1665
+ } else {
1666
+ this.videojs.muted(false);
1667
+ this.osd.feedback(constants$2.osd.volchange, {
1668
+ volume: this.volume
1669
+ });
1670
+ }
1671
+ }
1672
+ toggleFullscreen() {
1673
+ if (this.videojs.isFullscreen()) {
1674
+ this.videojs.exitFullscreen();
1675
+ } else {
1676
+ this.videojs.requestFullscreen();
1677
+ }
1678
+ }
1679
+ seek(secs) {
1680
+ let time = this.videojs.currentTime() + secs;
1681
+ if (time < 0) {
1682
+ time = 0;
1683
+ }
1684
+ this.videojs.currentTime(time);
1685
+ }
1686
+ seekTo(sec) {
1687
+ if (!this.videojs.hasStarted()) this.videojs.hasStarted(true);
1688
+ this.videojs.currentTime(sec);
1689
+ }
1690
+ jumpForward() {
1691
+ this.seek(constants$2.defaults.jumpSeconds);
1692
+ this.osd.feedback(constants$2.osd.jumpForward);
1693
+ }
1694
+ jumpBackward() {
1695
+ this.seek(-constants$2.defaults.jumpSeconds);
1696
+ this.osd.feedback(constants$2.osd.jumpBackward);
1697
+ }
1698
+ frameForward() {
1699
+ const thisFrame = this.timeConverter.secondsToFramesFromStart(this.videojs.currentTime());
1700
+ const nextFrame = Math.round(thisFrame) + 1;
1701
+ this.videojs.currentTime(this.timeConverter.frameToCurrentTimeFromStart(nextFrame));
1702
+ }
1703
+ frameBackward() {
1704
+ const thisFrame = this.timeConverter.secondsToFramesFromStart(this.videojs.currentTime());
1705
+ const previousFrame = Math.round(thisFrame) - 1;
1706
+ this.videojs.currentTime(this.timeConverter.frameToCurrentTimeFromStart(previousFrame));
1707
+ }
1708
+ secondsToFramesFromStart(seconds) {
1709
+ const frameRate = this.props.frameRate || constants$2.defaults.frameRate;
1710
+ let frame = seconds * frameRate;
1711
+ frame = Math.round(frame);
1712
+ return frame;
1713
+ }
1714
+ doEsothericWork() {
1715
+ if (this.props.color) {
1716
+ document.getElementsByClassName('vjs-play-progress')[0].style.backgroundColor = this.props.color;
1717
+ }
1718
+ this.osd = new OSD({
1719
+ target: this.videoEl.parentElement
1720
+ });
1721
+ if (this.props.onCinema) {
1722
+ this.videoEl.parentElement.classList.add('has-cinema-button');
1723
+ this.cinema = false;
1724
+ this.addButtonToControlBar({
1725
+ onClick: () => {
1726
+ this.props.onCinema(!this.cinema);
1727
+ this.cinema = !this.cinema;
1728
+ },
1729
+ componentName: 'CinemaButton',
1730
+ className: 'vdt-cinema-button',
1731
+ title: 'Cinema-mode'
1732
+ });
1733
+ }
1734
+ if (this.props.onScreenshot) {
1735
+ this.videoEl.parentElement.classList.add('has-screenshot-button');
1736
+ this.addButtonToControlBar({
1737
+ onClick: () => {
1738
+ this.props.onScreenshot(this.videojs.currentTime());
1739
+ },
1740
+ componentName: 'ScreenshotButton',
1741
+ className: 'vdt-screenshot-button',
1742
+ title: 'Screenshot'
1743
+ });
1744
+ }
1745
+ this.help = new HelpOverlay({
1746
+ target: this.videoEl.parentElement,
1747
+ onToggle: willShow => {
1748
+ if (this.menu && this.menu.opened) {
1749
+ this.menu.close();
1750
+ }
1751
+ if (willShow && !this.controlsBelowPlayer) {
1752
+ this.hideControls();
1753
+ } else {
1754
+ this.showControls();
1755
+ }
1756
+ }
1757
+ });
1758
+ }
1759
+ restoreSettings() {
1760
+ const volume = localStorage.getItem(constants$2.settings.volume);
1761
+ const subsEnabled = localStorage.getItem(constants$2.settings.subs);
1762
+ const subLang = localStorage.getItem(constants$2.settings.subLang);
1763
+ this.settings = {
1764
+ [constants$2.settings.volume]: volume !== null ? Math.round(volume * 100) / 100 : 1,
1765
+ [constants$2.settings.subLang]: subLang !== null ? subLang : 'en',
1766
+ [constants$2.settings.subs]: subsEnabled !== null ? JSON.parse(subsEnabled) : false
1767
+ };
1768
+ this.volume = this.settings[constants$2.settings.volume];
1769
+ if (this.settings[constants$2.settings.subs]) {
1770
+ this.enableSubs();
1771
+ }
1772
+ }
1773
+ persistSetting(key, val) {
1774
+ this.settings[key] = val;
1775
+ localStorage.setItem(key, val);
1776
+ }
1777
+ enableSubs() {
1778
+ const subs = this.videojs.textTracks();
1779
+ for (let i = 0; i < subs.length; i += 1) {
1780
+ const sub = subs[i];
1781
+ if (sub.language === this.settings[constants$2.settings.subLang]) {
1782
+ sub.mode = 'showing';
1783
+ } else {
1784
+ sub.mode = 'hidden';
1785
+ }
1786
+ }
1787
+ }
1788
+ disableSubs() {
1789
+ const subs = this.videojs.textTracks();
1790
+ for (let i = 0; i < subs.length; i += 1) {
1791
+ const sub = subs[i];
1792
+ sub.mode = 'hidden';
1793
+ }
1794
+ }
1795
+ toggleSubs() {
1796
+ if (this.videojs.textTracks().length > 0) {
1797
+ if (this.videojs.textTracks().tracks_.filter(track => track.mode === 'showing').length > 0) {
1798
+ this.disableSubs();
1799
+ } else {
1800
+ this.enableSubs();
1801
+ }
1802
+ }
1803
+ }
1804
+ addButtonToControlBar(options) {
1805
+ const Button = videojs.getComponent('Button');
1806
+ const CustomButton = videojs.extend(Button, {
1807
+ constructor: function constructor() {
1808
+ Button.apply(this, arguments);
1809
+ if (options.className) this.el_.classList.add(options.className);
1810
+ if (options.title) {
1811
+ this.el_.title = options.title;
1812
+ }
1813
+ },
1814
+ handleClick: () => {
1815
+ if (options.onClick) options.onClick();
1816
+ }
1817
+ });
1818
+ videojs.registerComponent(options.componentName, CustomButton);
1819
+ this.videojs.getChild('controlBar').addChild(options.componentName, {}, 7);
1820
+ }
1821
+ get volume() {
1822
+ return this.videojs.controlBar.volumePanel.volumeControl.volumeBar.getPercent();
1823
+ }
1824
+ set volume(floatVal) {
1825
+ if (floatVal === 0) {
1826
+ this.videojs.muted(true);
1827
+ } else {
1828
+ this.videojs.muted(false);
1829
+ this.videojs.volume(floatVal);
1830
+ }
1831
+ }
1832
+ setupMenu() {
1833
+ const menuItems = this.props.menuItems || [];
1834
+ const sourcesMenuItems = [];
1835
+ if (this.props.sources.length > 1) {
1836
+ this.props.sources.forEach(source => {
1837
+ if (source.label) {
1838
+ sourcesMenuItems.push({
1839
+ title: source.label,
1840
+ onClick: () => {
1841
+ this.src = source;
1842
+ }
1843
+ });
1844
+ }
1845
+ });
1846
+ menuItems.push({
1847
+ title: 'Sources',
1848
+ getActiveIndex: () => {
1849
+ const matchingSource = this.props.sources.find(source => source.src === this.videojs.src());
1850
+ return sourcesMenuItems.indexOf(sourcesMenuItems.find(menuItem => menuItem.title === matchingSource.label));
1851
+ },
1852
+ items: sourcesMenuItems
1853
+ });
1854
+ }
1855
+ if (this.props.subtitles && this.props.subtitles.length > 0) {
1856
+ const subtitleMenuItems = [{
1857
+ title: 'Off',
1858
+ onClick: () => {
1859
+ this.disableSubs();
1860
+ }
1861
+ }];
1862
+ this.props.subtitles.forEach((subtitle, index) => {
1863
+ subtitleMenuItems.push({
1864
+ title: subtitle.label,
1865
+ onClick: () => {
1866
+ const subs = this.videojs.textTracks();
1867
+ for (let i = 0; i < subs.length; i += 1) {
1868
+ const sub = subs[i];
1869
+ if (i === index) {
1870
+ sub.mode = 'showing';
1871
+ } else {
1872
+ sub.mode = 'hidden';
1873
+ }
1874
+ }
1875
+ }
1876
+ });
1877
+ });
1878
+ menuItems.push({
1879
+ title: 'Subtitles',
1880
+ getActiveIndex: () => {
1881
+ const subs = this.videojs.textTracks();
1882
+ for (let i = 0; i < subs.length; i += 1) {
1883
+ const sub = subs[i];
1884
+ if (sub.mode === 'showing') {
1885
+ return i + 1;
1886
+ }
1887
+ }
1888
+ return 0;
1889
+ },
1890
+ items: subtitleMenuItems
1891
+ });
1892
+ }
1893
+ if (menuItems.length > 0) {
1894
+ const target = this.videojs.el_;
1895
+ this.menu = new Menu({
1896
+ target,
1897
+ items: menuItems,
1898
+ onOpen: () => {
1899
+ if (!this.controlsBelowPlayer) this.hideControls();
1900
+ },
1901
+ onClose: () => {
1902
+ if (!this.controlsBelowPlayer) this.videojs.controls(true);
1903
+ }
1904
+ });
1905
+ this.videoEl.parentElement.classList.add('has-vdt-menu');
1906
+ this.addButtonToControlBar({
1907
+ onClick: () => {
1908
+ this.menu.toggle();
1909
+ },
1910
+ componentName: 'SettingsMenuButton',
1911
+ className: 'vdt-settings-menu-button',
1912
+ title: 'Settings'
1913
+ });
1914
+ }
1915
+ }
1916
+ hideControls() {
1917
+ this.videojs.userActive(false);
1918
+ this.videojs.controls(false);
1919
+ this.videojs.fluid(true);
1920
+ this.videoEl.parentElement.classList.remove('vjs-user-active');
1921
+ this.videojs.userActive(false);
1922
+ }
1923
+ showControls() {
1924
+ this.videojs.fluid(!this.controlsBelowPlayer);
1925
+ this.videojs.controls(true);
1926
+ this.videojs.userActive(true);
1927
+ }
1928
+ toggleControls() {
1929
+ if (this.videojs.controls()) {
1930
+ this.hideControls();
1931
+ } else {
1932
+ this.showControls();
1933
+ }
1934
+ }
1935
+ onHotkeyPressed(key) {
1936
+ if (key !== 'h' && this.help.state.active) {
1937
+ this.help.hide();
1938
+ }
1939
+ }
1940
+ triggerHotkey(key) {
1941
+ mousetrap.trigger(key);
1942
+ }
1943
+ toStart() {
1944
+ this.videojs.currentTime(0);
1945
+ }
1946
+ toEnd() {
1947
+ this.videojs.currentTime(this.videojs.duration());
1948
+ }
1949
+ replay() {
1950
+ this.toStart();
1951
+ this.videojs.play();
1952
+ }
1953
+ play() {
1954
+ this.videojs.play();
1955
+ }
1956
+ pause() {
1957
+ if (!this.videojs.paused()) this.videojs.pause();
1958
+ }
1959
+ togglePlayback() {
1960
+ if (this.videojs.paused()) {
1961
+ this.play();
1962
+ if (this.videojs.hasStarted_) this.osd.feedback(constants$2.osd.play);
1963
+ } else {
1964
+ this.pause();
1965
+ this.osd.feedback(constants$2.osd.pause);
1966
+ }
1967
+ }
1968
+ updateSmpte(valuesObj) {
1969
+ const startCount = this.nodeNames.length - 1;
1970
+ let i;
1971
+ for (i = startCount; i > -1; i -= 1) {
1972
+ const key = this.nodeNames[i];
1973
+ if (typeof valuesObj[key] !== 'undefined' && valuesObj[key] !== parseInt(this.nodes[key].value, 10) && key !== this.state.selectedNode) {
1974
+ const str = valuesObj[key].toString();
1975
+ this.nodes[key].value = this.toDigitPairStr(str);
1976
+ }
1977
+ }
1978
+ }
1979
+ destroy() {
1980
+ this.videojs.paused(true);
1981
+ if (this.rafID) cancelAnimationFrame(this.rafID);
1982
+ this.videojs.dispose();
1983
+ this.unbindHotKeys();
1984
+ if (this.seekBar) this.seekBar.destroy();
1985
+ }
1986
+ get container() {
1987
+ return this.videoEl.parentElement;
1988
+ }
1989
+ set onDark(bool) {
1990
+ if (bool) {
1991
+ this.container.classList.add('on-dark');
1992
+ } else {
1993
+ this.container.classList.remove('on-dark');
1994
+ }
1995
+ if (this.seekBar) {
1996
+ this.seekBar.onDark = bool;
1997
+ }
1998
+ }
1999
+ get onDark() {
2000
+ return this.container.classList.contains('on-dark');
2001
+ }
2002
+ set controlsBelowPlayer(bool) {
2003
+ if (this.videojs.controls()) {
2004
+ if (bool) {
2005
+ this.container.classList.add('controls-below-player');
2006
+ this.videojs.fluid(false);
2007
+ } else {
2008
+ this.container.classList.remove('controls-below-player');
2009
+ this.videojs.fluid(true);
2010
+ }
2011
+ }
2012
+ }
2013
+ get controlsBelowPlayer() {
2014
+ return this.container.classList.contains('controls-below-player');
2015
+ }
2016
+ get smpte() {
2017
+ const currentSecond = this.videojs.currentTime();
2018
+ return this.timeConverter.secondsFromStartToSMPTE(currentSecond);
2019
+ }
2020
+ set smpte(o) {
2021
+ this.updateSmpte(o);
2022
+ }
2023
+ set src(_ref) {
2024
+ let {
2025
+ src,
2026
+ type = 'video/mp4',
2027
+ label = null
2028
+ } = _ref;
2029
+ const currentTime = this.videojs.currentTime();
2030
+ const source = this.props.sources.find(aSource => aSource.src === src);
2031
+ const onReady = () => {
2032
+ let initdone = false;
2033
+ this.videojs.on('loadedmetadata', () => {
2034
+ this.videojs.currentTime(currentTime);
2035
+ });
2036
+ this.videojs.on('canplaythrough', () => {
2037
+ if (!initdone) {
2038
+ this.videojs.currentTime(currentTime);
2039
+ initdone = true;
2040
+ this.videojs.play();
2041
+ }
2042
+ });
2043
+ };
2044
+ if (source && Array.isArray(source) && source.length > 0) {
2045
+ this.videojs.addClass('vjs-waiting');
2046
+ this.videojs.src(_objectSpread({}, source[0]));
2047
+ onReady();
2048
+ } else {
2049
+ this.videojs.addClass('vjs-waiting');
2050
+ this.videojs.src(this.addSrc(src, type, label));
2051
+ onReady();
2052
+ }
2053
+ }
2054
+ get src() {
2055
+ return this.videojs.src();
2056
+ }
2057
+ addSrc(src, type, label) {
2058
+ const newSource = {
2059
+ src,
2060
+ type,
2061
+ label
2062
+ };
2063
+ const index = this.props.sources.push(newSource) - 1;
2064
+ return this.props.sources[index];
2065
+ }
2066
+ removeSrc(url) {
2067
+ }
2068
+ set controls(bool) {
2069
+ if (bool) {
2070
+ this.showControls();
2071
+ } else {
2072
+ this.hideControls();
2073
+ }
2074
+ }
2075
+ get percentLoaded() {
2076
+ const r = this.videojs.buffered();
2077
+ const percentLoaded = r.end(0) / this.videojs.duration() * 100;
2078
+ return percentLoaded;
2079
+ }
2080
+ }
2081
+ const button = videojs.prototype.constructor.getComponent('Button');
2082
+ button.prototype.handleKeyDown = function (e) {
2083
+ if (e.keyCode === 32) {
2084
+ e.preventDefault();
2085
+ }
2086
+ };
2087
+ videojs.prototype.constructor.registerComponent('Button', button);
2088
+ const subsButton = videojs.prototype.constructor.getComponent('SubsCapsButton');
2089
+ class CustomSubsCapsButton extends subsButton {
2090
+ constructor(player) {
2091
+ let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2092
+ super(player, options);
2093
+ this.menuButton_.off('mouseenter');
2094
+ this.menuButton_.off('mouseleave');
2095
+ }
2096
+ }
2097
+ videojs.prototype.constructor.registerComponent('SubsCapsButton', CustomSubsCapsButton);
2098
+
2099
+ export { ExternalControls, Player, SeekBar, TimeCodeDisplay, VideoTime };