proctoring 2.0.2

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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +245 -0
  4. data/Rakefile +32 -0
  5. data/app/assets/config/100ms_manifest.js +1 -0
  6. data/app/assets/config/knights_watch_manifest.js +4 -0
  7. data/app/assets/config/kurento_manifest.js +1 -0
  8. data/app/assets/config/proctoring_manifest.js +3 -0
  9. data/app/assets/config/videojs_manifest.js +2 -0
  10. data/app/assets/images/proctoring/poster.png +0 -0
  11. data/app/assets/javascripts/100ms/examine.js +27 -0
  12. data/app/assets/javascripts/100ms/hundred_ms.js +143 -0
  13. data/app/assets/javascripts/100ms/join_proctor_room.js +17 -0
  14. data/app/assets/javascripts/100ms/proctor.js +152 -0
  15. data/app/assets/javascripts/kurento/LiveVideoUsingSignalingServer.js +344 -0
  16. data/app/assets/javascripts/kurento/VideoPlayer.js +63 -0
  17. data/app/assets/javascripts/kurento/VideoRecording.js +286 -0
  18. data/app/assets/javascripts/kurento/VideoRecordingUsingSignalingServer.js +224 -0
  19. data/app/assets/javascripts/kurento/co.js +299 -0
  20. data/app/assets/javascripts/kurento/kurento-utils.js +4418 -0
  21. data/app/assets/javascripts/proctoring/stream_channel.js +66 -0
  22. data/app/assets/javascripts/proctoring/stream_room.js +131 -0
  23. data/app/assets/javascripts/proctoring/video_recorder.js +172 -0
  24. data/app/assets/javascripts/videojs/videojs-playlist-ui.js +516 -0
  25. data/app/assets/javascripts/videojs/videojs-playlist.js +909 -0
  26. data/app/assets/stylesheets/proctoring/application.css +15 -0
  27. data/app/assets/stylesheets/proctoring/video_player_100ms.css +34 -0
  28. data/app/assets/stylesheets/proctoring/video_streamings.css +49 -0
  29. data/app/assets/stylesheets/scaffold.css +80 -0
  30. data/app/assets/stylesheets/videojs/videojs-playlist-ui.css +1 -0
  31. data/app/controllers/proctoring/api/v1/authentication_controller.rb +31 -0
  32. data/app/controllers/proctoring/api/v1/hundred_ms/services_controller.rb +24 -0
  33. data/app/controllers/proctoring/application_controller.rb +23 -0
  34. data/app/controllers/proctoring/video_streamings_controller.rb +108 -0
  35. data/app/helpers/proctoring/application_helper.rb +4 -0
  36. data/app/helpers/proctoring/hundred_ms_service_helper.rb +90 -0
  37. data/app/helpers/proctoring/tokens_helper.rb +55 -0
  38. data/app/helpers/proctoring/video_streamings_helper.rb +4 -0
  39. data/app/jobs/proctoring/application_job.rb +4 -0
  40. data/app/mailers/proctoring/application_mailer.rb +6 -0
  41. data/app/models/proctoring/application_record.rb +5 -0
  42. data/app/models/proctoring/video_streaming.rb +54 -0
  43. data/app/models/proctoring/video_streaming_room.rb +29 -0
  44. data/app/views/layouts/proctoring/application.html.erb +15 -0
  45. data/app/views/proctoring/video_player/live_video_proctoring.html.erb +84 -0
  46. data/app/views/proctoring/video_player/live_video_proctoring_100ms.html.erb +48 -0
  47. data/app/views/proctoring/video_player/video_player.html.erb +40 -0
  48. data/app/views/proctoring/video_streamings/_form.html.erb +27 -0
  49. data/app/views/proctoring/video_streamings/_list.html.erb +27 -0
  50. data/app/views/proctoring/video_streamings/_record_video_from_client.html.erb +8 -0
  51. data/app/views/proctoring/video_streamings/_socket_rtc_scripts.html.erb +5 -0
  52. data/app/views/proctoring/video_streamings/_stream_video.html.erb +8 -0
  53. data/app/views/proctoring/video_streamings/_video_recording.html.erb +3 -0
  54. data/app/views/proctoring/video_streamings/_video_recording100ms.html.erb +4 -0
  55. data/app/views/proctoring/video_streamings/distribute_channel_to_rooms.html.erb +39 -0
  56. data/app/views/proctoring/video_streamings/edit.html.erb +6 -0
  57. data/app/views/proctoring/video_streamings/event.html.erb +9 -0
  58. data/app/views/proctoring/video_streamings/index.html.erb +1 -0
  59. data/app/views/proctoring/video_streamings/new.html.erb +5 -0
  60. data/app/views/proctoring/video_streamings/show.html.erb +19 -0
  61. data/app/views/proctoring/video_streamings/stream_channel.html.erb +1 -0
  62. data/app/views/proctoring/video_streamings/stream_room.html.erb +1 -0
  63. data/config/routes.rb +24 -0
  64. data/db/migrate/20200526061313_create_proctoring_video_streamings.rb +15 -0
  65. data/db/migrate/20200527045158_create_proctoring_video_streaming_rooms.rb +13 -0
  66. data/lib/proctoring/engine.rb +41 -0
  67. data/lib/proctoring/version.rb +3 -0
  68. data/lib/proctoring.rb +5 -0
  69. data/lib/tasks/proctoring_tasks.rake +4 -0
  70. metadata +158 -0
@@ -0,0 +1,909 @@
1
+ /*! @name videojs-playlist @version 4.3.1 @license Apache-2.0 */
2
+ (function (global, factory) {
3
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
4
+ typeof define === 'function' && define.amd ? define(['video.js'], factory) :
5
+ (global = global || self, global.videojsPlaylist = factory(global.videojs));
6
+ }(this, function (videojs) { 'use strict';
7
+
8
+ videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
9
+
10
+ /**
11
+ * Validates a number of seconds to use as the auto-advance delay.
12
+ *
13
+ * @private
14
+ * @param {number} s
15
+ * The number to check
16
+ *
17
+ * @return {boolean}
18
+ * Whether this is a valid second or not
19
+ */
20
+ var validSeconds = function validSeconds(s) {
21
+ return typeof s === 'number' && !isNaN(s) && s >= 0 && s < Infinity;
22
+ };
23
+ /**
24
+ * Resets the auto-advance behavior of a player.
25
+ *
26
+ * @param {Player} player
27
+ * The player to reset the behavior on
28
+ */
29
+
30
+
31
+ var reset = function reset(player) {
32
+ var aa = player.playlist.autoadvance_;
33
+
34
+ if (aa.timeout) {
35
+ player.clearTimeout(aa.timeout);
36
+ }
37
+
38
+ if (aa.trigger) {
39
+ player.off('ended', aa.trigger);
40
+ }
41
+
42
+ aa.timeout = null;
43
+ aa.trigger = null;
44
+ };
45
+ /**
46
+ * Sets up auto-advance behavior on a player.
47
+ *
48
+ * @param {Player} player
49
+ * the current player
50
+ *
51
+ * @param {number} delay
52
+ * The number of seconds to wait before each auto-advance.
53
+ *
54
+ * @return {undefined}
55
+ * Used to short circuit function logic
56
+ */
57
+
58
+
59
+ var setup = function setup(player, delay) {
60
+ reset(player); // Before queuing up new auto-advance behavior, check if `seconds` was
61
+ // called with a valid value.
62
+
63
+ if (!validSeconds(delay)) {
64
+ player.playlist.autoadvance_.delay = null;
65
+ return;
66
+ }
67
+
68
+ player.playlist.autoadvance_.delay = delay;
69
+
70
+ player.playlist.autoadvance_.trigger = function () {
71
+ // This calls setup again, which will reset the existing auto-advance and
72
+ // set up another auto-advance for the next "ended" event.
73
+ var cancelOnPlay = function cancelOnPlay() {
74
+ return setup(player, delay);
75
+ }; // If there is a "play" event while we're waiting for an auto-advance,
76
+ // we need to cancel the auto-advance. This could mean the user seeked
77
+ // back into the content or restarted the content. This is reproducible
78
+ // with an auto-advance > 0.
79
+
80
+
81
+ player.one('play', cancelOnPlay);
82
+ player.playlist.autoadvance_.timeout = player.setTimeout(function () {
83
+ reset(player);
84
+ player.off('play', cancelOnPlay);
85
+ player.playlist.next();
86
+ }, delay * 1000);
87
+ };
88
+
89
+ player.one('ended', player.playlist.autoadvance_.trigger);
90
+ };
91
+
92
+ /**
93
+ * Removes all remote text tracks from a player.
94
+ *
95
+ * @param {Player} player
96
+ * The player to clear tracks on
97
+ */
98
+
99
+ var clearTracks = function clearTracks(player) {
100
+ var tracks = player.remoteTextTracks();
101
+ var i = tracks && tracks.length || 0; // This uses a `while` loop rather than `forEach` because the
102
+ // `TextTrackList` object is a live DOM list (not an array).
103
+
104
+ while (i--) {
105
+ player.removeRemoteTextTrack(tracks[i]);
106
+ }
107
+ };
108
+ /**
109
+ * Plays an item on a player's playlist.
110
+ *
111
+ * @param {Player} player
112
+ * The player to play the item on
113
+ *
114
+ * @param {Object} item
115
+ * A source from the playlist.
116
+ *
117
+ * @return {Player}
118
+ * The player that is now playing the item
119
+ */
120
+
121
+
122
+ var playItem = function playItem(player, item) {
123
+ var replay = !player.paused() || player.ended();
124
+ player.trigger('beforeplaylistitem', item.originalValue || item);
125
+
126
+ if (item.playlistItemId_) {
127
+ player.playlist.currentPlaylistItemId_ = item.playlistItemId_;
128
+ }
129
+
130
+ player.poster(item.poster || '');
131
+ player.src(item.sources);
132
+ clearTracks(player);
133
+ player.ready(function () {
134
+ (item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player));
135
+ player.trigger('playlistitem', item.originalValue || item);
136
+
137
+ if (replay) {
138
+ var playPromise = player.play(); // silence error when a pause interrupts a play request
139
+ // on browsers which return a promise
140
+
141
+ if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
142
+ playPromise.then(null, function (e) {});
143
+ }
144
+ }
145
+
146
+ setup(player, player.playlist.autoadvance_.delay);
147
+ });
148
+ return player;
149
+ };
150
+
151
+ /**
152
+ * Returns whether a playlist item is an object of any kind, excluding null.
153
+ *
154
+ * @private
155
+ *
156
+ * @param {Object}
157
+ * value to be checked
158
+ *
159
+ * @return {boolean}
160
+ * The result
161
+ */
162
+
163
+ var isItemObject = function isItemObject(value) {
164
+ return !!value && typeof value === 'object';
165
+ };
166
+ /**
167
+ * Look through an array of playlist items and transform any primitive
168
+ * as well as null values to objects. This method also adds a property
169
+ * to the transformed item containing original value passed in an input list.
170
+ *
171
+ * @private
172
+ *
173
+ * @param {Array} arr
174
+ * An array of playlist items
175
+ *
176
+ * @return {Array}
177
+ * A new array with transformed items
178
+ */
179
+
180
+
181
+ var transformPrimitiveItems = function transformPrimitiveItems(arr) {
182
+ var list = [];
183
+ var tempItem;
184
+ arr.forEach(function (item) {
185
+ if (!isItemObject(item)) {
186
+ tempItem = Object(item);
187
+ tempItem.originalValue = item;
188
+ } else {
189
+ tempItem = item;
190
+ }
191
+
192
+ list.push(tempItem);
193
+ });
194
+ return list;
195
+ };
196
+ /**
197
+ * Generate a unique id for each playlist item object. This id will be used to determine
198
+ * index of an item in the playlist array for cases where there are multiple items with
199
+ * the same source set.
200
+ *
201
+ * @private
202
+ *
203
+ * @param {Array} arr
204
+ * An array of playlist items
205
+ */
206
+
207
+
208
+ var generatePlaylistItemId = function generatePlaylistItemId(arr) {
209
+ var guid = 1;
210
+ arr.forEach(function (item) {
211
+ item.playlistItemId_ = guid++;
212
+ });
213
+ };
214
+ /**
215
+ * Look through an array of playlist items for a specific playlist item id.
216
+ *
217
+ * @private
218
+ * @param {Array} list
219
+ * An array of playlist items to look through
220
+ *
221
+ * @param {number} currentItemId
222
+ * The current item ID.
223
+ *
224
+ * @return {number}
225
+ * The index of the playlist item or -1 if not found
226
+ */
227
+
228
+
229
+ var indexInPlaylistItemIds = function indexInPlaylistItemIds(list, currentItemId) {
230
+ for (var i = 0; i < list.length; i++) {
231
+ if (list[i].playlistItemId_ === currentItemId) {
232
+ return i;
233
+ }
234
+ }
235
+
236
+ return -1;
237
+ };
238
+ /**
239
+ * Given two sources, check to see whether the two sources are equal.
240
+ * If both source urls have a protocol, the protocols must match, otherwise, protocols
241
+ * are ignored.
242
+ *
243
+ * @private
244
+ * @param {string|Object} source1
245
+ * The first source
246
+ *
247
+ * @param {string|Object} source2
248
+ * The second source
249
+ *
250
+ * @return {boolean}
251
+ * The result
252
+ */
253
+
254
+
255
+ var sourceEquals = function sourceEquals(source1, source2) {
256
+ var src1 = source1;
257
+ var src2 = source2;
258
+
259
+ if (typeof source1 === 'object') {
260
+ src1 = source1.src;
261
+ }
262
+
263
+ if (typeof source2 === 'object') {
264
+ src2 = source2.src;
265
+ }
266
+
267
+ if (/^\/\//.test(src1)) {
268
+ src2 = src2.slice(src2.indexOf('//'));
269
+ }
270
+
271
+ if (/^\/\//.test(src2)) {
272
+ src1 = src1.slice(src1.indexOf('//'));
273
+ }
274
+
275
+ return src1 === src2;
276
+ };
277
+ /**
278
+ * Look through an array of playlist items for a specific `source`;
279
+ * checking both the value of elements and the value of their `src`
280
+ * property.
281
+ *
282
+ * @private
283
+ * @param {Array} arr
284
+ * An array of playlist items to look through
285
+ *
286
+ * @param {string} src
287
+ * The source to look for
288
+ *
289
+ * @return {number}
290
+ * The index of that source or -1
291
+ */
292
+
293
+
294
+ var indexInSources = function indexInSources(arr, src) {
295
+ for (var i = 0; i < arr.length; i++) {
296
+ var sources = arr[i].sources;
297
+
298
+ if (Array.isArray(sources)) {
299
+ for (var j = 0; j < sources.length; j++) {
300
+ var source = sources[j];
301
+
302
+ if (source && sourceEquals(source, src)) {
303
+ return i;
304
+ }
305
+ }
306
+ }
307
+ }
308
+
309
+ return -1;
310
+ };
311
+ /**
312
+ * Randomize the contents of an array.
313
+ *
314
+ * @private
315
+ * @param {Array} arr
316
+ * An array.
317
+ *
318
+ * @return {Array}
319
+ * The same array that was passed in.
320
+ */
321
+
322
+
323
+ var randomize = function randomize(arr) {
324
+ var index = -1;
325
+ var lastIndex = arr.length - 1;
326
+
327
+ while (++index < arr.length) {
328
+ var rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
329
+ var value = arr[rand];
330
+ arr[rand] = arr[index];
331
+ arr[index] = value;
332
+ }
333
+
334
+ return arr;
335
+ };
336
+ /**
337
+ * Factory function for creating new playlist implementation on the given player.
338
+ *
339
+ * API summary:
340
+ *
341
+ * playlist(['a', 'b', 'c']) // setter
342
+ * playlist() // getter
343
+ * playlist.currentItem() // getter, 0
344
+ * playlist.currentItem(1) // setter, 1
345
+ * playlist.next() // 'c'
346
+ * playlist.previous() // 'b'
347
+ * playlist.first() // 'a'
348
+ * playlist.last() // 'c'
349
+ * playlist.autoadvance(5) // 5 second delay
350
+ * playlist.autoadvance() // cancel autoadvance
351
+ *
352
+ * @param {Player} player
353
+ * The current player
354
+ *
355
+ * @param {Array=} initialList
356
+ * If given, an initial list of sources with which to populate
357
+ * the playlist.
358
+ *
359
+ * @param {number=} initialIndex
360
+ * If given, the index of the item in the list that should
361
+ * be loaded first. If -1, no video is loaded. If omitted, The
362
+ * the first video is loaded.
363
+ *
364
+ * @return {Function}
365
+ * Returns the playlist function specific to the given player.
366
+ */
367
+
368
+
369
+ function factory(player, initialList, initialIndex) {
370
+ if (initialIndex === void 0) {
371
+ initialIndex = 0;
372
+ }
373
+
374
+ var list = null;
375
+ var changing = false;
376
+ /**
377
+ * Get/set the playlist for a player.
378
+ *
379
+ * This function is added as an own property of the player and has its
380
+ * own methods which can be called to manipulate the internal state.
381
+ *
382
+ * @param {Array} [newList]
383
+ * If given, a new list of sources with which to populate the
384
+ * playlist. Without this, the function acts as a getter.
385
+ *
386
+ * @param {number} [newIndex]
387
+ * If given, the index of the item in the list that should
388
+ * be loaded first. If -1, no video is loaded. If omitted, The
389
+ * the first video is loaded.
390
+ *
391
+ * @return {Array}
392
+ * The playlist
393
+ */
394
+
395
+ var playlist = player.playlist = function (newList, newIndex) {
396
+ if (newIndex === void 0) {
397
+ newIndex = 0;
398
+ }
399
+
400
+ if (changing) {
401
+ throw new Error('do not call playlist() during a playlist change');
402
+ }
403
+
404
+ if (Array.isArray(newList)) {
405
+ // @todo - Simplify this to `list.slice()` for v5.
406
+ var previousPlaylist = Array.isArray(list) ? list.slice() : null;
407
+ var nextPlaylist = newList.slice();
408
+ list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects
409
+
410
+ if (list.filter(function (item) {
411
+ return isItemObject(item);
412
+ }).length !== list.length) {
413
+ list = transformPrimitiveItems(list);
414
+ } // Add unique id to each playlist item. This id will be used
415
+ // to determine index in cases where there are more than one
416
+ // identical sources in the playlist.
417
+
418
+
419
+ generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle.
420
+
421
+ changing = true;
422
+ player.trigger({
423
+ type: 'duringplaylistchange',
424
+ nextIndex: newIndex,
425
+ nextPlaylist: nextPlaylist,
426
+ previousIndex: playlist.currentIndex_,
427
+ // @todo - Simplify this to simply pass along `previousPlaylist` for v5.
428
+ previousPlaylist: previousPlaylist || []
429
+ });
430
+ changing = false;
431
+
432
+ if (newIndex !== -1) {
433
+ playlist.currentItem(newIndex);
434
+ } // The only time the previous playlist is null is the first call to this
435
+ // function. This allows us to fire the `duringplaylistchange` event
436
+ // every time the playlist is populated and to maintain backward
437
+ // compatibility by not firing the `playlistchange` event on the initial
438
+ // population of the list.
439
+ //
440
+ // @todo - Remove this condition in preparation for v5.
441
+
442
+
443
+ if (previousPlaylist) {
444
+ player.setTimeout(function () {
445
+ player.trigger('playlistchange');
446
+ }, 0);
447
+ }
448
+ } // Always return a shallow clone of the playlist list.
449
+ // We also want to return originalValue if any item in the list has it.
450
+
451
+
452
+ return list.map(function (item) {
453
+ return item.originalValue || item;
454
+ }).slice();
455
+ }; // On a new source, if there is no current item, disable auto-advance.
456
+
457
+
458
+ player.on('loadstart', function () {
459
+ if (playlist.currentItem() === -1) {
460
+ reset(player);
461
+ }
462
+ });
463
+ playlist.currentIndex_ = -1;
464
+ playlist.player_ = player;
465
+ playlist.autoadvance_ = {};
466
+ playlist.repeat_ = false;
467
+ playlist.currentPlaylistItemId_ = null;
468
+ /**
469
+ * Get or set the current item in the playlist.
470
+ *
471
+ * During the duringplaylistchange event, acts only as a getter.
472
+ *
473
+ * @param {number} [index]
474
+ * If given as a valid value, plays the playlist item at that index.
475
+ *
476
+ * @return {number}
477
+ * The current item index.
478
+ */
479
+
480
+ playlist.currentItem = function (index) {
481
+ // If the playlist is changing, only act as a getter.
482
+ if (changing) {
483
+ return playlist.currentIndex_;
484
+ } // Act as a setter when the index is given and is a valid number.
485
+
486
+
487
+ if (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) {
488
+ playlist.currentIndex_ = index;
489
+ playItem(playlist.player_, list[playlist.currentIndex_]);
490
+ return playlist.currentIndex_;
491
+ }
492
+
493
+ var src = playlist.player_.currentSrc() || ''; // If there is a currentPlaylistItemId_, validate that it matches the
494
+ // current source URL returned by the player. This is sufficient evidence
495
+ // to suggest that the source was set by the playlist plugin. This code
496
+ // exists primarily to deal with playlists where multiple items have the
497
+ // same source.
498
+
499
+ if (playlist.currentPlaylistItemId_) {
500
+ var indexInItemIds = indexInPlaylistItemIds(list, playlist.currentPlaylistItemId_);
501
+ var item = list[indexInItemIds]; // Found a match, this is our current index!
502
+
503
+ if (item && Array.isArray(item.sources) && indexInSources([item], src) > -1) {
504
+ playlist.currentIndex_ = indexInItemIds;
505
+ return playlist.currentIndex_;
506
+ } // If this does not match the current source, null it out so subsequent
507
+ // calls can skip this step.
508
+
509
+
510
+ playlist.currentPlaylistItemId_ = null;
511
+ } // Finally, if we don't have a valid, current playlist item ID, we can
512
+ // auto-detect it based on the player's current source URL.
513
+
514
+
515
+ playlist.currentIndex_ = playlist.indexOf(src);
516
+ return playlist.currentIndex_;
517
+ };
518
+ /**
519
+ * Checks if the playlist contains a value.
520
+ *
521
+ * @param {string|Object|Array} value
522
+ * The value to check
523
+ *
524
+ * @return {boolean}
525
+ * The result
526
+ */
527
+
528
+
529
+ playlist.contains = function (value) {
530
+ return playlist.indexOf(value) !== -1;
531
+ };
532
+ /**
533
+ * Gets the index of a value in the playlist or -1 if not found.
534
+ *
535
+ * @param {string|Object|Array} value
536
+ * The value to find the index of
537
+ *
538
+ * @return {number}
539
+ * The index or -1
540
+ */
541
+
542
+
543
+ playlist.indexOf = function (value) {
544
+ if (typeof value === 'string') {
545
+ return indexInSources(list, value);
546
+ }
547
+
548
+ var sources = Array.isArray(value) ? value : value.sources;
549
+
550
+ for (var i = 0; i < sources.length; i++) {
551
+ var source = sources[i];
552
+
553
+ if (typeof source === 'string') {
554
+ return indexInSources(list, source);
555
+ } else if (source.src) {
556
+ return indexInSources(list, source.src);
557
+ }
558
+ }
559
+
560
+ return -1;
561
+ };
562
+ /**
563
+ * Get the index of the current item in the playlist. This is identical to
564
+ * calling `currentItem()` with no arguments.
565
+ *
566
+ * @return {number}
567
+ * The current item index.
568
+ */
569
+
570
+
571
+ playlist.currentIndex = function () {
572
+ return playlist.currentItem();
573
+ };
574
+ /**
575
+ * Get the index of the last item in the playlist.
576
+ *
577
+ * @return {number}
578
+ * The index of the last item in the playlist or -1 if there are no
579
+ * items.
580
+ */
581
+
582
+
583
+ playlist.lastIndex = function () {
584
+ return list.length - 1;
585
+ };
586
+ /**
587
+ * Get the index of the next item in the playlist.
588
+ *
589
+ * @return {number}
590
+ * The index of the next item in the playlist or -1 if there is no
591
+ * current item.
592
+ */
593
+
594
+
595
+ playlist.nextIndex = function () {
596
+ var current = playlist.currentItem();
597
+
598
+ if (current === -1) {
599
+ return -1;
600
+ }
601
+
602
+ var lastIndex = playlist.lastIndex(); // When repeating, loop back to the beginning on the last item.
603
+
604
+ if (playlist.repeat_ && current === lastIndex) {
605
+ return 0;
606
+ } // Don't go past the end of the playlist.
607
+
608
+
609
+ return Math.min(current + 1, lastIndex);
610
+ };
611
+ /**
612
+ * Get the index of the previous item in the playlist.
613
+ *
614
+ * @return {number}
615
+ * The index of the previous item in the playlist or -1 if there is
616
+ * no current item.
617
+ */
618
+
619
+
620
+ playlist.previousIndex = function () {
621
+ var current = playlist.currentItem();
622
+
623
+ if (current === -1) {
624
+ return -1;
625
+ } // When repeating, loop back to the end of the playlist.
626
+
627
+
628
+ if (playlist.repeat_ && current === 0) {
629
+ return playlist.lastIndex();
630
+ } // Don't go past the beginning of the playlist.
631
+
632
+
633
+ return Math.max(current - 1, 0);
634
+ };
635
+ /**
636
+ * Plays the first item in the playlist.
637
+ *
638
+ * @return {Object|undefined}
639
+ * Returns undefined and has no side effects if the list is empty.
640
+ */
641
+
642
+
643
+ playlist.first = function () {
644
+ if (changing) {
645
+ return;
646
+ }
647
+
648
+ var newItem = playlist.currentItem(0);
649
+
650
+ if (list.length) {
651
+ return list[newItem].originalValue || list[newItem];
652
+ }
653
+
654
+ playlist.currentIndex_ = -1;
655
+ };
656
+ /**
657
+ * Plays the last item in the playlist.
658
+ *
659
+ * @return {Object|undefined}
660
+ * Returns undefined and has no side effects if the list is empty.
661
+ */
662
+
663
+
664
+ playlist.last = function () {
665
+ if (changing) {
666
+ return;
667
+ }
668
+
669
+ var newItem = playlist.currentItem(playlist.lastIndex());
670
+
671
+ if (list.length) {
672
+ return list[newItem].originalValue || list[newItem];
673
+ }
674
+
675
+ playlist.currentIndex_ = -1;
676
+ };
677
+ /**
678
+ * Plays the next item in the playlist.
679
+ *
680
+ * @return {Object|undefined}
681
+ * Returns undefined and has no side effects if on last item.
682
+ */
683
+
684
+
685
+ playlist.next = function () {
686
+ if (changing) {
687
+ return;
688
+ }
689
+
690
+ var index = playlist.nextIndex();
691
+
692
+ if (index !== playlist.currentIndex_) {
693
+ var newItem = playlist.currentItem(index);
694
+ return list[newItem].originalValue || list[newItem];
695
+ }
696
+ };
697
+ /**
698
+ * Plays the previous item in the playlist.
699
+ *
700
+ * @return {Object|undefined}
701
+ * Returns undefined and has no side effects if on first item.
702
+ */
703
+
704
+
705
+ playlist.previous = function () {
706
+ if (changing) {
707
+ return;
708
+ }
709
+
710
+ var index = playlist.previousIndex();
711
+
712
+ if (index !== playlist.currentIndex_) {
713
+ var newItem = playlist.currentItem(index);
714
+ return list[newItem].originalValue || list[newItem];
715
+ }
716
+ };
717
+ /**
718
+ * Set up auto-advance on the playlist.
719
+ *
720
+ * @param {number} [delay]
721
+ * The number of seconds to wait before each auto-advance.
722
+ */
723
+
724
+
725
+ playlist.autoadvance = function (delay) {
726
+ setup(playlist.player_, delay);
727
+ };
728
+ /**
729
+ * Sets `repeat` option, which makes the "next" video of the last video in
730
+ * the playlist be the first video in the playlist.
731
+ *
732
+ * @param {boolean} [val]
733
+ * The value to set repeat to
734
+ *
735
+ * @return {boolean}
736
+ * The current value of repeat
737
+ */
738
+
739
+
740
+ playlist.repeat = function (val) {
741
+ if (val === undefined) {
742
+ return playlist.repeat_;
743
+ }
744
+
745
+ if (typeof val !== 'boolean') {
746
+ videojs.log.error('videojs-playlist: Invalid value for repeat', val);
747
+ return;
748
+ }
749
+
750
+ playlist.repeat_ = !!val;
751
+ return playlist.repeat_;
752
+ };
753
+ /**
754
+ * Sorts the playlist array.
755
+ *
756
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort}
757
+ * @fires playlistsorted
758
+ *
759
+ * @param {Function} compare
760
+ * A comparator function as per the native Array method.
761
+ */
762
+
763
+
764
+ playlist.sort = function (compare) {
765
+ // Bail if the array is empty.
766
+ if (!list.length) {
767
+ return;
768
+ }
769
+
770
+ list.sort(compare); // If the playlist is changing, don't trigger events.
771
+
772
+ if (changing) {
773
+ return;
774
+ }
775
+ /**
776
+ * Triggered after the playlist is sorted internally.
777
+ *
778
+ * @event playlistsorted
779
+ * @type {Object}
780
+ */
781
+
782
+
783
+ player.trigger('playlistsorted');
784
+ };
785
+ /**
786
+ * Reverses the playlist array.
787
+ *
788
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse}
789
+ * @fires playlistsorted
790
+ */
791
+
792
+
793
+ playlist.reverse = function () {
794
+ // Bail if the array is empty.
795
+ if (!list.length) {
796
+ return;
797
+ }
798
+
799
+ list.reverse(); // If the playlist is changing, don't trigger events.
800
+
801
+ if (changing) {
802
+ return;
803
+ }
804
+ /**
805
+ * Triggered after the playlist is sorted internally.
806
+ *
807
+ * @event playlistsorted
808
+ * @type {Object}
809
+ */
810
+
811
+
812
+ player.trigger('playlistsorted');
813
+ };
814
+ /**
815
+ * Shuffle the contents of the list randomly.
816
+ *
817
+ * @see {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js}
818
+ * @fires playlistsorted
819
+ * @todo Make the `rest` option default to `true` in v5.0.0.
820
+ * @param {Object} [options]
821
+ * An object containing shuffle options.
822
+ *
823
+ * @param {boolean} [options.rest = false]
824
+ * By default, the entire playlist is randomized. However, this may
825
+ * not be desirable in all cases, such as when a user is already
826
+ * watching a video.
827
+ *
828
+ * When `true` is passed for this option, it will only shuffle
829
+ * playlist items after the current item. For example, when on the
830
+ * first item, will shuffle the second item and beyond.
831
+ */
832
+
833
+
834
+ playlist.shuffle = function (_temp) {
835
+ var _ref = _temp === void 0 ? {} : _temp,
836
+ rest = _ref.rest;
837
+
838
+ var index = 0;
839
+ var arr = list; // When options.rest is true, start randomization at the item after the
840
+ // current item.
841
+
842
+ if (rest) {
843
+ index = playlist.currentIndex_ + 1;
844
+ arr = list.slice(index);
845
+ } // Bail if the array is empty or too short to shuffle.
846
+
847
+
848
+ if (arr.length <= 1) {
849
+ return;
850
+ }
851
+
852
+ randomize(arr); // When options.rest is true, splice the randomized sub-array back into
853
+ // the original array.
854
+
855
+ if (rest) {
856
+ var _list;
857
+
858
+ (_list = list).splice.apply(_list, [index, arr.length].concat(arr));
859
+ } // If the playlist is changing, don't trigger events.
860
+
861
+
862
+ if (changing) {
863
+ return;
864
+ }
865
+ /**
866
+ * Triggered after the playlist is sorted internally.
867
+ *
868
+ * @event playlistsorted
869
+ * @type {Object}
870
+ */
871
+
872
+
873
+ player.trigger('playlistsorted');
874
+ }; // If an initial list was given, populate the playlist with it.
875
+
876
+
877
+ if (Array.isArray(initialList)) {
878
+ playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array.
879
+ } else {
880
+ list = [];
881
+ }
882
+
883
+ return playlist;
884
+ }
885
+
886
+ var version = "4.3.1";
887
+
888
+ var registerPlugin = videojs.registerPlugin || videojs.plugin;
889
+ /**
890
+ * The video.js playlist plugin. Invokes the playlist-maker to create a
891
+ * playlist function on the specific player.
892
+ *
893
+ * @param {Array} list
894
+ * a list of sources
895
+ *
896
+ * @param {number} item
897
+ * The index to start at
898
+ */
899
+
900
+ var plugin = function plugin(list, item) {
901
+ factory(this, list, item);
902
+ };
903
+
904
+ registerPlugin('playlist', plugin);
905
+ plugin.VERSION = version;
906
+
907
+ return plugin;
908
+
909
+ }));