@oat-sa/tao-core-ui 1.5.4 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/ckeditor/ckConfigurator.js +9 -1
  2. package/dist/mediaEditor/mediaEditorComponent.js +5 -3
  3. package/dist/mediaEditor/plugins/mediaDimension/mediaDimensionComponent.js +46 -25
  4. package/dist/mediaplayer/css/player.css +104 -14
  5. package/dist/mediaplayer/css/player.css.map +1 -1
  6. package/dist/mediaplayer/players/html5.js +767 -0
  7. package/dist/mediaplayer/players/youtube.js +470 -0
  8. package/dist/mediaplayer/players.js +35 -0
  9. package/dist/mediaplayer/support.js +134 -0
  10. package/dist/mediaplayer/utils/reminder.js +198 -0
  11. package/dist/mediaplayer/utils/timeObserver.js +149 -0
  12. package/dist/mediaplayer/youtubeManager.js +177 -0
  13. package/dist/mediaplayer.js +1251 -1912
  14. package/dist/previewer.js +25 -19
  15. package/package.json +1 -1
  16. package/scss/basic.scss +1 -0
  17. package/scss/inc/_jquery.nouislider.scss +254 -0
  18. package/src/ckeditor/ckConfigurator.js +10 -1
  19. package/src/itemButtonList/css/item-button-list.css +225 -0
  20. package/src/itemButtonList/css/item-button-list.css.map +1 -0
  21. package/src/mediaEditor/mediaEditorComponent.js +25 -26
  22. package/src/mediaEditor/plugins/mediaDimension/mediaDimensionComponent.js +83 -63
  23. package/src/mediaplayer/css/player.css +104 -14
  24. package/src/mediaplayer/css/player.css.map +1 -1
  25. package/src/mediaplayer/players/html5.js +564 -0
  26. package/src/mediaplayer/players/youtube.js +323 -0
  27. package/src/mediaplayer/players.js +29 -0
  28. package/src/mediaplayer/scss/player.scss +125 -16
  29. package/src/mediaplayer/support.js +126 -0
  30. package/src/mediaplayer/tpl/audio.tpl +6 -0
  31. package/src/mediaplayer/tpl/player.tpl +11 -32
  32. package/src/mediaplayer/tpl/source.tpl +1 -0
  33. package/src/mediaplayer/tpl/video.tpl +6 -0
  34. package/src/mediaplayer/tpl/youtube.tpl +1 -0
  35. package/src/mediaplayer/utils/reminder.js +184 -0
  36. package/src/mediaplayer/utils/timeObserver.js +143 -0
  37. package/src/mediaplayer/youtubeManager.js +161 -0
  38. package/src/mediaplayer.js +1217 -1901
  39. package/src/previewer.js +40 -33
  40. package/src/searchModal/css/advancedSearch.css +190 -0
  41. package/src/searchModal/css/advancedSearch.css.map +1 -0
  42. package/src/searchModal/css/searchModal.css +506 -0
  43. package/src/searchModal/css/searchModal.css.map +1 -0
@@ -0,0 +1,767 @@
1
+ define(['jquery', 'util/urlParser', 'core/eventifier', 'ui/mediaplayer/support', 'handlebars', 'i18n', 'lodash', 'lib/dompurify/purify', 'ui/mediaplayer/utils/reminder', 'ui/mediaplayer/utils/timeObserver'], function ($$1, UrlParser, eventifier, support, Handlebars, __, _, DOMPurify, reminderManagerFactory, timeObserverFactory) { 'use strict';
2
+
3
+ $$1 = $$1 && $$1.hasOwnProperty('default') ? $$1['default'] : $$1;
4
+ UrlParser = UrlParser && UrlParser.hasOwnProperty('default') ? UrlParser['default'] : UrlParser;
5
+ eventifier = eventifier && eventifier.hasOwnProperty('default') ? eventifier['default'] : eventifier;
6
+ support = support && support.hasOwnProperty('default') ? support['default'] : support;
7
+ Handlebars = Handlebars && Handlebars.hasOwnProperty('default') ? Handlebars['default'] : Handlebars;
8
+ __ = __ && __.hasOwnProperty('default') ? __['default'] : __;
9
+ _ = _ && _.hasOwnProperty('default') ? _['default'] : _;
10
+ DOMPurify = DOMPurify && DOMPurify.hasOwnProperty('default') ? DOMPurify['default'] : DOMPurify;
11
+ reminderManagerFactory = reminderManagerFactory && reminderManagerFactory.hasOwnProperty('default') ? reminderManagerFactory['default'] : reminderManagerFactory;
12
+ timeObserverFactory = timeObserverFactory && timeObserverFactory.hasOwnProperty('default') ? timeObserverFactory['default'] : timeObserverFactory;
13
+
14
+ function _typeof(obj) {
15
+ "@babel/helpers - typeof";
16
+
17
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
18
+ _typeof = function (obj) {
19
+ return typeof obj;
20
+ };
21
+ } else {
22
+ _typeof = function (obj) {
23
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
24
+ };
25
+ }
26
+
27
+ return _typeof(obj);
28
+ }
29
+
30
+ function Helpers0 (hb) {
31
+ //register a i18n helper
32
+ hb.registerHelper('__', function (key) {
33
+ return __(key);
34
+ });
35
+ /**
36
+ * Register dompurify helper
37
+ *
38
+ * https://github.com/cure53/DOMPurify
39
+ * with config SAFE_FOR_TEMPLATES: true
40
+ * to make output safe for template systems
41
+ */
42
+
43
+ hb.registerHelper('dompurify', function (context) {
44
+ return DOMPurify.sanitize(context);
45
+ });
46
+ /**
47
+ * Register join helper
48
+ *
49
+ * Example :
50
+ * var values = {a:v1, b:v2, c:v3};
51
+ * Using {{{join attributes '=' ' ' '"'}}} will return : a="v1" b="v2" c="v3"
52
+ * Using {{{join values null ' or ' '*'}}} will return : *v1* or *v2* or *v3*
53
+ */
54
+
55
+ hb.registerHelper('join', function (arr, keyValueGlue, fragmentGlue, wrapper) {
56
+ var fragments = [];
57
+ keyValueGlue = typeof keyValueGlue === 'string' ? keyValueGlue : undefined;
58
+ fragmentGlue = typeof fragmentGlue === 'string' ? fragmentGlue : ' ';
59
+ wrapper = typeof wrapper === 'string' ? wrapper : '"';
60
+
61
+ _.forIn(arr, function (value, key) {
62
+ var fragment = '';
63
+
64
+ if (value !== null || value !== undefined) {
65
+ if (typeof value === 'boolean') {
66
+ value = value ? 'true' : 'false';
67
+ } else if (_typeof(value) === 'object') {
68
+ value = _.values(value).join(' ');
69
+ }
70
+ } else {
71
+ value = '';
72
+ }
73
+
74
+ if (keyValueGlue !== undefined) {
75
+ fragment += key + keyValueGlue;
76
+ }
77
+
78
+ fragment += wrapper + value + wrapper;
79
+ fragments.push(fragment);
80
+ });
81
+
82
+ return fragments.join(fragmentGlue);
83
+ }); //register a classic "for loop" helper
84
+ //it also adds a local variable "i" as the index in each iteration loop
85
+
86
+ hb.registerHelper('for', function (startIndex, stopIndex, increment, options) {
87
+ var ret = '';
88
+ startIndex = parseInt(startIndex);
89
+ stopIndex = parseInt(stopIndex);
90
+ increment = parseInt(increment);
91
+
92
+ for (var i = startIndex; i < stopIndex; i += increment) {
93
+ ret += options.fn(_.extend({}, this, {
94
+ i: i
95
+ }));
96
+ }
97
+
98
+ return ret;
99
+ });
100
+ hb.registerHelper('equal', function (var1, var2, options) {
101
+ if (var1 == var2) {
102
+ return options.fn(this);
103
+ } else {
104
+ return options.inverse(this);
105
+ }
106
+ }); // register a "get property" helper
107
+ // it gets the named property from the provided context
108
+
109
+ hb.registerHelper('property', function (name, context) {
110
+ return context[name] || '';
111
+ }); // register an 'includes' helper
112
+ // it checks if value is in array
113
+
114
+ hb.registerHelper('includes', function (haystack, needle, options) {
115
+ if (_.contains(haystack, needle)) {
116
+ return options.fn(this);
117
+ }
118
+ });
119
+ }
120
+
121
+ if (!Helpers0.__initialized) {
122
+ Helpers0(Handlebars);
123
+ Helpers0.__initialized = true;
124
+ }
125
+ var Template = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
126
+ this.compilerInfo = [4,'>= 1.0.0'];
127
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
128
+ var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
129
+
130
+ function program1(depth0,data) {
131
+
132
+
133
+ return "crossorigin";
134
+ }
135
+
136
+ function program3(depth0,data) {
137
+
138
+ var buffer = "", stack1, helper, options;
139
+ buffer += "\n <a href=\"";
140
+ if (helper = helpers.link) { stack1 = helper.call(depth0, {hash:{},data:data}); }
141
+ else { helper = (depth0 && depth0.link); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
142
+ buffer += escapeExpression(stack1)
143
+ + "\">"
144
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Please download the track and listen offline.", options) : helperMissing.call(depth0, "__", "Please download the track and listen offline.", options)))
145
+ + "</a>\n";
146
+ return buffer;
147
+ }
148
+
149
+ buffer += "<audio class=\"media audio\" preload=\"";
150
+ if (helper = helpers.preload) { stack1 = helper.call(depth0, {hash:{},data:data}); }
151
+ else { helper = (depth0 && depth0.preload); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
152
+ buffer += escapeExpression(stack1)
153
+ + "\" controls ";
154
+ stack1 = helpers['if'].call(depth0, (depth0 && depth0.cors), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
155
+ if(stack1 || stack1 === 0) { buffer += stack1; }
156
+ buffer += ">\n "
157
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Your browser doesn’t support the audio player.", options) : helperMissing.call(depth0, "__", "Your browser doesn’t support the audio player.", options)))
158
+ + "\n";
159
+ stack1 = helpers['if'].call(depth0, (depth0 && depth0.link), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
160
+ if(stack1 || stack1 === 0) { buffer += stack1; }
161
+ buffer += "\n</audio>\n";
162
+ return buffer;
163
+ });
164
+ function audioTpl(data, options, asString) {
165
+ var html = Template(data, options);
166
+ return (asString || true) ? html : $(html);
167
+ }
168
+
169
+ if (!Helpers0.__initialized) {
170
+ Helpers0(Handlebars);
171
+ Helpers0.__initialized = true;
172
+ }
173
+ var Template$1 = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
174
+ this.compilerInfo = [4,'>= 1.0.0'];
175
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
176
+ var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
177
+
178
+ function program1(depth0,data) {
179
+
180
+
181
+ return "crossorigin";
182
+ }
183
+
184
+ function program3(depth0,data) {
185
+
186
+ var buffer = "", stack1, helper, options;
187
+ buffer += "\n <a href=\"";
188
+ if (helper = helpers.link) { stack1 = helper.call(depth0, {hash:{},data:data}); }
189
+ else { helper = (depth0 && depth0.link); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
190
+ buffer += escapeExpression(stack1)
191
+ + "\">"
192
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Please download the video and view offline.", options) : helperMissing.call(depth0, "__", "Please download the video and view offline.", options)))
193
+ + "</a>\n";
194
+ return buffer;
195
+ }
196
+
197
+ buffer += "<video class=\"media video\" preload=\"";
198
+ if (helper = helpers.preload) { stack1 = helper.call(depth0, {hash:{},data:data}); }
199
+ else { helper = (depth0 && depth0.preload); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
200
+ buffer += escapeExpression(stack1)
201
+ + "\" poster=\"";
202
+ if (helper = helpers.poster) { stack1 = helper.call(depth0, {hash:{},data:data}); }
203
+ else { helper = (depth0 && depth0.poster); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
204
+ buffer += escapeExpression(stack1)
205
+ + "\" controls ";
206
+ stack1 = helpers['if'].call(depth0, (depth0 && depth0.cors), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
207
+ if(stack1 || stack1 === 0) { buffer += stack1; }
208
+ buffer += ">\n "
209
+ + escapeExpression((helper = helpers.__ || (depth0 && depth0.__),options={hash:{},data:data},helper ? helper.call(depth0, "Your browser doesn’t support the video player.", options) : helperMissing.call(depth0, "__", "Your browser doesn’t support the video player.", options)))
210
+ + "\n";
211
+ stack1 = helpers['if'].call(depth0, (depth0 && depth0.link), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
212
+ if(stack1 || stack1 === 0) { buffer += stack1; }
213
+ buffer += "\n</video>\n";
214
+ return buffer;
215
+ });
216
+ function videoTpl(data, options, asString) {
217
+ var html = Template$1(data, options);
218
+ return (asString || true) ? html : $(html);
219
+ }
220
+
221
+ if (!Helpers0.__initialized) {
222
+ Helpers0(Handlebars);
223
+ Helpers0.__initialized = true;
224
+ }
225
+ var Template$2 = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
226
+ this.compilerInfo = [4,'>= 1.0.0'];
227
+ helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
228
+ var buffer = "", stack1, helper, functionType="function", escapeExpression=this.escapeExpression;
229
+
230
+
231
+ buffer += "<source src=\"";
232
+ if (helper = helpers.src) { stack1 = helper.call(depth0, {hash:{},data:data}); }
233
+ else { helper = (depth0 && depth0.src); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
234
+ buffer += escapeExpression(stack1)
235
+ + "\" type=\"";
236
+ if (helper = helpers.type) { stack1 = helper.call(depth0, {hash:{},data:data}); }
237
+ else { helper = (depth0 && depth0.type); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
238
+ buffer += escapeExpression(stack1)
239
+ + "\">\n";
240
+ return buffer;
241
+ });
242
+ function sourceTpl(data, options, asString) {
243
+ var html = Template$2(data, options);
244
+ return (asString || true) ? html : $(html);
245
+ }
246
+
247
+ /**
248
+ * This program is free software; you can redistribute it and/or
249
+ * modify it under the terms of the GNU General Public License
250
+ * as published by the Free Software Foundation; under version 2
251
+ * of the License (non-upgradable).
252
+ *
253
+ * This program is distributed in the hope that it will be useful,
254
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
255
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
256
+ * GNU General Public License for more details.
257
+ *
258
+ * You should have received a copy of the GNU General Public License
259
+ * along with this program; if not, write to the Free Software
260
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
261
+ *
262
+ * Copyright (c) 2015-2021 (original work) Open Assessment Technologies SA ;
263
+ */
264
+ /**
265
+ * CSS namespace
266
+ * @type {string}
267
+ */
268
+
269
+ var ns = '.mediaplayer';
270
+ /**
271
+ * Range value of the volume
272
+ * @type {number}
273
+ */
274
+
275
+ var volumeRange = 100;
276
+ /**
277
+ * Delay before considering a media stalled
278
+ * @type {number}
279
+ */
280
+
281
+ var stalledDetectionDelay = 2000;
282
+ /**
283
+ * List of media events that can be listened to for debugging
284
+ * @type {string[]}
285
+ */
286
+
287
+ var mediaEvents = ['abort', 'canplay', 'canplaythrough', 'canshowcurrentframe', 'dataunavailable', 'durationchange', 'emptied', 'empty', 'ended', 'error', 'loadeddata', 'loadedfirstframe', 'loadedmetadata', 'loadstart', 'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting'];
288
+ /**
289
+ * List of player events that can be listened to for debugging
290
+ * @type {string[]}
291
+ */
292
+
293
+ var playerEvents = ['end', 'error', 'pause', 'play', 'playing', 'ready', 'resize', 'stalled', 'timeupdate'];
294
+ /**
295
+ * Defines a player object dedicated to the native HTML5 player
296
+ * @param {jQuery} $container - Where to render the player
297
+ * @param {object} config - The list of config entries
298
+ * @param {Array} config.sources - The list of media sources
299
+ * @param {string} [config.type] - The type of player (video or audio) (default: video)
300
+ * @param {boolean} [config.preview] - Enables the media preview (load media metadata)
301
+ * @param {boolean} [config.debug] - Enables the debug mode
302
+ * @param {number} [config.config.stalledDetectionDelay] - The delay before considering a media is stalled
303
+ * @returns {object} player
304
+ */
305
+
306
+ function html5PlayerFactory($container) {
307
+ var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
308
+ var type = config.type || 'video';
309
+ var sources = config.sources || [];
310
+ var updateObserver = reminderManagerFactory();
311
+ var timeObserver = timeObserverFactory();
312
+ config.stalledDetectionDelay = config.stalledDetectionDelay || stalledDetectionDelay;
313
+ var $media;
314
+ var media;
315
+ var state = {};
316
+
317
+ var getDebugContext = function getDebugContext(action) {
318
+ var networkState = media && media.networkState;
319
+ var readyState = media && media.readyState;
320
+ return "[html5-".concat(type, "(networkState=").concat(networkState, ",readyState=").concat(readyState, "):").concat(action, "]");
321
+ }; // eslint-disable-next-line
322
+
323
+
324
+ var debug = function debug(action) {
325
+ var _window$console;
326
+
327
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
328
+ args[_key - 1] = arguments[_key];
329
+ }
330
+
331
+ return (config.debug === true || config.debug === action) && (_window$console = window.console).log.apply(_window$console, [getDebugContext(action)].concat(args));
332
+ };
333
+
334
+ return eventifier({
335
+ init: function init() {
336
+ var _this = this;
337
+
338
+ var tpl = 'audio' === type ? audioTpl : videoTpl;
339
+ var page = new UrlParser(window.location);
340
+ var cors = false;
341
+ var preload = config.preview ? 'metadata' : 'none';
342
+ var poster = '';
343
+ var link = '';
344
+ var result = false;
345
+ state = {};
346
+ sources.forEach(function (source) {
347
+ if (!page.sameDomain(source.src)) {
348
+ cors = true;
349
+ }
350
+
351
+ if (source.poster) {
352
+ poster = source.poster;
353
+ }
354
+
355
+ if (source.link) {
356
+ link = source.link;
357
+ }
358
+ });
359
+ $media = $$1(tpl({
360
+ cors: cors,
361
+ preload: preload,
362
+ poster: poster,
363
+ link: link
364
+ }));
365
+ $container.append($media);
366
+ media = $media.get(0);
367
+ result = !!(media && support.checkSupport(media)); // Remove the browser native controls if we can use the API instead
368
+
369
+ if (support.canControl()) {
370
+ $media.removeAttr('controls');
371
+ } // Detect stalled video when the timer suddenly jump to the end
372
+
373
+
374
+ timeObserver.removeAllListeners().on('irregularity', function (position) {
375
+ if (state.playback && state.stallDetection) {
376
+ _this.stalled(position);
377
+ }
378
+ });
379
+ $media.on("play".concat(ns), function () {
380
+ state.playback = true;
381
+ state.playedViaApi = false;
382
+ timeObserver.init(media.currentTime, media.duration);
383
+
384
+ _this.trigger('play');
385
+ }).on("pause".concat(ns), function () {
386
+ if (state.stallDetection && !state.pausedViaApi && updateObserver.running && updateObserver.elapsed < 100) {
387
+ // The pause event may be triggered after a connectivity issue as the player is out of data
388
+ _this.stalled();
389
+ }
390
+
391
+ state.pausedViaApi = false;
392
+ state.playing = false;
393
+ updateObserver.stop();
394
+
395
+ _this.trigger('pause');
396
+ }).on("seeked".concat(ns), function () {
397
+ // When the user try changing the current playing position while the network is down,
398
+ // the player will end the playback by moving straight to the end.
399
+ if (state.seekedViaApi && Math.floor(state.seekAt) !== Math.floor(media.currentTime)) {
400
+ state.stallDetection = true;
401
+ }
402
+
403
+ state.seekedViaApi = false;
404
+ }).on("ended".concat(ns), function () {
405
+ updateObserver.forget().stop();
406
+ timeObserver.update(media.currentTime);
407
+ state.playback = false;
408
+ state.playing = false;
409
+
410
+ _this.trigger('end');
411
+ }).on("timeupdate".concat(ns), function () {
412
+ state.playing = true;
413
+ updateObserver.start();
414
+ timeObserver.update(media.currentTime);
415
+
416
+ _this.trigger('timeupdate');
417
+ }).on('loadstart', function () {
418
+ if (media.networkState === HTMLMediaElement.NETWORK_NO_SOURCE) {
419
+ _this.trigger('error');
420
+ }
421
+
422
+ if (!config.preview && media.networkState === HTMLMediaElement.NETWORK_IDLE) {
423
+ _this.trigger('ready');
424
+ } // The media may be unreachable straight from the beginning
425
+
426
+
427
+ _this.detectStalledNetwork();
428
+ }).on("waiting".concat(ns), function () {
429
+ // The "waiting" event means the player is pending data,
430
+ // it may be the symptom of a connectivity issue
431
+ _this.detectStalledNetwork();
432
+ }).on("error".concat(ns), function () {
433
+ if (media.networkState === HTMLMediaElement.NETWORK_NO_SOURCE || media.error instanceof MediaError && media.error.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED) {
434
+ // No source means the player does not support the supplied media.
435
+ // Or it can be more explicit with the not supported error.
436
+ // There is nothing that we can do from this stage.
437
+ _this.trigger('error');
438
+ } else {
439
+ // Other errors need special attention as they can be recoverable
440
+ _this.handleError(media.error);
441
+ }
442
+ }).on('loadedmetadata', function () {
443
+ timeObserver.init(media.currentTime, media.duration);
444
+
445
+ _this.ready();
446
+ }).on("canplay".concat(ns), function () {
447
+ if (!state.stalled) {
448
+ _this.ready();
449
+ }
450
+ }).on("stalled".concat(ns), function () {
451
+ // The "stalled" event may be triggered once the player is halted after initialisation,
452
+ // but this does not mean the playback is actually stalled, hence we only take care of the playing state
453
+ if (state.playing && !media.paused) {
454
+ _this.handleError(media.error);
455
+ }
456
+ }).on("playing".concat(ns), function () {
457
+ if (state.stallDetection) {
458
+ // The "playing" event may occur after a connectivity issue.
459
+ // For the sake of the stall detection, we need to discard this event
460
+ return;
461
+ }
462
+
463
+ updateObserver.forget().start();
464
+ state.playing = true;
465
+
466
+ _this.trigger('playing');
467
+ }); // install debug logger
468
+
469
+ if (config.debug) {
470
+ debug('installed', media);
471
+ mediaEvents.forEach(function (eventName) {
472
+ $media.on(eventName + ns, function (e) {
473
+ return debug('media event', eventName, media && media.currentSrc, e);
474
+ });
475
+ });
476
+ playerEvents.forEach(function (eventName) {
477
+ _this.on(eventName, function () {
478
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
479
+ args[_key2] = arguments[_key2];
480
+ }
481
+
482
+ return debug.apply(void 0, ['player event', eventName, media && media.currentSrc].concat(args));
483
+ });
484
+ });
485
+ }
486
+
487
+ result = result && sources.reduce(function (supported, source) {
488
+ return _this.addMedia(source.src, source.type) || supported;
489
+ }, false);
490
+ return result;
491
+ },
492
+ handleError: function handleError(error) {
493
+ var _this2 = this;
494
+
495
+ // Discard legitimate and non-blocking errors
496
+ switch (error && error.name) {
497
+ case 'NotAllowedError':
498
+ debug('api call', 'handleError', 'the autoplay is not allowed without a user interaction', error);
499
+ return;
500
+
501
+ case 'AbortError':
502
+ debug('api call', 'handleError', 'the action has been aborted for some reason', error);
503
+ return;
504
+ }
505
+
506
+ debug('api call', 'handleError', error); // Detect if the playback can continue a bit
507
+
508
+ var canContinueTemporarily = media && (media.readyState === HTMLMediaElement.HAVE_ENOUGH_DATA || media.readyState === HTMLMediaElement.HAVE_FUTURE_DATA || media.readyState === HTMLMediaElement.HAVE_CURRENT_DATA); // If a connectivity error occurs we may need to enter in stalled mode unless we can wait a bit
509
+
510
+ if (error instanceof MediaError && (error.code === MediaError.MEDIA_ERR_NETWORK || error.code === MediaError.MEDIA_ERR_DECODE) && !canContinueTemporarily) {
511
+ this.stalled();
512
+ return;
513
+ } // To this point, there is a big chance the media is stalled.
514
+ // We start an observer to remind as soon as an irregularity occurs on the time update
515
+
516
+
517
+ state.stallDetection = true;
518
+ updateObserver.remind(function () {
519
+ // The last time update is a bit old, the media is most probably stalled now
520
+ if (updateObserver.elapsed >= config.stalledDetectionDelay) {
521
+ _this2.stalled();
522
+ }
523
+ }, config.stalledDetectionDelay);
524
+ updateObserver.start();
525
+ },
526
+ ready: function ready() {
527
+ if (!state.ready) {
528
+ state.ready = true;
529
+ this.trigger('ready');
530
+ }
531
+ },
532
+ detectStalledNetwork: function detectStalledNetwork() {
533
+ var _this3 = this;
534
+
535
+ // Install an observer that will watch the network state after a small delay.
536
+ // It is needed since the network state may need time to settle.
537
+ setTimeout(function () {
538
+ if (media && media.networkState === HTMLMediaElement.NETWORK_NO_SOURCE && media.readyState === HTMLMediaElement.HAVE_NOTHING) {
539
+ if (!state.ready) {
540
+ _this3.trigger('ready');
541
+ }
542
+
543
+ _this3.stalled();
544
+ }
545
+ }, config.stalledDetectionDelay);
546
+ },
547
+ stalled: function stalled(position) {
548
+ debug('api call', 'stalled');
549
+
550
+ if (media) {
551
+ if ('undefined' !== typeof position) {
552
+ state.stalledAt = position;
553
+ } else {
554
+ state.stalledAt = timeObserver.position;
555
+ }
556
+ }
557
+
558
+ state.stalled = true;
559
+ state.stallDetection = false;
560
+ updateObserver.forget().stop();
561
+ this.pause();
562
+ this.trigger('stalled');
563
+ },
564
+ recover: function recover() {
565
+ debug('api call', 'recover');
566
+ state.stalled = false;
567
+ state.stallDetection = false;
568
+
569
+ if (media) {
570
+ // Special processing of video player to prevent visual glitch while reloading
571
+ if (media.tagName === 'VIDEO') {
572
+ // Temporarily set the size of the media to prevent a shrink while reloading it
573
+ $media.width($media.width());
574
+ $media.height($media.height());
575
+ $media.on('loadedmetadata.recover', function () {
576
+ $media.off('loadedmetadata.recover');
577
+ $media.css({
578
+ width: '',
579
+ height: ''
580
+ });
581
+ });
582
+ }
583
+
584
+ media.load();
585
+
586
+ if (state.stalledAt) {
587
+ this.seek(state.stalledAt);
588
+ }
589
+
590
+ if (state.playback && !state.playing || state.playedViaApi) {
591
+ this.play();
592
+ }
593
+ }
594
+ },
595
+ destroy: function destroy() {
596
+ debug('api call', 'destroy');
597
+ this.stop();
598
+ this.removeAllListeners();
599
+ updateObserver.forget();
600
+ timeObserver.removeAllListeners();
601
+
602
+ if ($media) {
603
+ $media.off(ns).remove();
604
+ }
605
+
606
+ $media = void 0;
607
+ media = void 0;
608
+ state = {};
609
+ },
610
+ getMedia: function getMedia() {
611
+ debug('api call', 'getMedia', media);
612
+ return media;
613
+ },
614
+ getMediaSize: function getMediaSize() {
615
+ var size = {};
616
+
617
+ if (media) {
618
+ size = {
619
+ width: media.videoWidth,
620
+ height: media.videoHeight
621
+ };
622
+ }
623
+
624
+ debug('api call', 'getMediaSize', size);
625
+ return size;
626
+ },
627
+ getPosition: function getPosition() {
628
+ var position = 0;
629
+
630
+ if (media) {
631
+ position = media.currentTime;
632
+ }
633
+
634
+ debug('api call', 'getPosition', position);
635
+ return position;
636
+ },
637
+ getDuration: function getDuration() {
638
+ var duration = 0;
639
+
640
+ if (media) {
641
+ duration = media.duration;
642
+ }
643
+
644
+ debug('api call', 'getDuration', duration);
645
+ return duration;
646
+ },
647
+ getVolume: function getVolume() {
648
+ var volume = 0;
649
+
650
+ if (media) {
651
+ volume = parseFloat(media.volume) * volumeRange;
652
+ }
653
+
654
+ debug('api call', 'getVolume', volume);
655
+ return volume;
656
+ },
657
+ setVolume: function setVolume(volume) {
658
+ debug('api call', 'setVolume', volume);
659
+
660
+ if (media) {
661
+ media.volume = parseFloat(volume) / volumeRange;
662
+ }
663
+ },
664
+ setSize: function setSize(width, height) {
665
+ debug('api call', 'setSize', width, height);
666
+ this.trigger('resize', width, height);
667
+ },
668
+ seek: function seek(time) {
669
+ debug('api call', 'seek', time);
670
+
671
+ if (media) {
672
+ media.currentTime = parseFloat(time);
673
+ state.seekedViaApi = true;
674
+ state.seekAt = media.currentTime;
675
+ timeObserver.seek(media.currentTime);
676
+
677
+ if (!state.playback) {
678
+ this.play();
679
+ }
680
+ }
681
+ },
682
+ play: function play() {
683
+ var _this4 = this;
684
+
685
+ debug('api call', 'play');
686
+
687
+ if (media) {
688
+ state.playedViaApi = true;
689
+ var startPlayPromise = media.play();
690
+
691
+ if ('undefined' !== typeof startPlayPromise) {
692
+ startPlayPromise.catch(function (error) {
693
+ return _this4.handleError(error);
694
+ });
695
+ }
696
+ }
697
+ },
698
+ pause: function pause() {
699
+ debug('api call', 'pause');
700
+
701
+ if (media) {
702
+ if (!media.paused) {
703
+ state.pausedViaApi = true;
704
+ }
705
+
706
+ media.pause();
707
+ }
708
+ },
709
+ stop: function stop() {
710
+ debug('api call', 'stop');
711
+
712
+ if (media && media.duration && state.playback && !state.stalled) {
713
+ media.currentTime = media.duration;
714
+ }
715
+ },
716
+ mute: function mute(muted) {
717
+ debug('api call', 'mute', muted);
718
+
719
+ if (media) {
720
+ media.muted = !!muted;
721
+ }
722
+ },
723
+ isMuted: function isMuted() {
724
+ var mute = false;
725
+
726
+ if (media) {
727
+ mute = !!media.muted;
728
+ }
729
+
730
+ debug('api call', 'isMuted', mute);
731
+ return mute;
732
+ },
733
+ addMedia: function addMedia(src, srcType) {
734
+ debug('api call', 'addMedia', src, srcType);
735
+
736
+ if (media) {
737
+ if (!support.checkSupport(media, srcType)) {
738
+ return false;
739
+ }
740
+ }
741
+
742
+ if (src && $media) {
743
+ $media.append(sourceTpl({
744
+ src: src,
745
+ type: srcType
746
+ }));
747
+ return true;
748
+ }
749
+
750
+ return false;
751
+ },
752
+ setMedia: function setMedia(src, srcType) {
753
+ debug('api call', 'setMedia', src, srcType);
754
+
755
+ if ($media) {
756
+ $media.empty();
757
+ return this.addMedia(src, srcType);
758
+ }
759
+
760
+ return false;
761
+ }
762
+ });
763
+ }
764
+
765
+ return html5PlayerFactory;
766
+
767
+ });