Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/* The MIT License (MIT)
2
 
3
Copyright (c) 2014-2015 Benoit Tremblay <trembl.ben@gmail.com>
4
 
5
Permission is hereby granted, free of charge, to any person obtaining a copy
6
of this software and associated documentation files (the "Software"), to deal
7
in the Software without restriction, including without limitation the rights
8
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
copies of the Software, and to permit persons to whom the Software is
10
furnished to do so, subject to the following conditions:
11
 
12
The above copyright notice and this permission notice shall be included in
13
all copies or substantial portions of the Software.
14
 
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
THE SOFTWARE. */
22
/*global define, YT*/
23
(function (root, factory) {
24
  if(typeof exports==='object' && typeof module!=='undefined') {
25
    var videojs = require('video.js');
26
    module.exports = factory(videojs.default || videojs);
27
  } else if(typeof define === 'function' && define.amd) {
28
    define(['media_videojs/video-lazy'], function(videojs){
29
      return (root.Youtube = factory(videojs));
30
    });
31
  } else {
32
    root.Youtube = factory(root.videojs);
33
  }
34
}(this, function(videojs) {
35
  'use strict';
36
 
37
  var _isOnMobile = videojs.browser.IS_IOS || videojs.browser.IS_NATIVE_ANDROID;
38
  var Tech = videojs.getTech('Tech');
39
 
40
  class Youtube extends Tech {
41
 
42
    constructor(options, ready) {
43
      super(options, ready);
44
 
45
      this.setPoster(options.poster);
46
      this.setSrc(this.options_.source, true);
47
 
48
      // Set the vjs-youtube class to the player
49
      // Parent is not set yet so we have to wait a tick
50
      this.setTimeout(function() {
51
        if (this.el_) {
52
          this.el_.parentNode.className += ' vjs-youtube';
53
 
54
          if (_isOnMobile) {
55
            this.el_.parentNode.className += ' vjs-youtube-mobile';
56
          }
57
 
58
          if (Youtube.isApiReady) {
59
            this.initYTPlayer();
60
          } else {
61
            Youtube.apiReadyQueue.push(this);
62
          }
63
        }
64
      }.bind(this));
65
    }
66
 
67
    dispose() {
68
      if (this.ytPlayer) {
69
        //Dispose of the YouTube Player
70
        if (this.ytPlayer.stopVideo) {
71
          this.ytPlayer.stopVideo();
72
        }
73
        if (this.ytPlayer.destroy) {
74
          this.ytPlayer.destroy();
75
        }
76
      } else {
77
        //YouTube API hasn't finished loading or the player is already disposed
78
        var index = Youtube.apiReadyQueue.indexOf(this);
79
        if (index !== -1) {
80
          Youtube.apiReadyQueue.splice(index, 1);
81
        }
82
      }
83
      this.ytPlayer = null;
84
 
85
      this.el_.parentNode.className = this.el_.parentNode.className
86
          .replace(' vjs-youtube', '')
87
          .replace(' vjs-youtube-mobile', '');
88
      this.el_.parentNode.removeChild(this.el_);
89
 
90
      //Needs to be called after the YouTube player is destroyed, otherwise there will be a null reference exception
91
      Tech.prototype.dispose.call(this);
92
    }
93
 
94
    createEl() {
95
      var div = document.createElement('div');
96
      div.setAttribute('id', this.options_.techId);
97
      div.setAttribute('style', 'width:100%;height:100%;top:0;left:0;position:absolute');
98
      div.setAttribute('class', 'vjs-tech');
99
 
100
      var divWrapper = document.createElement('div');
101
      divWrapper.appendChild(div);
102
 
103
      if (!_isOnMobile && !this.options_.ytControls) {
104
        var divBlocker = document.createElement('div');
105
        divBlocker.setAttribute('class', 'vjs-iframe-blocker');
106
        divBlocker.setAttribute('style', 'position:absolute;top:0;left:0;width:100%;height:100%');
107
 
108
        // In case the blocker is still there and we want to pause
109
        divBlocker.onclick = function() {
110
          this.pause();
111
        }.bind(this);
112
 
113
        divWrapper.appendChild(divBlocker);
114
      }
115
 
116
      return divWrapper;
117
    }
118
 
119
    initYTPlayer() {
120
      var playerVars = {
121
        controls: 0,
122
        modestbranding: 1,
123
        rel: 0,
124
        showinfo: 0,
125
        loop: this.options_.loop ? 1 : 0
126
      };
127
 
128
      // Let the user set any YouTube parameter
129
      // https://developers.google.com/youtube/player_parameters?playerVersion=HTML5#Parameters
130
      // To use YouTube controls, you must use ytControls instead
131
      // To use the loop or autoplay, use the video.js settings
132
 
133
      if (typeof this.options_.autohide !== 'undefined') {
134
        playerVars.autohide = this.options_.autohide;
135
      }
136
 
137
      if (typeof this.options_['cc_load_policy'] !== 'undefined') {
138
        playerVars['cc_load_policy'] = this.options_['cc_load_policy'];
139
      }
140
 
141
      if (typeof this.options_.ytControls !== 'undefined') {
142
        playerVars.controls = this.options_.ytControls;
143
      }
144
 
145
      if (typeof this.options_.disablekb !== 'undefined') {
146
        playerVars.disablekb = this.options_.disablekb;
147
      }
148
 
149
      if (typeof this.options_.color !== 'undefined') {
150
        playerVars.color = this.options_.color;
151
      }
152
 
153
      if (!playerVars.controls) {
154
        // Let video.js handle the fullscreen unless it is the YouTube native controls
155
        playerVars.fs = 0;
156
      } else if (typeof this.options_.fs !== 'undefined') {
157
        playerVars.fs = this.options_.fs;
158
      }
159
 
160
      if (this.options_.source.src.indexOf('end=') !== -1) {
161
        var srcEndTime = this.options_.source.src.match(/end=([0-9]*)/);
162
        this.options_.end = parseInt(srcEndTime[1]);
163
      }
164
 
165
      if (typeof this.options_.end !== 'undefined') {
166
        playerVars.end = this.options_.end;
167
      }
168
 
169
      if (typeof this.options_.hl !== 'undefined') {
170
        playerVars.hl = this.options_.hl;
171
      } else if (typeof this.options_.language !== 'undefined') {
172
        // Set the YouTube player on the same language than video.js
173
        playerVars.hl = this.options_.language.substr(0, 2);
174
      }
175
 
176
      if (typeof this.options_['iv_load_policy'] !== 'undefined') {
177
        playerVars['iv_load_policy'] = this.options_['iv_load_policy'];
178
      }
179
 
180
      if (typeof this.options_.list !== 'undefined') {
181
        playerVars.list = this.options_.list;
182
      } else if (this.url && typeof this.url.listId !== 'undefined') {
183
        playerVars.list = this.url.listId;
184
      }
185
 
186
      if (typeof this.options_.listType !== 'undefined') {
187
        playerVars.listType = this.options_.listType;
188
      }
189
 
190
      if (typeof this.options_.modestbranding !== 'undefined') {
191
        playerVars.modestbranding = this.options_.modestbranding;
192
      }
193
 
194
      if (typeof this.options_.playlist !== 'undefined') {
195
        playerVars.playlist = this.options_.playlist;
196
      }
197
 
198
      if (typeof this.options_.playsinline !== 'undefined') {
199
        playerVars.playsinline = this.options_.playsinline;
200
      }
201
 
202
      if (typeof this.options_.rel !== 'undefined') {
203
        playerVars.rel = this.options_.rel;
204
      }
205
 
206
      if (typeof this.options_.showinfo !== 'undefined') {
207
        playerVars.showinfo = this.options_.showinfo;
208
      }
209
 
210
      if (this.options_.source.src.indexOf('start=') !== -1) {
211
        var srcStartTime = this.options_.source.src.match(/start=([0-9]*)/);
212
        this.options_.start = parseInt(srcStartTime[1]);
213
      }
214
 
215
      if (typeof this.options_.start !== 'undefined') {
216
        playerVars.start = this.options_.start;
217
      }
218
 
219
      if (typeof this.options_.theme !== 'undefined') {
220
        playerVars.theme = this.options_.theme;
221
      }
222
 
223
      // Allow undocumented options to be passed along via customVars
224
      if (typeof this.options_.customVars !== 'undefined') {
225
        var customVars = this.options_.customVars;
226
        Object.keys(customVars).forEach(function(key) {
227
          playerVars[key] = customVars[key];
228
        });
229
      }
230
 
231
      this.activeVideoId = this.url ? this.url.videoId : null;
232
      this.activeList = playerVars.list;
233
 
234
      var playerConfig = {
235
        videoId: this.activeVideoId,
236
        playerVars: playerVars,
237
        events: {
238
          onReady: this.onPlayerReady.bind(this),
239
          onPlaybackQualityChange: this.onPlayerPlaybackQualityChange.bind(this),
240
          onPlaybackRateChange: this.onPlayerPlaybackRateChange.bind(this),
241
          onStateChange: this.onPlayerStateChange.bind(this),
242
          onVolumeChange: this.onPlayerVolumeChange.bind(this),
243
          onError: this.onPlayerError.bind(this)
244
        }
245
      };
246
 
247
      if (typeof this.options_.enablePrivacyEnhancedMode !== 'undefined' && this.options_.enablePrivacyEnhancedMode) {
248
        playerConfig.host = 'https://www.youtube-nocookie.com';
249
      }
250
 
251
      this.ytPlayer = new YT.Player(this.options_.techId, playerConfig);
252
    }
253
 
254
    onPlayerReady() {
255
      if (this.options_.muted) {
256
        this.ytPlayer.mute();
257
      }
258
 
259
      var playbackRates = this.ytPlayer.getAvailablePlaybackRates();
260
      if (playbackRates.length > 1) {
261
        this.featuresPlaybackRate = true;
262
      }
263
 
264
      this.playerReady_ = true;
265
      this.triggerReady();
266
 
267
      if (this.playOnReady) {
268
        this.play();
269
      } else if (this.cueOnReady) {
270
        this.cueVideoById_(this.url.videoId);
271
        this.activeVideoId = this.url.videoId;
272
      }
273
    }
274
 
275
    onPlayerPlaybackQualityChange() {
276
 
277
    }
278
 
279
    onPlayerPlaybackRateChange() {
280
      this.trigger('ratechange');
281
    }
282
 
283
    onPlayerStateChange(e) {
284
      var state = e.data;
285
 
286
      if (state === this.lastState || this.errorNumber) {
287
        return;
288
      }
289
 
290
      this.lastState = state;
291
 
292
      switch (state) {
293
        case -1:
294
          this.trigger('loadstart');
295
          this.trigger('loadedmetadata');
296
          this.trigger('durationchange');
297
          this.trigger('ratechange');
298
          break;
299
 
300
        case YT.PlayerState.ENDED:
301
          this.trigger('ended');
302
          break;
303
 
304
        case YT.PlayerState.PLAYING:
305
          this.trigger('timeupdate');
306
          this.trigger('durationchange');
307
          this.trigger('playing');
308
          this.trigger('play');
309
 
310
          if (this.isSeeking) {
311
            this.onSeeked();
312
          }
313
          break;
314
 
315
        case YT.PlayerState.PAUSED:
316
          this.trigger('canplay');
317
          if (this.isSeeking) {
318
            this.onSeeked();
319
          } else {
320
            this.trigger('pause');
321
          }
322
          break;
323
 
324
        case YT.PlayerState.BUFFERING:
325
          this.player_.trigger('timeupdate');
326
          this.player_.trigger('waiting');
327
          break;
328
      }
329
    }
330
 
331
    onPlayerVolumeChange() {
332
      this.trigger('volumechange');
333
    }
334
 
335
    onPlayerError(e) {
336
      this.errorNumber = e.data;
337
      this.trigger('pause');
338
      this.trigger('error');
339
    }
340
 
341
    error() {
342
      var code = 1000 + this.errorNumber; // as smaller codes are reserved
343
      switch (this.errorNumber) {
344
        case 5:
345
          return { code: code, message: 'Error while trying to play the video' };
346
 
347
        case 2:
348
        case 100:
349
          return { code: code, message: 'Unable to find the video' };
350
 
351
        case 101:
352
        case 150:
353
          return {
354
            code: code,
355
            message: 'Playback on other Websites has been disabled by the video owner.'
356
          };
357
      }
358
 
359
      return { code: code, message: 'YouTube unknown error (' + this.errorNumber + ')' };
360
    }
361
 
362
    loadVideoById_(id) {
363
      var options = {
364
        videoId: id
365
      };
366
      if (this.options_.start) {
367
        options.startSeconds = this.options_.start;
368
      }
369
      if (this.options_.end) {
370
        options.endSeconds = this.options_.end;
371
      }
372
      this.ytPlayer.loadVideoById(options);
373
    }
374
 
375
    cueVideoById_(id) {
376
      var options = {
377
        videoId: id
378
      };
379
      if (this.options_.start) {
380
        options.startSeconds = this.options_.start;
381
      }
382
      if (this.options_.end) {
383
        options.endSeconds = this.options_.end;
384
      }
385
      this.ytPlayer.cueVideoById(options);
386
    }
387
 
388
    src(src) {
389
      if (src) {
390
        this.setSrc({ src: src });
391
      }
392
 
393
      return this.source;
394
    }
395
 
396
    poster() {
397
      // You can't start programmaticlly a video with a mobile
398
      // through the iframe so we hide the poster and the play button (with CSS)
399
      if (_isOnMobile) {
400
        return null;
401
      }
402
 
403
      return this.poster_;
404
    }
405
 
406
    setPoster(poster) {
407
      this.poster_ = poster;
408
    }
409
 
410
    setSrc(source) {
411
      if (!source || !source.src) {
412
        return;
413
      }
414
 
415
      delete this.errorNumber;
416
      this.source = source;
417
      this.url = Youtube.parseUrl(source.src);
418
 
419
      if (!this.options_.poster) {
420
        if (this.url.videoId) {
421
          // Set the low resolution first
422
          this.poster_ = 'https://img.youtube.com/vi/' + this.url.videoId + '/0.jpg';
423
          this.trigger('posterchange');
424
 
425
          // Check if their is a high res
426
          this.checkHighResPoster();
427
        }
428
      }
429
 
430
      if (this.options_.autoplay && !_isOnMobile) {
431
        if (this.isReady_) {
432
          this.play();
433
        } else {
434
          this.playOnReady = true;
435
        }
436
      } else if (this.activeVideoId !== this.url.videoId) {
437
        if (this.isReady_) {
438
          this.cueVideoById_(this.url.videoId);
439
          this.activeVideoId = this.url.videoId;
440
        } else {
441
          this.cueOnReady = true;
442
        }
443
      }
444
    }
445
 
446
    autoplay() {
447
      return this.options_.autoplay;
448
    }
449
 
450
    setAutoplay(val) {
451
      this.options_.autoplay = val;
452
    }
453
 
454
    loop() {
455
      return this.options_.loop;
456
    }
457
 
458
    setLoop(val) {
459
      this.options_.loop = val;
460
    }
461
 
462
    play() {
463
      if (!this.url || !this.url.videoId) {
464
        return;
465
      }
466
 
467
      this.wasPausedBeforeSeek = false;
468
 
469
      if (this.isReady_) {
470
        if (this.url.listId) {
471
          if (this.activeList === this.url.listId) {
472
            this.ytPlayer.playVideo();
473
          } else {
474
            this.ytPlayer.loadPlaylist(this.url.listId);
475
            this.activeList = this.url.listId;
476
          }
477
        }
478
 
479
        if (this.activeVideoId === this.url.videoId) {
480
          this.ytPlayer.playVideo();
481
        } else {
482
          this.loadVideoById_(this.url.videoId);
483
          this.activeVideoId = this.url.videoId;
484
        }
485
      } else {
486
        this.trigger('waiting');
487
        this.playOnReady = true;
488
      }
489
    }
490
 
491
    pause() {
492
      if (this.ytPlayer) {
493
        this.ytPlayer.pauseVideo();
494
      }
495
    }
496
 
497
    paused() {
498
      return (this.ytPlayer) ?
499
          (this.lastState !== YT.PlayerState.PLAYING && this.lastState !== YT.PlayerState.BUFFERING)
500
          : true;
501
    }
502
 
503
    currentTime() {
504
      return this.ytPlayer ? this.ytPlayer.getCurrentTime() : 0;
505
    }
506
 
507
    setCurrentTime(seconds) {
508
      if (this.lastState === YT.PlayerState.PAUSED) {
509
        this.timeBeforeSeek = this.currentTime();
510
      }
511
 
512
      if (!this.isSeeking) {
513
        this.wasPausedBeforeSeek = this.paused();
514
      }
515
 
516
      this.ytPlayer.seekTo(seconds, true);
517
      this.trigger('timeupdate');
518
      this.trigger('seeking');
519
      this.isSeeking = true;
520
 
521
      // A seek event during pause does not return an event to trigger a seeked event,
522
      // so run an interval timer to look for the currentTime to change
523
      if (this.lastState === YT.PlayerState.PAUSED && this.timeBeforeSeek !== seconds) {
524
        clearInterval(this.checkSeekedInPauseInterval);
525
        this.checkSeekedInPauseInterval = setInterval(function() {
526
          if (this.lastState !== YT.PlayerState.PAUSED || !this.isSeeking) {
527
            // If something changed while we were waiting for the currentTime to change,
528
            //  clear the interval timer
529
            clearInterval(this.checkSeekedInPauseInterval);
530
          } else if (this.currentTime() !== this.timeBeforeSeek) {
531
            this.trigger('timeupdate');
532
            this.onSeeked();
533
          }
534
        }.bind(this), 250);
535
      }
536
    }
537
 
538
    seeking() {
539
      return this.isSeeking;
540
    }
541
 
542
    seekable() {
543
      if(!this.ytPlayer) {
544
        return videojs.createTimeRange();
545
      }
546
 
547
      return videojs.createTimeRange(0, this.ytPlayer.getDuration());
548
    }
549
 
550
    onSeeked() {
551
      clearInterval(this.checkSeekedInPauseInterval);
552
      this.isSeeking = false;
553
 
554
      if (this.wasPausedBeforeSeek) {
555
        this.pause();
556
      }
557
 
558
      this.trigger('seeked');
559
    }
560
 
561
    playbackRate() {
562
      return this.ytPlayer ? this.ytPlayer.getPlaybackRate() : 1;
563
    }
564
 
565
    setPlaybackRate(suggestedRate) {
566
      if (!this.ytPlayer) {
567
        return;
568
      }
569
 
570
      this.ytPlayer.setPlaybackRate(suggestedRate);
571
    }
572
 
573
    duration() {
574
      return this.ytPlayer ? this.ytPlayer.getDuration() : 0;
575
    }
576
 
577
    currentSrc() {
578
      return this.source && this.source.src;
579
    }
580
 
581
    ended() {
582
      return this.ytPlayer ? (this.lastState === YT.PlayerState.ENDED) : false;
583
    }
584
 
585
    volume() {
586
      return this.ytPlayer ? this.ytPlayer.getVolume() / 100.0 : 1;
587
    }
588
 
589
    setVolume(percentAsDecimal) {
590
      if (!this.ytPlayer) {
591
        return;
592
      }
593
 
594
      this.ytPlayer.setVolume(percentAsDecimal * 100.0);
595
    }
596
 
597
    muted() {
598
      return this.ytPlayer ? this.ytPlayer.isMuted() : false;
599
    }
600
 
601
    setMuted(mute) {
602
      if (!this.ytPlayer) {
603
        return;
604
      }
605
      else{
606
        this.muted(true);
607
      }
608
 
609
      if (mute) {
610
        this.ytPlayer.mute();
611
      } else {
612
        this.ytPlayer.unMute();
613
      }
614
      this.setTimeout( function(){
615
        this.trigger('volumechange');
616
      }, 50);
617
    }
618
 
619
    buffered() {
620
      if(!this.ytPlayer || !this.ytPlayer.getVideoLoadedFraction) {
621
        return videojs.createTimeRange();
622
      }
623
 
624
      var bufferedEnd = this.ytPlayer.getVideoLoadedFraction() * this.ytPlayer.getDuration();
625
 
626
      return videojs.createTimeRange(0, bufferedEnd);
627
    }
628
 
629
    // TODO: Can we really do something with this on YouTUbe?
630
    preload() {}
631
    load() {}
632
    reset() {}
633
    networkState() {
634
      if (!this.ytPlayer) {
635
        return 0; //NETWORK_EMPTY
636
      }
637
      switch (this.ytPlayer.getPlayerState()) {
638
        case -1: //unstarted
639
          return 0; //NETWORK_EMPTY
640
        case 3: //buffering
641
          return 2; //NETWORK_LOADING
642
        default:
643
          return 1; //NETWORK_IDLE
644
      }
645
    }
646
    readyState() {
647
      if (!this.ytPlayer) {
648
        return 0; //HAVE_NOTHING
649
      }
650
      switch (this.ytPlayer.getPlayerState()) {
651
        case -1: //unstarted
652
          return 0; //HAVE_NOTHING
653
        case 5: //video cued
654
          return 1; //HAVE_METADATA
655
        case 3: //buffering
656
          return 2; //HAVE_CURRENT_DATA
657
        default:
658
          return 4; //HAVE_ENOUGH_DATA
659
      }
660
    }
661
 
662
    supportsFullScreen() {
663
      return document.fullscreenEnabled ||
664
          document.webkitFullscreenEnabled ||
665
          document.mozFullScreenEnabled ||
666
          document.msFullscreenEnabled;
667
    }
668
 
669
    // Tries to get the highest resolution thumbnail available for the video
670
    checkHighResPoster(){
671
      var uri = 'https://img.youtube.com/vi/' + this.url.videoId + '/maxresdefault.jpg';
672
 
673
      try {
674
        var image = new Image();
675
        image.onload = function(){
676
          // Onload may still be called if YouTube returns the 120x90 error thumbnail
677
          if('naturalHeight' in image){
678
            if (image.naturalHeight <= 90 || image.naturalWidth <= 120) {
679
              return;
680
            }
681
          } else if(image.height <= 90 || image.width <= 120) {
682
            return;
683
          }
684
 
685
          this.poster_ = uri;
686
          this.trigger('posterchange');
687
        }.bind(this);
688
        image.onerror = function(){};
689
        image.src = uri;
690
      }
691
      catch(e){}
692
    }
693
  }
694
 
695
  Youtube.isSupported = function() {
696
    return true;
697
  };
698
 
699
  Youtube.canPlaySource = function(e) {
700
    return Youtube.canPlayType(e.type);
701
  };
702
 
703
  Youtube.canPlayType = function(e) {
704
    return (e === 'video/youtube');
705
  };
706
 
707
  Youtube.parseUrl = function(url) {
708
    var result = {
709
      videoId: null
710
    };
711
 
712
    var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
713
    var match = url.match(regex);
714
 
715
    if (match && match[2].length === 11) {
716
      result.videoId = match[2];
717
    }
718
 
719
    var regPlaylist = /[?&]list=([^#\&\?]+)/;
720
    match = url.match(regPlaylist);
721
 
722
    if(match && match[1]) {
723
      result.listId = match[1];
724
    }
725
 
726
    return result;
727
  };
728
 
729
  function apiLoaded() {
730
    YT.ready(function() {
731
      Youtube.isApiReady = true;
732
 
733
      for (var i = 0; i < Youtube.apiReadyQueue.length; ++i) {
734
        Youtube.apiReadyQueue[i].initYTPlayer();
735
      }
736
    });
737
  }
738
 
739
  function loadScript(src, callback) {
740
    var loaded = false;
741
    var tag = document.createElement('script');
742
    var firstScriptTag = document.getElementsByTagName('script')[0];
743
    if (!firstScriptTag) {
744
      // when loaded in jest without jsdom setup it doesn't get any element.
745
      // In jest it doesn't really make sense to do anything, because no one is watching youtube in jest
746
      return;
747
    }
748
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
749
    tag.onload = function () {
750
      if (!loaded) {
751
        loaded = true;
752
        callback();
753
      }
754
    };
755
    tag.onreadystatechange = function () {
756
      if (!loaded && (this.readyState === 'complete' || this.readyState === 'loaded')) {
757
        loaded = true;
758
        callback();
759
      }
760
    };
761
    tag.src = src;
762
  }
763
 
764
  function injectCss() {
765
    var css = // iframe blocker to catch mouse events
766
        '.vjs-youtube .vjs-iframe-blocker { display: none; }' +
767
        '.vjs-youtube.vjs-user-inactive .vjs-iframe-blocker { display: block; }' +
768
        '.vjs-youtube .vjs-poster { background-size: cover; }' +
769
        '.vjs-youtube-mobile .vjs-big-play-button { display: none; }';
770
 
771
    var head = document.head || document.getElementsByTagName('head')[0];
772
 
773
    var style = document.createElement('style');
774
    style.type = 'text/css';
775
 
776
    if (style.styleSheet){
777
      style.styleSheet.cssText = css;
778
    } else {
779
      style.appendChild(document.createTextNode(css));
780
    }
781
 
782
    head.appendChild(style);
783
  }
784
 
785
  Youtube.apiReadyQueue = [];
786
 
787
  if (typeof document !== 'undefined'){
788
    loadScript('https://www.youtube.com/iframe_api', apiLoaded);
789
    injectCss();
790
  }
791
 
792
  // Older versions of VJS5 doesn't have the registerTech function
793
  if (typeof videojs.registerTech !== 'undefined') {
794
    videojs.registerTech('Youtube', Youtube);
795
  } else {
796
    videojs.registerComponent('Youtube', Youtube);
797
  }
798
}));