]> _ Git - fluidbook-html5.git/commitdiff
wait #6015 @2
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Fri, 23 Jun 2023 12:05:53 +0000 (14:05 +0200)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Fri, 23 Jun 2023 12:05:53 +0000 (14:05 +0200)
js/libs/fluidbook/fluidbook.menu.js
js/libs/fluidbook/fluidbook.stats.js
js/libs/fluidbook/fluidbook.video.js
js/libs/fluidbook/slideshow/fluidbook.slideshow.js
js/libs/videojs/Dailymotion.js [new file with mode: 0644]
js/libs/videojs/Vimeo.js [new file with mode: 0644]

index 9b9c1e60b6c32db9e14cbabc6eec35ea5262f898..bd2a2a47f1d91e77d8bbd426b99e6ae860a44a1d 100644 (file)
@@ -337,7 +337,6 @@ FluidbookMenu.prototype = {
         view += '</div>';
         this.viewWrap(view, 'video', '', '', false, hash);
 
-        this.fluidbook.stats.track(11);
         this.fluidbook.initVideos();
         var $this = this;
         var times = [250, 500, 750, 1000, 1250];
@@ -370,9 +369,6 @@ FluidbookMenu.prototype = {
         view += '</div>';
         this.viewWrap(view, 'slideshow', '', '', false, hash);
 
-        // TODO: check which type should be passed to fluidbook.stats.track() ???
-        // this.fluidbook.stats.track(11);
-
         this.fluidbook.slideshow.initPopupSlideshow($(".fb-slideshow").eq(0));
 
         // var $this = this;
@@ -408,7 +404,6 @@ FluidbookMenu.prototype = {
         view += markup;
         view += '</div>';
         this.viewWrap(view, 'audio', '', '', false, hash);
-        this.fluidbook.stats.track(11);
         var $this = this;
         var times = [250, 500, 750, 1000, 1250];
         $.each(times, function (k, v) {
@@ -431,7 +426,6 @@ FluidbookMenu.prototype = {
         view += '</div>';
         this.viewWrap(view, 'webvideo', '', '', false, hash);
         this.fluidbook.initVideos();
-        this.fluidbook.stats.track(11);
 
         if (callback != undefined) {
             callback();
index 5221580da467f3e1afd3e40bf1ad0aa14d90c5e2..a81077f389ed3d6212d53e6f7e7108b3d1c5da92 100644 (file)
@@ -285,9 +285,6 @@ FluidbookStats.prototype = {
             case 2:// Zoom
                 window._paq.push(['trackEvent', 'zoom', 'page', page]);
                 break;
-            case 11:// Video
-                window._paq.push(['trackEvent', 'video', 'view', extra]);
-                break;
             case 15://
                 window._paq.push(['trackEvent', 'cart', 'addproduct', extra]);
         }
@@ -349,14 +346,29 @@ FluidbookStats.prototype = {
             case 2:// Zoom
                 this._ga('event', 'zoom', 'page', page);
                 break;
-            case 11:// Video
-                this._ga('event', 'video', 'view', extra);
-                break;
             case 15://
                 this._ga('event', 'cart', 'addproduct', extra);
         }
     },
 
+    trackEvent: function (category, action, name) {
+        if (!this.fluidbook.support.hasNetwork() || this.fluidbook.nointerface) {
+            return;
+        }
+        if (this.fluidbook.settings.statsMatomo) {
+            try {
+                window._paq.push(['trackEvent', category, action, name + '|:|' + this.fluidbook.currentPage]);
+            } catch (e) {
+
+            }
+        }
+        try {
+            this._ga('event', category, action, name);
+        } catch (e) {
+
+        }
+    },
+
     _ga: function (a0, a1, a2, a3, a4) {
         var args = Array.prototype.slice.call(arguments);
         if (this.ga === 'gtm') {
@@ -412,8 +424,6 @@ FluidbookStats.prototype = {
         }
 
         var $this = this;
-
-
         var data = {
             id: $this.id, vid: $this.vid, type: type, page: page, str: extra, time: new Date().getTime()
         };
index 9a1a011fc369925f68d7d58ebcd4506e72c05608..ed511d02ea1acbf3588b8140ca1dbe9c2a9dbe1d 100644 (file)
@@ -108,6 +108,8 @@ FluidbookVideo.prototype = {
             sound = parseInt($(e).data('sound')) == 1,
             autoplay = parseInt($(e).data('autoplay')) == 1,
             nativeAutoplay = !autoplay && parseInt($(e).data('nativeautoplay')) == 1,
+            statsName = $(e).data('stats-name'),
+            statsType = $(e).data('stats-type'),
             setup = $(e).data('setup'),
             linkid = $(e).data('link-id'),
             url = $(e).data('url'),
@@ -117,7 +119,7 @@ FluidbookVideo.prototype = {
             player;
         var hidelinksonplay = $(e).data('hidelinksonplay') == undefined || $(e).data('hidelinksonplay') === '' ? [] : $(e).data('hidelinksonplay').split(',');
 
-        //console.log('Initialising video ID: ' + id);
+        // console.log('Initialising video ID: ' + id, statsType, statsName);
 
         // Player might be active but not visible so we need to dispose of it before re-initialising the element
         if (videojs.players[id]) {
@@ -212,11 +214,22 @@ FluidbookVideo.prototype = {
 
         $(e).html(html);
 
+        let playEventSent = false;
+
+        function sendPlayEvent() {
+            if (!playEventSent) {
+                $this.fluidbook.stats.trackEvent(statsType, 'play', statsName);
+                playEventSent = true;
+            }
+        }
 
         player = videojs(id, setup);
         player.ready(function () {
             //console.log(id + ' player is ready');
 
+
+            $this.fluidbook.stats.trackEvent(statsType, 'show', statsName);
+
             $this.resizeControls(); // Make sure player controls are the right size
 
             if (autoplay) {
@@ -224,10 +237,12 @@ FluidbookVideo.prototype = {
 
                 if (promise !== undefined) {
                     promise.then(function () {
-                        console.log('autoplay ok');
+                        sendPlayEvent();
                         // Autoplay started!
                     }).catch(function (error) {
-                        console.log('autoplay nok');
+                        player.one('play', function () {
+                            sendPlayEvent();
+                        });
                         // Autoplay was prevented.
                     });
                 } else {
@@ -238,7 +253,6 @@ FluidbookVideo.prototype = {
             if (fluidbook.video.players[id]) {
                 //console.log('found saved settings for player ID ' + id);
                 var settings = fluidbook.video.players[id];
-                //console.log(settings);
 
                 // In the case of autoplay videos, we don't want the autoplay to fire
                 // more than once (ie. never after we have these settings reccorded)
@@ -256,13 +270,16 @@ FluidbookVideo.prototype = {
                 }, 50);
 
                 player.one('play', function () {
-                    //console.log('Player has started playing... ID: ' + id);
-                    // Handle pause in here if needed.. Better than using the timeout...
+                    // Handle pause in here if needed.
                     if (settings.paused) {
+                        player.one('play', function () {
+                            sendPlayEvent();
+                        });
                         setTimeout(function () {
                             player.pause();
                         }, 100);
                     } else {
+                        sendPlayEvent();
                         $.each(hidelinksonplay, function (k, id) {
                             $this.fluidbook.links.hideLinkById(id);
                         });
@@ -273,6 +290,11 @@ FluidbookVideo.prototype = {
                 player.play(); // Start player to go to current position - necessary even if it will be paused immediately
                 if (settings.paused) {
                     player.pause();
+                    player.one('play', function () {
+                        sendPlayEvent();
+                    });
+                } else {
+                    sendPlayEvent();
                 }
 
                 setTimeout(function () {
@@ -281,6 +303,7 @@ FluidbookVideo.prototype = {
             }
         });
 
+
         player.on('play', function () {
             $.each(hidelinksonplay, function (k, id) {
                 $this.fluidbook.links.hideLinkById(id);
@@ -288,8 +311,6 @@ FluidbookVideo.prototype = {
         });
 
         player.on('pause', function () {
-            // console.log(id + ' player paused');
-
             // Show play button (ref: http://stackoverflow.com/a/25296575)
             if (controls) {
                 this.bigPlayButton.show();
@@ -307,12 +328,6 @@ FluidbookVideo.prototype = {
             });
         });
 
-        // player.on('fullscreenchange', function() {
-        //     console.log('Entering or exiting fullscreen... resetting control sizes #' + player.id());
-        // Note: this doesn't work because the fluidbookresize gets called multiple times after this fires and that overrides this change
-        //     $('#' + player.id()).attr('style', ''); // Reset inline styles
-        // });
-
         player.on('fullscreenchange', function () {
             if (player.isFullscreen()) {
                 $(window).trigger('videoFullscreenEntered');
@@ -342,8 +357,6 @@ FluidbookVideo.prototype = {
             path,
             poster;
 
-        //console.info(width, height, name, loop);
-
         if (fluidbook.settings.mobileVideosPath == '') {
             path = "data/links/" + name;
         } else {
index ee2cad37397f84f21bf584338b748ae77d3d8f09..ea22a6bce90d9aa9894265d7206d09eb62bab428 100644 (file)
@@ -57,6 +57,7 @@ FluidbookSlideshow.prototype = {
 
     initPopupSlideshow: function (s) {
         s = this.normalizeSlideshowElement(s);
+        this.fluidbook.stats.trackEvent('slideshow', 'show', $(s).data('name'));
         this.popupInstance.initSlideshow(s);
     },
 
diff --git a/js/libs/videojs/Dailymotion.js b/js/libs/videojs/Dailymotion.js
new file mode 100644 (file)
index 0000000..3622085
--- /dev/null
@@ -0,0 +1,556 @@
+/*global define, DM*/
+(function (root, factory) {
+    if (typeof window !== 'undefined' && window.videojs) {
+        factory(window.videojs);
+    } else if (typeof exports==='object' && typeof module!=='undefined') {
+        var videojs = require('video.js');
+        module.exports = factory(videojs.default || videojs);
+    } else if (typeof define === 'function' && define.amd) {
+        define(['videojs'], function(videojs){
+            return (root.Dailymotion = factory(videojs));
+        });
+    } else {
+        root.Dailymotion = factory(root.videojs);
+    }
+}(this, function(videojs) {
+    'use strict';
+
+    var _isOnMobile = videojs.browser.IS_IOS || videojs.browser.IS_NATIVE_ANDROID;
+    var Tech = videojs.getTech('Tech');
+
+    var Dailymotion = videojs.extend(Tech, {
+
+        constructor: function(options, ready) {
+            Tech.call(this, options, ready);
+
+            this.setSrc(this.options_.source);
+
+            // Set the vjs-dailymotion class to the player
+            // Parent is not set yet so we have to wait a tick
+            var vm = this;
+            setTimeout(function() {
+                if (this.el_) {
+                    this.el_.parentNode.className += ' vjs-dailymotion';
+
+                    if (_isOnMobile) {
+                        this.el_.parentNode.className += ' vjs-dailymotion-mobile';
+                    }
+
+                    if (Dailymotion.isSdkReady) {
+                        vm.initDMPlayer();
+                    } else {
+                        Dailymotion.sdkReadyQueue.push(vm);
+                    }
+                }
+            }.bind(this));
+        },
+
+        _getPlayerParams: function() {
+            var playerParams = {
+                autoplay: false,
+                mute: false,
+                controls: false,
+                'queue-autoplay-next': false,
+                'queue-enable': false
+            };
+            // Let the user set any Dailymotion parameter
+            // https://developer.dailymotion.com/player/#player-parameters
+            // To use Dailymotion controls, you must use dmControls instead
+
+            var params = ['api', 'autoplay', 'autoplay-mute', 'id', 'mute', 'origin', 'quality', 'queue-autoplay-next',
+                'queue-enable', 'sharing-enable', 'start', 'subtitles-default', 'syndication', 'ui-highlight', 'ui-logo',
+                'ui-start-screen-info', 'ui-theme', 'apimode', 'playlist'];
+            var options = this.options_;
+            params.forEach(function(param) {
+                if (typeof options[param] === 'undefined') {
+                    return;
+                }
+                playerParams[param] = options[param];
+            });
+
+            if (typeof this.options_.dmControls !== 'undefined') {
+                playerParams.controls = this.options_.dmControls;
+            }
+
+            // Overwriting playlist if it is included in url
+            if (this.url && typeof this.url.playlist !== 'undefined') {
+                playerParams.playlist = this.url.playlist;
+            }
+
+            // Allow undocumented options to be passed along via customVars
+            if (typeof this.options_.customVars !== 'undefined') {
+                var customVars = this.options_.customVars;
+                Object.keys(customVars).forEach(function(key) {
+                    playerParams[key] = customVars[key];
+                });
+            }
+
+            return playerParams;
+        },
+
+        _getPlayerConfig: function() {
+            var playerConfig = {
+                width: '100%',
+                height: '100%',
+                params: this._getPlayerParams()
+            };
+
+            if (this.url && typeof this.url.video !== 'undefined') {
+                playerConfig.video = this.url.video;
+            } else if (typeof this.options_.video !== 'undefined') {
+                playerConfig.video = this.options_.video;
+            }
+
+            return playerConfig;
+        },
+
+        initDMPlayer: function() {
+            if (this.dmPlayer) {
+                return;
+            }
+            this.dmPlayer = new DM.player(
+                document.getElementById(this.options_.techId),
+                this._getPlayerConfig()
+            );
+            var vm = this;
+            this.isApiReady = false;
+            this.dmPlayer.addEventListener('apiready', function() {
+                vm.triggerReady();
+                vm.isApiReady = true;
+            });
+            this.dmPlayer.addEventListener('durationchange', function() {
+                vm.trigger('durationchange');
+            });
+            this.dmPlayer.addEventListener('end', function() {
+                vm.trigger('ended');
+            });
+            this.dmPlayer.addEventListener('error', function() {
+                vm.trigger('error');
+            });
+            this.dmPlayer.addEventListener('loadedmetadata', function() {
+                vm.trigger('loadeddata');
+                vm.trigger('loadedmetadata');
+            });
+            this.dmPlayer.addEventListener('pause', function() {
+                vm.trigger('pause');
+            });
+            this.dmPlayer.addEventListener('play', function() {
+                vm.trigger('loadStart');
+                vm.trigger('play');
+                vm.trigger('playing');
+                vm.trigger('waiting');
+            });
+            this.dmPlayer.addEventListener('playback_ready', function() {
+                vm.trigger('loadeddata');
+            });
+            this.dmPlayer.addEventListener('timeupdate', function() {
+                vm.trigger('timeupdate');
+            });
+            this.dmPlayer.addEventListener('volumechange', function() {
+                vm.trigger('volumechange');
+            });
+        },
+
+        autoplay: function(autoplay) {
+            if (typeof autoplay !== 'undefined') {
+                return this.setAutoplay(autoplay);
+            }
+
+            return this.options_.autoplay;
+        },
+
+        setAutoplay: function(val) {
+            return this.options_.autoplay = val;
+        },
+
+        buffered: function() {
+            if(!this.dmPlayer || !this.dmPlayer.bufferedTime) {
+                return videojs.createTimeRange();
+            }
+
+            return videojs.createTimeRange(0, this.dmPlayer.bufferedTime);
+        },
+
+        createEl: function() {
+            var div = document.createElement('div');
+            div.setAttribute('id', this.options_.techId);
+            div.setAttribute('style', 'width:100%;height:100%;top:0;left:0;position:absolute');
+            div.setAttribute('class', 'vjs-tech');
+
+            var divWrapper = document.createElement('div');
+            divWrapper.appendChild(div);
+
+            if (!_isOnMobile && !this.options_.dmControls) {
+                // var divBlocker = document.createElement('div');
+                // divBlocker.setAttribute('class', 'vjs-iframe-blocker');
+                // divBlocker.setAttribute('style', 'position:absolute;top:0;left:0;width:100%;height:100%');
+                //
+                // // In case the blocker is still there and we want to pause
+                // divBlocker.onclick = function() {
+                //   this.pause();
+                // }.bind(this);
+                //
+                // divWrapper.appendChild(divBlocker);
+            }
+
+            return divWrapper;
+        },
+
+        currentSrc: function() {
+            return this.source && this.source.src;
+        },
+
+        currentTime: function(seconds) {
+            if (typeof seconds !== 'undefined') {
+                return this.setCurrentTime(seconds);
+            }
+            return this.dmPlayer && this.dmPlayer.currentTime;
+        },
+
+        setCurrentTime: function(seconds) {
+            if (!this.dmPlayer || !this.dmPlayer.seek) {
+                return;
+            }
+
+            return this.dmPlayer.seek(seconds);
+        },
+
+        dispose: function() {
+            if (DM && DM.destroy) {
+                //Destroy the Dailymotion Player
+                DM.destroy(this.options_.techId);
+            } else {
+                //Dailymotion API hasn't finished loading or the player is already disposed
+                var index = Dailymotion.sdkReadyQueue.indexOf(this);
+                if (index !== -1) {
+                    Dailymotion.sdkReadyQueue.splice(index, 1);
+                }
+            }
+            this.dmPlayer = undefined;
+
+            this.el_.parentNode.className = this.el_.parentNode.className
+                .replace(' vjs-dailymotion', '')
+                .replace(' vjs-dailymotion-mobile', '');
+            this.el_.parentNode.removeChild(this.el_);
+
+            // Needs to be called after the Dailymotion player is destroyed,
+            // otherwise there will be a undefined reference exception
+            Tech.prototype.dispose.call(this);
+        },
+
+        duration: function(seconds) {
+            if (typeof seconds !== 'undefined') {
+                return this.setDuration(seconds);
+            }
+            return this.dmPlayer ? this.dmPlayer.duration : 0;
+        },
+
+        setDuration: function(seconds) {
+            if (!this.dmPlayer || !this.dmPlayer.seek) {
+                return;
+            }
+            return this.dmPlayer.seek(seconds);
+        },
+
+        ended: function() {
+            return this.dmPlayer && this.dmPlayer.ended;
+        },
+
+        enterFullWindow: function() {
+            if (!this.dmPlayer || !this.dmPlayer.setFullscreen) {
+                return;
+            }
+            return this.dmPlayer.setFullscreen(true);
+        },
+
+        error: function() {
+            return this.dmPlayer && this.dmPlayer.error;
+        },
+
+        exitFullscreen: function() {
+            if (!this.dmPlayer || !this.dmPlayer.setFullscreen) {
+                return;
+            }
+            return this.dmPlayer.setFullscreen(false);
+        },
+
+        isFullscreen: function() {
+            return this.dmPlayer && this.dmPlayer.fullscreen;
+        },
+
+        // Not supported by Dailymotion
+        language: function() {
+            return undefined;
+        },
+
+        // Not supported by Dailymotion
+        languages: function() {
+            return undefined;
+        },
+
+        load: function() {
+            if (!this.dmPlayer || !this.dmPlayer.load) {
+                return;
+            }
+            return this.dmPlayer.load(this._getPlayerConfig());
+        },
+
+        // Not supported by Dailymotion
+        loop: function() {
+            return undefined;
+        },
+
+        muted: function(muted) {
+            if (typeof muted !== undefined) {
+                return this.setMuted(muted);
+            }
+            return this.dmPlayer && this.dmPlayer.mute;
+        },
+
+        setMuted: function(mute) {
+            if (typeof mute === 'undefined' || !this.dmPlayer || !this.dmPlayer.setMuted) {
+                return;
+            }
+
+            if (mute) {
+                this.volumeBeforeMute = this.volume();
+                this.setVolume(0);
+            } else {
+                this.setVolume(this.volumeBeforeMute);
+            }
+            this.dmPlayer.setMuted(mute);
+            // var vm = this;
+            // setTimeout( function(){
+            //   vm.trigger('volumechange');
+            // }, 50);
+        },
+
+        networkState: function () {
+            if (!this.dmPlayer || this.dmPlayer.error) {
+                return 0; //NETWORK_EMPTY
+            }
+
+            if (this.dmPlayer.seeking) {
+                return 2; //NETWORK_LOADING
+            }
+        },
+
+        pause: function() {
+            if (!this.dmPlayer || !this.dmPlayer.pause) {
+                return;
+            }
+
+            return this.dmPlayer.pause();
+        },
+
+        paused: function() {
+            return this.dmPlayer && this.dmPlayer.paused;
+        },
+
+        play: function() {
+            if (!this.isApiReady || !this.dmPlayer || !this.dmPlayer.play) {
+                return;
+            }
+
+            this.trigger('loadStart');
+            this.trigger('waiting');
+            return this.dmPlayer.play();
+        },
+
+        // Playback rate is not support by Dailymotion
+        playbackRate: function() {
+            return 1;
+        },
+
+        // Not supported by Dailymotion
+        poster: function() {
+            return undefined;
+        },
+
+        // Not supported by Dailymotion
+        preload: function() {
+            return undefined;
+        },
+
+        // TODO: Confirm if it can be more detail
+        readyState: function() {
+            if (!this.dmPlayer || this.dmPlayer.error) {
+                return 0; //NETWORK_EMPTY
+            }
+
+            if (this.dmPlayer.seeking) {
+                return 1; //HAVE_METADATA
+            }
+            return 4; //HAVE_ENOUGH_DATA
+        },
+
+        remainingTime: function() {
+            return this.dmPlayer && (this.dmPlayer.duration - this.dmPlayer.currentTime);
+        },
+
+        requestFullscreen: function() {
+            return this.enterFullWindow();
+        },
+
+        enterFullScreen: function() {
+            return this.enterFullWindow();
+        },
+
+        reset: function() {
+            this.load();
+        },
+
+        seekable: function () {
+            if(!this.dmPlayer) {
+                return videojs.createTimeRange();
+            }
+
+            return videojs.createTimeRange(0, this.dmPlayer.duration);
+        },
+
+        seeking: function () {
+            return this.dmPlayer && this.dmPlayer.seeking;
+        },
+
+        src: function(source) {
+            if (typeof source !== 'undefined') {
+                return this.setSrc(source);
+            }
+
+            return this.source;
+        },
+
+        setSrc: function(source) {
+            if (typeof source === 'undefined') {
+                return;
+            }
+
+            this.source = source;
+            this.url = Dailymotion.parseUrl(source.src || source);
+
+            // Load the video if sdk is ready
+            if (Dailymotion.isSdkReady) {
+                this.load();
+            }
+            return this.source;
+        },
+
+        supportsFullScreen: function() {
+            return true;
+        },
+
+        volume: function() {
+            return this.dmPlayer ? this.dmPlayer.volume : 1;
+        },
+
+        setVolume: function(percentAsDecimal) {
+            if (!this.dmPlayer || !this.dmPlayer.setMuted || !this.dmPlayer.setVolume) {
+                return;
+            }
+
+            this.dmPlayer.setMuted(false);
+            this.dmPlayer.setVolume(percentAsDecimal);
+        },
+
+    });
+
+    Dailymotion.isSupported = function() {
+        return true;
+    };
+
+    Dailymotion.canPlaySource = function(e) {
+        return Dailymotion.canPlayType(e.type);
+    };
+
+    Dailymotion.canPlayType = function(e) {
+        return (e === 'video/dailymotion');
+    };
+
+    Dailymotion.parseUrl = function(url) {
+        var result = {};
+
+        var regex = /video\/[^?|^\/]*/;
+        var match = url.match(regex);
+
+        if (match && match[0]) {
+            result.video = match[0].replace('video/', '');
+        }
+
+        var regPlaylist = /playlist(=|\/)[^&]*/;
+        match = url.match(regPlaylist);
+
+        if(match && match[0]) {
+            result.playlist = match[0].replace(/playlist(=|\/)/, '');
+        }
+
+        return result;
+    };
+
+    function apiLoaded() {
+        Dailymotion.isSdkReady = true;
+
+        for (var i = 0; i < Dailymotion.sdkReadyQueue.length; ++i) {
+            Dailymotion.sdkReadyQueue[i].initDMPlayer();
+        }
+    }
+
+    function loadScript(src, callback) {
+        var loaded = false;
+        var tag = document.createElement('script');
+        var firstScriptTag = document.getElementsByTagName('script')[0];
+        if (!firstScriptTag) {
+            // when loaded in jest without jsdom setup it doesn't get any element.
+            // In jest it doesn't really make sense to do anything, because no one is watching dailymotion in jest
+            return;
+        }
+        firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
+        tag.onload = function () {
+            if (!loaded) {
+                loaded = true;
+                callback();
+            }
+        };
+        tag.onreadystatechange = function () {
+            if (!loaded && (this.readyState === 'complete' || this.readyState === 'loaded')) {
+                loaded = true;
+                callback();
+            }
+        };
+        tag.src = src;
+    }
+
+    function injectCss() {
+        var css = // iframe blocker to catch mouse events
+            '.vjs-dailymotion .vjs-iframe-blocker { display: none; }' +
+            '.vjs-dailymotion.vjs-user-inactive .vjs-iframe-blocker { display: block; }' +
+            '.vjs-dailymotion .vjs-poster { background-size: cover; }' +
+            '.vjs-dailymotion-mobile .vjs-big-play-button { display: none; }';
+
+        var head = document.head || document.getElementsByTagName('head')[0];
+
+        var style = document.createElement('style');
+        style.setAttribute('type', 'text/css');
+
+        if (style.styleSheet){
+            style.styleSheet.cssText = css;
+        } else {
+            style.appendChild(document.createTextNode(css));
+        }
+
+        head.appendChild(style);
+    }
+
+    Dailymotion.sdkReadyQueue = [];
+
+    if (typeof document !== 'undefined'){
+        loadScript('https://api.dmcdn.net/all.js', apiLoaded);
+        injectCss();
+    }
+
+    // Older versions of VJS5 doesn't have the registerTech function
+    if (typeof videojs.registerTech !== 'undefined') {
+        videojs.registerTech('Dailymotion', Dailymotion);
+    } else {
+        videojs.registerComponent('Dailymotion', Dailymotion);
+    }
+}));
diff --git a/js/libs/videojs/Vimeo.js b/js/libs/videojs/Vimeo.js
new file mode 100644 (file)
index 0000000..fc293ea
--- /dev/null
@@ -0,0 +1,2414 @@
+(function (global, factory) {
+    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
+        typeof define === 'function' && define.amd ? define(['video.js'], factory) :
+            (global = global || self, global['videojs-vimeo'] = factory(global.videojs));
+}(this, (function (videojs) { 'use strict';
+
+    videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
+
+    /*! @vimeo/player v2.10.0 | (c) 2019 Vimeo | MIT License | https://github.com/vimeo/player.js */
+    function _classCallCheck(instance, Constructor) {
+        if (!(instance instanceof Constructor)) {
+            throw new TypeError("Cannot call a class as a function");
+        }
+    }
+
+    function _defineProperties(target, props) {
+        for (var i = 0; i < props.length; i++) {
+            var descriptor = props[i];
+            descriptor.enumerable = descriptor.enumerable || false;
+            descriptor.configurable = true;
+            if ("value" in descriptor) descriptor.writable = true;
+            Object.defineProperty(target, descriptor.key, descriptor);
+        }
+    }
+
+    function _createClass(Constructor, protoProps, staticProps) {
+        if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+        if (staticProps) _defineProperties(Constructor, staticProps);
+        return Constructor;
+    }
+
+    /**
+     * @module lib/functions
+     */
+
+    /**
+     * Check to see this is a node environment.
+     * @type {Boolean}
+     */
+
+    /* global global */
+    var isNode = typeof global !== 'undefined' && {}.toString.call(global) === '[object global]';
+    /**
+     * Get the name of the method for a given getter or setter.
+     *
+     * @param {string} prop The name of the property.
+     * @param {string} type Either “get” or “set”.
+     * @return {string}
+     */
+
+    function getMethodName(prop, type) {
+        if (prop.indexOf(type.toLowerCase()) === 0) {
+            return prop;
+        }
+
+        return "".concat(type.toLowerCase()).concat(prop.substr(0, 1).toUpperCase()).concat(prop.substr(1));
+    }
+    /**
+     * Check to see if the object is a DOM Element.
+     *
+     * @param {*} element The object to check.
+     * @return {boolean}
+     */
+
+    function isDomElement(element) {
+        return Boolean(element && element.nodeType === 1 && 'nodeName' in element && element.ownerDocument && element.ownerDocument.defaultView);
+    }
+    /**
+     * Check to see whether the value is a number.
+     *
+     * @see http://dl.dropboxusercontent.com/u/35146/js/tests/isNumber.html
+     * @param {*} value The value to check.
+     * @param {boolean} integer Check if the value is an integer.
+     * @return {boolean}
+     */
+
+    function isInteger(value) {
+        // eslint-disable-next-line eqeqeq
+        return !isNaN(parseFloat(value)) && isFinite(value) && Math.floor(value) == value;
+    }
+    /**
+     * Check to see if the URL is a Vimeo url.
+     *
+     * @param {string} url The url string.
+     * @return {boolean}
+     */
+
+    function isVimeoUrl(url) {
+        return /^(https?:)?\/\/((player|www)\.)?vimeo\.com(?=$|\/)/.test(url);
+    }
+    /**
+     * Get the Vimeo URL from an element.
+     * The element must have either a data-vimeo-id or data-vimeo-url attribute.
+     *
+     * @param {object} oEmbedParameters The oEmbed parameters.
+     * @return {string}
+     */
+
+    function getVimeoUrl() {
+        var oEmbedParameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+        var id = oEmbedParameters.id;
+        var url = oEmbedParameters.url;
+        var idOrUrl = id || url;
+
+        if (!idOrUrl) {
+            throw new Error('An id or url must be passed, either in an options object or as a data-vimeo-id or data-vimeo-url attribute.');
+        }
+
+        if (isInteger(idOrUrl)) {
+            return "https://vimeo.com/".concat(idOrUrl);
+        }
+
+        if (isVimeoUrl(idOrUrl)) {
+            return idOrUrl.replace('http:', 'https:');
+        }
+
+        if (id) {
+            throw new TypeError("\u201C".concat(id, "\u201D is not a valid video id."));
+        }
+
+        throw new TypeError("\u201C".concat(idOrUrl, "\u201D is not a vimeo.com url."));
+    }
+
+    var arrayIndexOfSupport = typeof Array.prototype.indexOf !== 'undefined';
+    var postMessageSupport = typeof window !== 'undefined' && typeof window.postMessage !== 'undefined';
+
+    if (!isNode && (!arrayIndexOfSupport || !postMessageSupport)) {
+        throw new Error('Sorry, the Vimeo Player API is not available in this browser.');
+    }
+
+    var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+    function createCommonjsModule(fn, module) {
+        return module = { exports: {} }, fn(module, module.exports), module.exports;
+    }
+
+    /*!
+     * weakmap-polyfill v2.0.0 - ECMAScript6 WeakMap polyfill
+     * https://github.com/polygonplanet/weakmap-polyfill
+     * Copyright (c) 2015-2016 polygon planet <polygon.planet.aqua@gmail.com>
+     * @license MIT
+     */
+    (function (self) {
+
+        if (self.WeakMap) {
+            return;
+        }
+
+        var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+        var defineProperty = function (object, name, value) {
+            if (Object.defineProperty) {
+                Object.defineProperty(object, name, {
+                    configurable: true,
+                    writable: true,
+                    value: value
+                });
+            } else {
+                object[name] = value;
+            }
+        };
+
+        self.WeakMap = function () {
+            // ECMA-262 23.3 WeakMap Objects
+            function WeakMap() {
+                if (this === void 0) {
+                    throw new TypeError("Constructor WeakMap requires 'new'");
+                }
+
+                defineProperty(this, '_id', genId('_WeakMap')); // ECMA-262 23.3.1.1 WeakMap([iterable])
+
+                if (arguments.length > 0) {
+                    // Currently, WeakMap `iterable` argument is not supported
+                    throw new TypeError('WeakMap iterable is not supported');
+                }
+            } // ECMA-262 23.3.3.2 WeakMap.prototype.delete(key)
+
+
+            defineProperty(WeakMap.prototype, 'delete', function (key) {
+                checkInstance(this, 'delete');
+
+                if (!isObject(key)) {
+                    return false;
+                }
+
+                var entry = key[this._id];
+
+                if (entry && entry[0] === key) {
+                    delete key[this._id];
+                    return true;
+                }
+
+                return false;
+            }); // ECMA-262 23.3.3.3 WeakMap.prototype.get(key)
+
+            defineProperty(WeakMap.prototype, 'get', function (key) {
+                checkInstance(this, 'get');
+
+                if (!isObject(key)) {
+                    return void 0;
+                }
+
+                var entry = key[this._id];
+
+                if (entry && entry[0] === key) {
+                    return entry[1];
+                }
+
+                return void 0;
+            }); // ECMA-262 23.3.3.4 WeakMap.prototype.has(key)
+
+            defineProperty(WeakMap.prototype, 'has', function (key) {
+                checkInstance(this, 'has');
+
+                if (!isObject(key)) {
+                    return false;
+                }
+
+                var entry = key[this._id];
+
+                if (entry && entry[0] === key) {
+                    return true;
+                }
+
+                return false;
+            }); // ECMA-262 23.3.3.5 WeakMap.prototype.set(key, value)
+
+            defineProperty(WeakMap.prototype, 'set', function (key, value) {
+                checkInstance(this, 'set');
+
+                if (!isObject(key)) {
+                    throw new TypeError('Invalid value used as weak map key');
+                }
+
+                var entry = key[this._id];
+
+                if (entry && entry[0] === key) {
+                    entry[1] = value;
+                    return this;
+                }
+
+                defineProperty(key, this._id, [key, value]);
+                return this;
+            });
+
+            function checkInstance(x, methodName) {
+                if (!isObject(x) || !hasOwnProperty.call(x, '_id')) {
+                    throw new TypeError(methodName + ' method called on incompatible receiver ' + typeof x);
+                }
+            }
+
+            function genId(prefix) {
+                return prefix + '_' + rand() + '.' + rand();
+            }
+
+            function rand() {
+                return Math.random().toString().substring(2);
+            }
+
+            defineProperty(WeakMap, '_polyfill', true);
+            return WeakMap;
+        }();
+
+        function isObject(x) {
+            return Object(x) === x;
+        }
+    })(typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : typeof commonjsGlobal !== 'undefined' ? commonjsGlobal : commonjsGlobal);
+
+    var npo_src = createCommonjsModule(function (module) {
+        /*! Native Promise Only
+            v0.8.1 (c) Kyle Simpson
+            MIT License: http://getify.mit-license.org
+        */
+        (function UMD(name, context, definition) {
+            // special form of UMD for polyfilling across evironments
+            context[name] = context[name] || definition();
+
+            if (module.exports) {
+                module.exports = context[name];
+            }
+        })("Promise", typeof commonjsGlobal != "undefined" ? commonjsGlobal : commonjsGlobal, function DEF() {
+
+            var builtInProp,
+                cycle,
+                scheduling_queue,
+                ToString = Object.prototype.toString,
+                timer = typeof setImmediate != "undefined" ? function timer(fn) {
+                    return setImmediate(fn);
+                } : setTimeout; // dammit, IE8.
+
+            try {
+                Object.defineProperty({}, "x", {});
+
+                builtInProp = function builtInProp(obj, name, val, config) {
+                    return Object.defineProperty(obj, name, {
+                        value: val,
+                        writable: true,
+                        configurable: config !== false
+                    });
+                };
+            } catch (err) {
+                builtInProp = function builtInProp(obj, name, val) {
+                    obj[name] = val;
+                    return obj;
+                };
+            } // Note: using a queue instead of array for efficiency
+
+
+            scheduling_queue = function Queue() {
+                var first, last, item;
+
+                function Item(fn, self) {
+                    this.fn = fn;
+                    this.self = self;
+                    this.next = void 0;
+                }
+
+                return {
+                    add: function add(fn, self) {
+                        item = new Item(fn, self);
+
+                        if (last) {
+                            last.next = item;
+                        } else {
+                            first = item;
+                        }
+
+                        last = item;
+                        item = void 0;
+                    },
+                    drain: function drain() {
+                        var f = first;
+                        first = last = cycle = void 0;
+
+                        while (f) {
+                            f.fn.call(f.self);
+                            f = f.next;
+                        }
+                    }
+                };
+            }();
+
+            function schedule(fn, self) {
+                scheduling_queue.add(fn, self);
+
+                if (!cycle) {
+                    cycle = timer(scheduling_queue.drain);
+                }
+            } // promise duck typing
+
+
+            function isThenable(o) {
+                var _then,
+                    o_type = typeof o;
+
+                if (o != null && (o_type == "object" || o_type == "function")) {
+                    _then = o.then;
+                }
+
+                return typeof _then == "function" ? _then : false;
+            }
+
+            function notify() {
+                for (var i = 0; i < this.chain.length; i++) {
+                    notifyIsolated(this, this.state === 1 ? this.chain[i].success : this.chain[i].failure, this.chain[i]);
+                }
+
+                this.chain.length = 0;
+            } // NOTE: This is a separate function to isolate
+            // the `try..catch` so that other code can be
+            // optimized better
+
+
+            function notifyIsolated(self, cb, chain) {
+                var ret, _then;
+
+                try {
+                    if (cb === false) {
+                        chain.reject(self.msg);
+                    } else {
+                        if (cb === true) {
+                            ret = self.msg;
+                        } else {
+                            ret = cb.call(void 0, self.msg);
+                        }
+
+                        if (ret === chain.promise) {
+                            chain.reject(TypeError("Promise-chain cycle"));
+                        } else if (_then = isThenable(ret)) {
+                            _then.call(ret, chain.resolve, chain.reject);
+                        } else {
+                            chain.resolve(ret);
+                        }
+                    }
+                } catch (err) {
+                    chain.reject(err);
+                }
+            }
+
+            function resolve(msg) {
+                var _then,
+                    self = this; // already triggered?
+
+
+                if (self.triggered) {
+                    return;
+                }
+
+                self.triggered = true; // unwrap
+
+                if (self.def) {
+                    self = self.def;
+                }
+
+                try {
+                    if (_then = isThenable(msg)) {
+                        schedule(function () {
+                            var def_wrapper = new MakeDefWrapper(self);
+
+                            try {
+                                _then.call(msg, function $resolve$() {
+                                    resolve.apply(def_wrapper, arguments);
+                                }, function $reject$() {
+                                    reject.apply(def_wrapper, arguments);
+                                });
+                            } catch (err) {
+                                reject.call(def_wrapper, err);
+                            }
+                        });
+                    } else {
+                        self.msg = msg;
+                        self.state = 1;
+
+                        if (self.chain.length > 0) {
+                            schedule(notify, self);
+                        }
+                    }
+                } catch (err) {
+                    reject.call(new MakeDefWrapper(self), err);
+                }
+            }
+
+            function reject(msg) {
+                var self = this; // already triggered?
+
+                if (self.triggered) {
+                    return;
+                }
+
+                self.triggered = true; // unwrap
+
+                if (self.def) {
+                    self = self.def;
+                }
+
+                self.msg = msg;
+                self.state = 2;
+
+                if (self.chain.length > 0) {
+                    schedule(notify, self);
+                }
+            }
+
+            function iteratePromises(Constructor, arr, resolver, rejecter) {
+                for (var idx = 0; idx < arr.length; idx++) {
+                    (function IIFE(idx) {
+                        Constructor.resolve(arr[idx]).then(function $resolver$(msg) {
+                            resolver(idx, msg);
+                        }, rejecter);
+                    })(idx);
+                }
+            }
+
+            function MakeDefWrapper(self) {
+                this.def = self;
+                this.triggered = false;
+            }
+
+            function MakeDef(self) {
+                this.promise = self;
+                this.state = 0;
+                this.triggered = false;
+                this.chain = [];
+                this.msg = void 0;
+            }
+
+            function Promise(executor) {
+                if (typeof executor != "function") {
+                    throw TypeError("Not a function");
+                }
+
+                if (this.__NPO__ !== 0) {
+                    throw TypeError("Not a promise");
+                } // instance shadowing the inherited "brand"
+                // to signal an already "initialized" promise
+
+
+                this.__NPO__ = 1;
+                var def = new MakeDef(this);
+
+                this["then"] = function then(success, failure) {
+                    var o = {
+                        success: typeof success == "function" ? success : true,
+                        failure: typeof failure == "function" ? failure : false
+                    }; // Note: `then(..)` itself can be borrowed to be used against
+                    // a different promise constructor for making the chained promise,
+                    // by substituting a different `this` binding.
+
+                    o.promise = new this.constructor(function extractChain(resolve, reject) {
+                        if (typeof resolve != "function" || typeof reject != "function") {
+                            throw TypeError("Not a function");
+                        }
+
+                        o.resolve = resolve;
+                        o.reject = reject;
+                    });
+                    def.chain.push(o);
+
+                    if (def.state !== 0) {
+                        schedule(notify, def);
+                    }
+
+                    return o.promise;
+                };
+
+                this["catch"] = function $catch$(failure) {
+                    return this.then(void 0, failure);
+                };
+
+                try {
+                    executor.call(void 0, function publicResolve(msg) {
+                        resolve.call(def, msg);
+                    }, function publicReject(msg) {
+                        reject.call(def, msg);
+                    });
+                } catch (err) {
+                    reject.call(def, err);
+                }
+            }
+
+            var PromisePrototype = builtInProp({}, "constructor", Promise,
+                /*configurable=*/
+                false); // Note: Android 4 cannot use `Object.defineProperty(..)` here
+
+            Promise.prototype = PromisePrototype; // built-in "brand" to signal an "uninitialized" promise
+
+            builtInProp(PromisePrototype, "__NPO__", 0,
+                /*configurable=*/
+                false);
+            builtInProp(Promise, "resolve", function Promise$resolve(msg) {
+                var Constructor = this; // spec mandated checks
+                // note: best "isPromise" check that's practical for now
+
+                if (msg && typeof msg == "object" && msg.__NPO__ === 1) {
+                    return msg;
+                }
+
+                return new Constructor(function executor(resolve, reject) {
+                    if (typeof resolve != "function" || typeof reject != "function") {
+                        throw TypeError("Not a function");
+                    }
+
+                    resolve(msg);
+                });
+            });
+            builtInProp(Promise, "reject", function Promise$reject(msg) {
+                return new this(function executor(resolve, reject) {
+                    if (typeof resolve != "function" || typeof reject != "function") {
+                        throw TypeError("Not a function");
+                    }
+
+                    reject(msg);
+                });
+            });
+            builtInProp(Promise, "all", function Promise$all(arr) {
+                var Constructor = this; // spec mandated checks
+
+                if (ToString.call(arr) != "[object Array]") {
+                    return Constructor.reject(TypeError("Not an array"));
+                }
+
+                if (arr.length === 0) {
+                    return Constructor.resolve([]);
+                }
+
+                return new Constructor(function executor(resolve, reject) {
+                    if (typeof resolve != "function" || typeof reject != "function") {
+                        throw TypeError("Not a function");
+                    }
+
+                    var len = arr.length,
+                        msgs = Array(len),
+                        count = 0;
+                    iteratePromises(Constructor, arr, function resolver(idx, msg) {
+                        msgs[idx] = msg;
+
+                        if (++count === len) {
+                            resolve(msgs);
+                        }
+                    }, reject);
+                });
+            });
+            builtInProp(Promise, "race", function Promise$race(arr) {
+                var Constructor = this; // spec mandated checks
+
+                if (ToString.call(arr) != "[object Array]") {
+                    return Constructor.reject(TypeError("Not an array"));
+                }
+
+                return new Constructor(function executor(resolve, reject) {
+                    if (typeof resolve != "function" || typeof reject != "function") {
+                        throw TypeError("Not a function");
+                    }
+
+                    iteratePromises(Constructor, arr, function resolver(idx, msg) {
+                        resolve(msg);
+                    }, reject);
+                });
+            });
+            return Promise;
+        });
+    });
+
+    /**
+     * @module lib/callbacks
+     */
+    var callbackMap = new WeakMap();
+    /**
+     * Store a callback for a method or event for a player.
+     *
+     * @param {Player} player The player object.
+     * @param {string} name The method or event name.
+     * @param {(function(this:Player, *): void|{resolve: function, reject: function})} callback
+     *        The callback to call or an object with resolve and reject functions for a promise.
+     * @return {void}
+     */
+
+    function storeCallback(player, name, callback) {
+        var playerCallbacks = callbackMap.get(player.element) || {};
+
+        if (!(name in playerCallbacks)) {
+            playerCallbacks[name] = [];
+        }
+
+        playerCallbacks[name].push(callback);
+        callbackMap.set(player.element, playerCallbacks);
+    }
+    /**
+     * Get the callbacks for a player and event or method.
+     *
+     * @param {Player} player The player object.
+     * @param {string} name The method or event name
+     * @return {function[]}
+     */
+
+    function getCallbacks(player, name) {
+        var playerCallbacks = callbackMap.get(player.element) || {};
+        return playerCallbacks[name] || [];
+    }
+    /**
+     * Remove a stored callback for a method or event for a player.
+     *
+     * @param {Player} player The player object.
+     * @param {string} name The method or event name
+     * @param {function} [callback] The specific callback to remove.
+     * @return {boolean} Was this the last callback?
+     */
+
+    function removeCallback(player, name, callback) {
+        var playerCallbacks = callbackMap.get(player.element) || {};
+
+        if (!playerCallbacks[name]) {
+            return true;
+        } // If no callback is passed, remove all callbacks for the event
+
+
+        if (!callback) {
+            playerCallbacks[name] = [];
+            callbackMap.set(player.element, playerCallbacks);
+            return true;
+        }
+
+        var index = playerCallbacks[name].indexOf(callback);
+
+        if (index !== -1) {
+            playerCallbacks[name].splice(index, 1);
+        }
+
+        callbackMap.set(player.element, playerCallbacks);
+        return playerCallbacks[name] && playerCallbacks[name].length === 0;
+    }
+    /**
+     * Return the first stored callback for a player and event or method.
+     *
+     * @param {Player} player The player object.
+     * @param {string} name The method or event name.
+     * @return {function} The callback, or false if there were none
+     */
+
+    function shiftCallbacks(player, name) {
+        var playerCallbacks = getCallbacks(player, name);
+
+        if (playerCallbacks.length < 1) {
+            return false;
+        }
+
+        var callback = playerCallbacks.shift();
+        removeCallback(player, name, callback);
+        return callback;
+    }
+    /**
+     * Move callbacks associated with an element to another element.
+     *
+     * @param {HTMLElement} oldElement The old element.
+     * @param {HTMLElement} newElement The new element.
+     * @return {void}
+     */
+
+    function swapCallbacks(oldElement, newElement) {
+        var playerCallbacks = callbackMap.get(oldElement);
+        callbackMap.set(newElement, playerCallbacks);
+        callbackMap.delete(oldElement);
+    }
+
+    /**
+     * @module lib/embed
+     */
+    var oEmbedParameters = ['autopause', 'autoplay', 'background', 'byline', 'color', 'controls', 'dnt', 'height', 'id', 'loop', 'maxheight', 'maxwidth', 'muted', 'playsinline', 'portrait', 'responsive', 'speed', 'texttrack', 'title', 'transparent', 'url', 'width'];
+    /**
+     * Get the 'data-vimeo'-prefixed attributes from an element as an object.
+     *
+     * @param {HTMLElement} element The element.
+     * @param {Object} [defaults={}] The default values to use.
+     * @return {Object<string, string>}
+     */
+
+    function getOEmbedParameters(element) {
+        var defaults = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+        return oEmbedParameters.reduce(function (params, param) {
+            var value = element.getAttribute("data-vimeo-".concat(param));
+
+            if (value || value === '') {
+                params[param] = value === '' ? 1 : value;
+            }
+
+            return params;
+        }, defaults);
+    }
+    /**
+     * Create an embed from oEmbed data inside an element.
+     *
+     * @param {object} data The oEmbed data.
+     * @param {HTMLElement} element The element to put the iframe in.
+     * @return {HTMLIFrameElement} The iframe embed.
+     */
+
+    function createEmbed(_ref, element) {
+        var html = _ref.html;
+
+        if (!element) {
+            throw new TypeError('An element must be provided');
+        }
+
+        if (element.getAttribute('data-vimeo-initialized') !== null) {
+            return element.querySelector('iframe');
+        }
+
+        var div = document.createElement('div');
+        div.innerHTML = html;
+        element.appendChild(div.firstChild);
+        element.setAttribute('data-vimeo-initialized', 'true');
+        return element.querySelector('iframe');
+    }
+    /**
+     * Make an oEmbed call for the specified URL.
+     *
+     * @param {string} videoUrl The vimeo.com url for the video.
+     * @param {Object} [params] Parameters to pass to oEmbed.
+     * @param {HTMLElement} element The element.
+     * @return {Promise}
+     */
+
+    function getOEmbedData(videoUrl) {
+        var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+        var element = arguments.length > 2 ? arguments[2] : undefined;
+        return new Promise(function (resolve, reject) {
+            if (!isVimeoUrl(videoUrl)) {
+                throw new TypeError("\u201C".concat(videoUrl, "\u201D is not a vimeo.com url."));
+            }
+
+            var url = "https://vimeo.com/api/oembed.json?url=".concat(encodeURIComponent(videoUrl));
+
+            for (var param in params) {
+                if (params.hasOwnProperty(param)) {
+                    url += "&".concat(param, "=").concat(encodeURIComponent(params[param]));
+                }
+            }
+
+            var xhr = 'XDomainRequest' in window ? new XDomainRequest() : new XMLHttpRequest();
+            xhr.open('GET', url, true);
+
+            xhr.onload = function () {
+                if (xhr.status === 404) {
+                    reject(new Error("\u201C".concat(videoUrl, "\u201D was not found.")));
+                    return;
+                }
+
+                if (xhr.status === 403) {
+                    reject(new Error("\u201C".concat(videoUrl, "\u201D is not embeddable.")));
+                    return;
+                }
+
+                try {
+                    var json = JSON.parse(xhr.responseText); // Check api response for 403 on oembed
+
+                    if (json.domain_status_code === 403) {
+                        // We still want to create the embed to give users visual feedback
+                        createEmbed(json, element);
+                        reject(new Error("\u201C".concat(videoUrl, "\u201D is not embeddable.")));
+                        return;
+                    }
+
+                    resolve(json);
+                } catch (error) {
+                    reject(error);
+                }
+            };
+
+            xhr.onerror = function () {
+                var status = xhr.status ? " (".concat(xhr.status, ")") : '';
+                reject(new Error("There was an error fetching the embed code from Vimeo".concat(status, ".")));
+            };
+
+            xhr.send();
+        });
+    }
+    /**
+     * Initialize all embeds within a specific element
+     *
+     * @param {HTMLElement} [parent=document] The parent element.
+     * @return {void}
+     */
+
+    function initializeEmbeds() {
+        var parent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
+        var elements = [].slice.call(parent.querySelectorAll('[data-vimeo-id], [data-vimeo-url]'));
+
+        var handleError = function handleError(error) {
+            if ('console' in window && console.error) {
+                console.error("There was an error creating an embed: ".concat(error));
+            }
+        };
+
+        elements.forEach(function (element) {
+            try {
+                // Skip any that have data-vimeo-defer
+                if (element.getAttribute('data-vimeo-defer') !== null) {
+                    return;
+                }
+
+                var params = getOEmbedParameters(element);
+                var url = getVimeoUrl(params);
+                getOEmbedData(url, params, element).then(function (data) {
+                    return createEmbed(data, element);
+                }).catch(handleError);
+            } catch (error) {
+                handleError(error);
+            }
+        });
+    }
+    /**
+     * Resize embeds when messaged by the player.
+     *
+     * @param {HTMLElement} [parent=document] The parent element.
+     * @return {void}
+     */
+
+    function resizeEmbeds() {
+        var parent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document;
+
+        // Prevent execution if users include the player.js script multiple times.
+        if (window.VimeoPlayerResizeEmbeds_) {
+            return;
+        }
+
+        window.VimeoPlayerResizeEmbeds_ = true;
+
+        var onMessage = function onMessage(event) {
+            if (!isVimeoUrl(event.origin)) {
+                return;
+            } // 'spacechange' is fired only on embeds with cards
+
+
+            if (!event.data || event.data.event !== 'spacechange') {
+                return;
+            }
+
+            var iframes = parent.querySelectorAll('iframe');
+
+            for (var i = 0; i < iframes.length; i++) {
+                if (iframes[i].contentWindow !== event.source) {
+                    continue;
+                } // Change padding-bottom of the enclosing div to accommodate
+                // card carousel without distorting aspect ratio
+
+
+                var space = iframes[i].parentElement;
+                space.style.paddingBottom = "".concat(event.data.data[0].bottom, "px");
+                break;
+            }
+        };
+
+        if (window.addEventListener) {
+            window.addEventListener('message', onMessage, false);
+        } else if (window.attachEvent) {
+            window.attachEvent('onmessage', onMessage);
+        }
+    }
+
+    /**
+     * @module lib/postmessage
+     */
+    /**
+     * Parse a message received from postMessage.
+     *
+     * @param {*} data The data received from postMessage.
+     * @return {object}
+     */
+
+    function parseMessageData(data) {
+        if (typeof data === 'string') {
+            try {
+                data = JSON.parse(data);
+            } catch (error) {
+                // If the message cannot be parsed, throw the error as a warning
+                console.warn(error);
+                return {};
+            }
+        }
+
+        return data;
+    }
+    /**
+     * Post a message to the specified target.
+     *
+     * @param {Player} player The player object to use.
+     * @param {string} method The API method to call.
+     * @param {object} params The parameters to send to the player.
+     * @return {void}
+     */
+
+    function postMessage(player, method, params) {
+        if (!player.element.contentWindow || !player.element.contentWindow.postMessage) {
+            return;
+        }
+
+        var message = {
+            method: method
+        };
+
+        if (params !== undefined) {
+            message.value = params;
+        } // IE 8 and 9 do not support passing messages, so stringify them
+
+
+        var ieVersion = parseFloat(navigator.userAgent.toLowerCase().replace(/^.*msie (\d+).*$/, '$1'));
+
+        if (ieVersion >= 8 && ieVersion < 10) {
+            message = JSON.stringify(message);
+        }
+
+        player.element.contentWindow.postMessage(message, player.origin);
+    }
+    /**
+     * Parse the data received from a message event.
+     *
+     * @param {Player} player The player that received the message.
+     * @param {(Object|string)} data The message data. Strings will be parsed into JSON.
+     * @return {void}
+     */
+
+    function processData(player, data) {
+        data = parseMessageData(data);
+        var callbacks = [];
+        var param;
+
+        if (data.event) {
+            if (data.event === 'error') {
+                var promises = getCallbacks(player, data.data.method);
+                promises.forEach(function (promise) {
+                    var error = new Error(data.data.message);
+                    error.name = data.data.name;
+                    promise.reject(error);
+                    removeCallback(player, data.data.method, promise);
+                });
+            }
+
+            callbacks = getCallbacks(player, "event:".concat(data.event));
+            param = data.data;
+        } else if (data.method) {
+            var callback = shiftCallbacks(player, data.method);
+
+            if (callback) {
+                callbacks.push(callback);
+                param = data.value;
+            }
+        }
+
+        callbacks.forEach(function (callback) {
+            try {
+                if (typeof callback === 'function') {
+                    callback.call(player, param);
+                    return;
+                }
+
+                callback.resolve(param);
+            } catch (e) {// empty
+            }
+        });
+    }
+
+    var playerMap = new WeakMap();
+    var readyMap = new WeakMap();
+
+    var Player =
+        /*#__PURE__*/
+        function () {
+            /**
+             * Create a Player.
+             *
+             * @param {(HTMLIFrameElement|HTMLElement|string|jQuery)} element A reference to the Vimeo
+             *        player iframe, and id, or a jQuery object.
+             * @param {object} [options] oEmbed parameters to use when creating an embed in the element.
+             * @return {Player}
+             */
+            function Player(element) {
+                var _this = this;
+
+                var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+                _classCallCheck(this, Player);
+
+                /* global jQuery */
+                if (window.jQuery && element instanceof jQuery) {
+                    if (element.length > 1 && window.console && console.warn) {
+                        console.warn('A jQuery object with multiple elements was passed, using the first element.');
+                    }
+
+                    element = element[0];
+                } // Find an element by ID
+
+
+                if (typeof document !== 'undefined' && typeof element === 'string') {
+                    element = document.getElementById(element);
+                } // Not an element!
+
+
+                if (!isDomElement(element)) {
+                    throw new TypeError('You must pass either a valid element or a valid id.');
+                }
+
+                var win = element.ownerDocument.defaultView; // Already initialized an embed in this div, so grab the iframe
+
+                if (element.nodeName !== 'IFRAME') {
+                    var iframe = element.querySelector('iframe');
+
+                    if (iframe) {
+                        element = iframe;
+                    }
+                } // iframe url is not a Vimeo url
+
+
+                if (element.nodeName === 'IFRAME' && !isVimeoUrl(element.getAttribute('src') || '')) {
+                    throw new Error('The player element passed isn’t a Vimeo embed.');
+                } // If there is already a player object in the map, return that
+
+
+                if (playerMap.has(element)) {
+                    return playerMap.get(element);
+                }
+
+                this.element = element;
+                this.origin = '*';
+                var readyPromise = new npo_src(function (resolve, reject) {
+                    var onMessage = function onMessage(event) {
+                        if (!isVimeoUrl(event.origin) || _this.element.contentWindow !== event.source) {
+                            return;
+                        }
+
+                        if (_this.origin === '*') {
+                            _this.origin = event.origin;
+                        }
+
+                        var data = parseMessageData(event.data);
+                        var isError = data && data.event === 'error';
+                        var isReadyError = isError && data.data && data.data.method === 'ready';
+
+                        if (isReadyError) {
+                            var error = new Error(data.data.message);
+                            error.name = data.data.name;
+                            reject(error);
+                            return;
+                        }
+
+                        var isReadyEvent = data && data.event === 'ready';
+                        var isPingResponse = data && data.method === 'ping';
+
+                        if (isReadyEvent || isPingResponse) {
+                            _this.element.setAttribute('data-ready', 'true');
+
+                            resolve();
+                            return;
+                        }
+
+                        processData(_this, data);
+                    };
+
+                    if (win.addEventListener) {
+                        win.addEventListener('message', onMessage, false);
+                    } else if (win.attachEvent) {
+                        win.attachEvent('onmessage', onMessage);
+                    }
+
+                    if (_this.element.nodeName !== 'IFRAME') {
+                        var params = getOEmbedParameters(element, options);
+                        var url = getVimeoUrl(params);
+                        getOEmbedData(url, params, element).then(function (data) {
+                            var iframe = createEmbed(data, element); // Overwrite element with the new iframe,
+                            // but store reference to the original element
+
+                            _this.element = iframe;
+                            _this._originalElement = element;
+                            swapCallbacks(element, iframe);
+                            playerMap.set(_this.element, _this);
+                            return data;
+                        }).catch(reject);
+                    }
+                }); // Store a copy of this Player in the map
+
+                readyMap.set(this, readyPromise);
+                playerMap.set(this.element, this); // Send a ping to the iframe so the ready promise will be resolved if
+                // the player is already ready.
+
+                if (this.element.nodeName === 'IFRAME') {
+                    postMessage(this, 'ping');
+                }
+
+                return this;
+            }
+            /**
+             * Get a promise for a method.
+             *
+             * @param {string} name The API method to call.
+             * @param {Object} [args={}] Arguments to send via postMessage.
+             * @return {Promise}
+             */
+
+
+            _createClass(Player, [{
+                key: "callMethod",
+                value: function callMethod(name) {
+                    var _this2 = this;
+
+                    var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+                    return new npo_src(function (resolve, reject) {
+                        // We are storing the resolve/reject handlers to call later, so we
+                        // can’t return here.
+                        // eslint-disable-next-line promise/always-return
+                        return _this2.ready().then(function () {
+                            storeCallback(_this2, name, {
+                                resolve: resolve,
+                                reject: reject
+                            });
+                            postMessage(_this2, name, args);
+                        }).catch(reject);
+                    });
+                }
+                /**
+                 * Get a promise for the value of a player property.
+                 *
+                 * @param {string} name The property name
+                 * @return {Promise}
+                 */
+
+            }, {
+                key: "get",
+                value: function get(name) {
+                    var _this3 = this;
+
+                    return new npo_src(function (resolve, reject) {
+                        name = getMethodName(name, 'get'); // We are storing the resolve/reject handlers to call later, so we
+                        // can’t return here.
+                        // eslint-disable-next-line promise/always-return
+
+                        return _this3.ready().then(function () {
+                            storeCallback(_this3, name, {
+                                resolve: resolve,
+                                reject: reject
+                            });
+                            postMessage(_this3, name);
+                        }).catch(reject);
+                    });
+                }
+                /**
+                 * Get a promise for setting the value of a player property.
+                 *
+                 * @param {string} name The API method to call.
+                 * @param {mixed} value The value to set.
+                 * @return {Promise}
+                 */
+
+            }, {
+                key: "set",
+                value: function set(name, value) {
+                    var _this4 = this;
+
+                    return new npo_src(function (resolve, reject) {
+                        name = getMethodName(name, 'set');
+
+                        if (value === undefined || value === null) {
+                            throw new TypeError('There must be a value to set.');
+                        } // We are storing the resolve/reject handlers to call later, so we
+                        // can’t return here.
+                        // eslint-disable-next-line promise/always-return
+
+
+                        return _this4.ready().then(function () {
+                            storeCallback(_this4, name, {
+                                resolve: resolve,
+                                reject: reject
+                            });
+                            postMessage(_this4, name, value);
+                        }).catch(reject);
+                    });
+                }
+                /**
+                 * Add an event listener for the specified event. Will call the
+                 * callback with a single parameter, `data`, that contains the data for
+                 * that event.
+                 *
+                 * @param {string} eventName The name of the event.
+                 * @param {function(*)} callback The function to call when the event fires.
+                 * @return {void}
+                 */
+
+            }, {
+                key: "on",
+                value: function on(eventName, callback) {
+                    if (!eventName) {
+                        throw new TypeError('You must pass an event name.');
+                    }
+
+                    if (!callback) {
+                        throw new TypeError('You must pass a callback function.');
+                    }
+
+                    if (typeof callback !== 'function') {
+                        throw new TypeError('The callback must be a function.');
+                    }
+
+                    var callbacks = getCallbacks(this, "event:".concat(eventName));
+
+                    if (callbacks.length === 0) {
+                        this.callMethod('addEventListener', eventName).catch(function () {// Ignore the error. There will be an error event fired that
+                            // will trigger the error callback if they are listening.
+                        });
+                    }
+
+                    storeCallback(this, "event:".concat(eventName), callback);
+                }
+                /**
+                 * Remove an event listener for the specified event. Will remove all
+                 * listeners for that event if a `callback` isn’t passed, or only that
+                 * specific callback if it is passed.
+                 *
+                 * @param {string} eventName The name of the event.
+                 * @param {function} [callback] The specific callback to remove.
+                 * @return {void}
+                 */
+
+            }, {
+                key: "off",
+                value: function off(eventName, callback) {
+                    if (!eventName) {
+                        throw new TypeError('You must pass an event name.');
+                    }
+
+                    if (callback && typeof callback !== 'function') {
+                        throw new TypeError('The callback must be a function.');
+                    }
+
+                    var lastCallback = removeCallback(this, "event:".concat(eventName), callback); // If there are no callbacks left, remove the listener
+
+                    if (lastCallback) {
+                        this.callMethod('removeEventListener', eventName).catch(function (e) {// Ignore the error. There will be an error event fired that
+                            // will trigger the error callback if they are listening.
+                        });
+                    }
+                }
+                /**
+                 * A promise to load a new video.
+                 *
+                 * @promise LoadVideoPromise
+                 * @fulfill {number} The video with this id successfully loaded.
+                 * @reject {TypeError} The id was not a number.
+                 */
+
+                /**
+                 * Load a new video into this embed. The promise will be resolved if
+                 * the video is successfully loaded, or it will be rejected if it could
+                 * not be loaded.
+                 *
+                 * @param {number|object} options The id of the video or an object with embed options.
+                 * @return {LoadVideoPromise}
+                 */
+
+            }, {
+                key: "loadVideo",
+                value: function loadVideo(options) {
+                    return this.callMethod('loadVideo', options);
+                }
+                /**
+                 * A promise to perform an action when the Player is ready.
+                 *
+                 * @todo document errors
+                 * @promise LoadVideoPromise
+                 * @fulfill {void}
+                 */
+
+                /**
+                 * Trigger a function when the player iframe has initialized. You do not
+                 * need to wait for `ready` to trigger to begin adding event listeners
+                 * or calling other methods.
+                 *
+                 * @return {ReadyPromise}
+                 */
+
+            }, {
+                key: "ready",
+                value: function ready() {
+                    var readyPromise = readyMap.get(this) || new npo_src(function (resolve, reject) {
+                        reject(new Error('Unknown player. Probably unloaded.'));
+                    });
+                    return npo_src.resolve(readyPromise);
+                }
+                /**
+                 * A promise to add a cue point to the player.
+                 *
+                 * @promise AddCuePointPromise
+                 * @fulfill {string} The id of the cue point to use for removeCuePoint.
+                 * @reject {RangeError} the time was less than 0 or greater than the
+                 *         video’s duration.
+                 * @reject {UnsupportedError} Cue points are not supported with the current
+                 *         player or browser.
+                 */
+
+                /**
+                 * Add a cue point to the player.
+                 *
+                 * @param {number} time The time for the cue point.
+                 * @param {object} [data] Arbitrary data to be returned with the cue point.
+                 * @return {AddCuePointPromise}
+                 */
+
+            }, {
+                key: "addCuePoint",
+                value: function addCuePoint(time) {
+                    var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+                    return this.callMethod('addCuePoint', {
+                        time: time,
+                        data: data
+                    });
+                }
+                /**
+                 * A promise to remove a cue point from the player.
+                 *
+                 * @promise AddCuePointPromise
+                 * @fulfill {string} The id of the cue point that was removed.
+                 * @reject {InvalidCuePoint} The cue point with the specified id was not
+                 *         found.
+                 * @reject {UnsupportedError} Cue points are not supported with the current
+                 *         player or browser.
+                 */
+
+                /**
+                 * Remove a cue point from the video.
+                 *
+                 * @param {string} id The id of the cue point to remove.
+                 * @return {RemoveCuePointPromise}
+                 */
+
+            }, {
+                key: "removeCuePoint",
+                value: function removeCuePoint(id) {
+                    return this.callMethod('removeCuePoint', id);
+                }
+                /**
+                 * A representation of a text track on a video.
+                 *
+                 * @typedef {Object} VimeoTextTrack
+                 * @property {string} language The ISO language code.
+                 * @property {string} kind The kind of track it is (captions or subtitles).
+                 * @property {string} label The human‐readable label for the track.
+                 */
+
+                /**
+                 * A promise to enable a text track.
+                 *
+                 * @promise EnableTextTrackPromise
+                 * @fulfill {VimeoTextTrack} The text track that was enabled.
+                 * @reject {InvalidTrackLanguageError} No track was available with the
+                 *         specified language.
+                 * @reject {InvalidTrackError} No track was available with the specified
+                 *         language and kind.
+                 */
+
+                /**
+                 * Enable the text track with the specified language, and optionally the
+                 * specified kind (captions or subtitles).
+                 *
+                 * When set via the API, the track language will not change the viewer’s
+                 * stored preference.
+                 *
+                 * @param {string} language The two‐letter language code.
+                 * @param {string} [kind] The kind of track to enable (captions or subtitles).
+                 * @return {EnableTextTrackPromise}
+                 */
+
+            }, {
+                key: "enableTextTrack",
+                value: function enableTextTrack(language, kind) {
+                    if (!language) {
+                        throw new TypeError('You must pass a language.');
+                    }
+
+                    return this.callMethod('enableTextTrack', {
+                        language: language,
+                        kind: kind
+                    });
+                }
+                /**
+                 * A promise to disable the active text track.
+                 *
+                 * @promise DisableTextTrackPromise
+                 * @fulfill {void} The track was disabled.
+                 */
+
+                /**
+                 * Disable the currently-active text track.
+                 *
+                 * @return {DisableTextTrackPromise}
+                 */
+
+            }, {
+                key: "disableTextTrack",
+                value: function disableTextTrack() {
+                    return this.callMethod('disableTextTrack');
+                }
+                /**
+                 * A promise to pause the video.
+                 *
+                 * @promise PausePromise
+                 * @fulfill {void} The video was paused.
+                 */
+
+                /**
+                 * Pause the video if it’s playing.
+                 *
+                 * @return {PausePromise}
+                 */
+
+            }, {
+                key: "pause",
+                value: function pause() {
+                    return this.callMethod('pause');
+                }
+                /**
+                 * A promise to play the video.
+                 *
+                 * @promise PlayPromise
+                 * @fulfill {void} The video was played.
+                 */
+
+                /**
+                 * Play the video if it’s paused. **Note:** on iOS and some other
+                 * mobile devices, you cannot programmatically trigger play. Once the
+                 * viewer has tapped on the play button in the player, however, you
+                 * will be able to use this function.
+                 *
+                 * @return {PlayPromise}
+                 */
+
+            }, {
+                key: "play",
+                value: function play() {
+                    return this.callMethod('play');
+                }
+                /**
+                 * A promise to unload the video.
+                 *
+                 * @promise UnloadPromise
+                 * @fulfill {void} The video was unloaded.
+                 */
+
+                /**
+                 * Return the player to its initial state.
+                 *
+                 * @return {UnloadPromise}
+                 */
+
+            }, {
+                key: "unload",
+                value: function unload() {
+                    return this.callMethod('unload');
+                }
+                /**
+                 * Cleanup the player and remove it from the DOM
+                 *
+                 * It won't be usable and a new one should be constructed
+                 *  in order to do any operations.
+                 *
+                 * @return {Promise}
+                 */
+
+            }, {
+                key: "destroy",
+                value: function destroy() {
+                    var _this5 = this;
+
+                    return new npo_src(function (resolve) {
+                        readyMap.delete(_this5);
+                        playerMap.delete(_this5.element);
+
+                        if (_this5._originalElement) {
+                            playerMap.delete(_this5._originalElement);
+
+                            _this5._originalElement.removeAttribute('data-vimeo-initialized');
+                        }
+
+                        if (_this5.element && _this5.element.nodeName === 'IFRAME' && _this5.element.parentNode) {
+                            _this5.element.parentNode.removeChild(_this5.element);
+                        }
+
+                        resolve();
+                    });
+                }
+                /**
+                 * A promise to get the autopause behavior of the video.
+                 *
+                 * @promise GetAutopausePromise
+                 * @fulfill {boolean} Whether autopause is turned on or off.
+                 * @reject {UnsupportedError} Autopause is not supported with the current
+                 *         player or browser.
+                 */
+
+                /**
+                 * Get the autopause behavior for this player.
+                 *
+                 * @return {GetAutopausePromise}
+                 */
+
+            }, {
+                key: "getAutopause",
+                value: function getAutopause() {
+                    return this.get('autopause');
+                }
+                /**
+                 * A promise to set the autopause behavior of the video.
+                 *
+                 * @promise SetAutopausePromise
+                 * @fulfill {boolean} Whether autopause is turned on or off.
+                 * @reject {UnsupportedError} Autopause is not supported with the current
+                 *         player or browser.
+                 */
+
+                /**
+                 * Enable or disable the autopause behavior of this player.
+                 *
+                 * By default, when another video is played in the same browser, this
+                 * player will automatically pause. Unless you have a specific reason
+                 * for doing so, we recommend that you leave autopause set to the
+                 * default (`true`).
+                 *
+                 * @param {boolean} autopause
+                 * @return {SetAutopausePromise}
+                 */
+
+            }, {
+                key: "setAutopause",
+                value: function setAutopause(autopause) {
+                    return this.set('autopause', autopause);
+                }
+                /**
+                 * A promise to get the buffered property of the video.
+                 *
+                 * @promise GetBufferedPromise
+                 * @fulfill {Array} Buffered Timeranges converted to an Array.
+                 */
+
+                /**
+                 * Get the buffered property of the video.
+                 *
+                 * @return {GetBufferedPromise}
+                 */
+
+            }, {
+                key: "getBuffered",
+                value: function getBuffered() {
+                    return this.get('buffered');
+                }
+                /**
+                 * A promise to get the color of the player.
+                 *
+                 * @promise GetColorPromise
+                 * @fulfill {string} The hex color of the player.
+                 */
+
+                /**
+                 * Get the color for this player.
+                 *
+                 * @return {GetColorPromise}
+                 */
+
+            }, {
+                key: "getColor",
+                value: function getColor() {
+                    return this.get('color');
+                }
+                /**
+                 * A promise to set the color of the player.
+                 *
+                 * @promise SetColorPromise
+                 * @fulfill {string} The color was successfully set.
+                 * @reject {TypeError} The string was not a valid hex or rgb color.
+                 * @reject {ContrastError} The color was set, but the contrast is
+                 *         outside of the acceptable range.
+                 * @reject {EmbedSettingsError} The owner of the player has chosen to
+                 *         use a specific color.
+                 */
+
+                /**
+                 * Set the color of this player to a hex or rgb string. Setting the
+                 * color may fail if the owner of the video has set their embed
+                 * preferences to force a specific color.
+                 *
+                 * @param {string} color The hex or rgb color string to set.
+                 * @return {SetColorPromise}
+                 */
+
+            }, {
+                key: "setColor",
+                value: function setColor(color) {
+                    return this.set('color', color);
+                }
+                /**
+                 * A representation of a cue point.
+                 *
+                 * @typedef {Object} VimeoCuePoint
+                 * @property {number} time The time of the cue point.
+                 * @property {object} data The data passed when adding the cue point.
+                 * @property {string} id The unique id for use with removeCuePoint.
+                 */
+
+                /**
+                 * A promise to get the cue points of a video.
+                 *
+                 * @promise GetCuePointsPromise
+                 * @fulfill {VimeoCuePoint[]} The cue points added to the video.
+                 * @reject {UnsupportedError} Cue points are not supported with the current
+                 *         player or browser.
+                 */
+
+                /**
+                 * Get an array of the cue points added to the video.
+                 *
+                 * @return {GetCuePointsPromise}
+                 */
+
+            }, {
+                key: "getCuePoints",
+                value: function getCuePoints() {
+                    return this.get('cuePoints');
+                }
+                /**
+                 * A promise to get the current time of the video.
+                 *
+                 * @promise GetCurrentTimePromise
+                 * @fulfill {number} The current time in seconds.
+                 */
+
+                /**
+                 * Get the current playback position in seconds.
+                 *
+                 * @return {GetCurrentTimePromise}
+                 */
+
+            }, {
+                key: "getCurrentTime",
+                value: function getCurrentTime() {
+                    return this.get('currentTime');
+                }
+                /**
+                 * A promise to set the current time of the video.
+                 *
+                 * @promise SetCurrentTimePromise
+                 * @fulfill {number} The actual current time that was set.
+                 * @reject {RangeError} the time was less than 0 or greater than the
+                 *         video’s duration.
+                 */
+
+                /**
+                 * Set the current playback position in seconds. If the player was
+                 * paused, it will remain paused. Likewise, if the player was playing,
+                 * it will resume playing once the video has buffered.
+                 *
+                 * You can provide an accurate time and the player will attempt to seek
+                 * to as close to that time as possible. The exact time will be the
+                 * fulfilled value of the promise.
+                 *
+                 * @param {number} currentTime
+                 * @return {SetCurrentTimePromise}
+                 */
+
+            }, {
+                key: "setCurrentTime",
+                value: function setCurrentTime(currentTime) {
+                    return this.set('currentTime', currentTime);
+                }
+                /**
+                 * A promise to get the duration of the video.
+                 *
+                 * @promise GetDurationPromise
+                 * @fulfill {number} The duration in seconds.
+                 */
+
+                /**
+                 * Get the duration of the video in seconds. It will be rounded to the
+                 * nearest second before playback begins, and to the nearest thousandth
+                 * of a second after playback begins.
+                 *
+                 * @return {GetDurationPromise}
+                 */
+
+            }, {
+                key: "getDuration",
+                value: function getDuration() {
+                    return this.get('duration');
+                }
+                /**
+                 * A promise to get the ended state of the video.
+                 *
+                 * @promise GetEndedPromise
+                 * @fulfill {boolean} Whether or not the video has ended.
+                 */
+
+                /**
+                 * Get the ended state of the video. The video has ended if
+                 * `currentTime === duration`.
+                 *
+                 * @return {GetEndedPromise}
+                 */
+
+            }, {
+                key: "getEnded",
+                value: function getEnded() {
+                    return this.get('ended');
+                }
+                /**
+                 * A promise to get the loop state of the player.
+                 *
+                 * @promise GetLoopPromise
+                 * @fulfill {boolean} Whether or not the player is set to loop.
+                 */
+
+                /**
+                 * Get the loop state of the player.
+                 *
+                 * @return {GetLoopPromise}
+                 */
+
+            }, {
+                key: "getLoop",
+                value: function getLoop() {
+                    return this.get('loop');
+                }
+                /**
+                 * A promise to set the loop state of the player.
+                 *
+                 * @promise SetLoopPromise
+                 * @fulfill {boolean} The loop state that was set.
+                 */
+
+                /**
+                 * Set the loop state of the player. When set to `true`, the player
+                 * will start over immediately once playback ends.
+                 *
+                 * @param {boolean} loop
+                 * @return {SetLoopPromise}
+                 */
+
+            }, {
+                key: "setLoop",
+                value: function setLoop(loop) {
+                    return this.set('loop', loop);
+                }
+                /**
+                 * A promise to set the muted state of the player.
+                 *
+                 * @promise SetMutedPromise
+                 * @fulfill {boolean} The muted state that was set.
+                 */
+
+                /**
+                 * Set the muted state of the player. When set to `true`, the player
+                 * volume will be muted.
+                 *
+                 * @param {boolean} muted
+                 * @return {SetMutedPromise}
+                 */
+
+            }, {
+                key: "setMuted",
+                value: function setMuted(muted) {
+                    return this.set('muted', muted);
+                }
+                /**
+                 * A promise to get the muted state of the player.
+                 *
+                 * @promise GetMutedPromise
+                 * @fulfill {boolean} Whether or not the player is muted.
+                 */
+
+                /**
+                 * Get the muted state of the player.
+                 *
+                 * @return {GetMutedPromise}
+                 */
+
+            }, {
+                key: "getMuted",
+                value: function getMuted() {
+                    return this.get('muted');
+                }
+                /**
+                 * A promise to get the paused state of the player.
+                 *
+                 * @promise GetLoopPromise
+                 * @fulfill {boolean} Whether or not the video is paused.
+                 */
+
+                /**
+                 * Get the paused state of the player.
+                 *
+                 * @return {GetLoopPromise}
+                 */
+
+            }, {
+                key: "getPaused",
+                value: function getPaused() {
+                    return this.get('paused');
+                }
+                /**
+                 * A promise to get the playback rate of the player.
+                 *
+                 * @promise GetPlaybackRatePromise
+                 * @fulfill {number} The playback rate of the player on a scale from 0.5 to 2.
+                 */
+
+                /**
+                 * Get the playback rate of the player on a scale from `0.5` to `2`.
+                 *
+                 * @return {GetPlaybackRatePromise}
+                 */
+
+            }, {
+                key: "getPlaybackRate",
+                value: function getPlaybackRate() {
+                    return this.get('playbackRate');
+                }
+                /**
+                 * A promise to set the playbackrate of the player.
+                 *
+                 * @promise SetPlaybackRatePromise
+                 * @fulfill {number} The playback rate was set.
+                 * @reject {RangeError} The playback rate was less than 0.5 or greater than 2.
+                 */
+
+                /**
+                 * Set the playback rate of the player on a scale from `0.5` to `2`. When set
+                 * via the API, the playback rate will not be synchronized to other
+                 * players or stored as the viewer's preference.
+                 *
+                 * @param {number} playbackRate
+                 * @return {SetPlaybackRatePromise}
+                 */
+
+            }, {
+                key: "setPlaybackRate",
+                value: function setPlaybackRate(playbackRate) {
+                    return this.set('playbackRate', playbackRate);
+                }
+                /**
+                 * A promise to get the played property of the video.
+                 *
+                 * @promise GetPlayedPromise
+                 * @fulfill {Array} Played Timeranges converted to an Array.
+                 */
+
+                /**
+                 * Get the played property of the video.
+                 *
+                 * @return {GetPlayedPromise}
+                 */
+
+            }, {
+                key: "getPlayed",
+                value: function getPlayed() {
+                    return this.get('played');
+                }
+                /**
+                 * A promise to get the seekable property of the video.
+                 *
+                 * @promise GetSeekablePromise
+                 * @fulfill {Array} Seekable Timeranges converted to an Array.
+                 */
+
+                /**
+                 * Get the seekable property of the video.
+                 *
+                 * @return {GetSeekablePromise}
+                 */
+
+            }, {
+                key: "getSeekable",
+                value: function getSeekable() {
+                    return this.get('seekable');
+                }
+                /**
+                 * A promise to get the seeking property of the player.
+                 *
+                 * @promise GetSeekingPromise
+                 * @fulfill {boolean} Whether or not the player is currently seeking.
+                 */
+
+                /**
+                 * Get if the player is currently seeking.
+                 *
+                 * @return {GetSeekingPromise}
+                 */
+
+            }, {
+                key: "getSeeking",
+                value: function getSeeking() {
+                    return this.get('seeking');
+                }
+                /**
+                 * A promise to get the text tracks of a video.
+                 *
+                 * @promise GetTextTracksPromise
+                 * @fulfill {VimeoTextTrack[]} The text tracks associated with the video.
+                 */
+
+                /**
+                 * Get an array of the text tracks that exist for the video.
+                 *
+                 * @return {GetTextTracksPromise}
+                 */
+
+            }, {
+                key: "getTextTracks",
+                value: function getTextTracks() {
+                    return this.get('textTracks');
+                }
+                /**
+                 * A promise to get the embed code for the video.
+                 *
+                 * @promise GetVideoEmbedCodePromise
+                 * @fulfill {string} The `<iframe>` embed code for the video.
+                 */
+
+                /**
+                 * Get the `<iframe>` embed code for the video.
+                 *
+                 * @return {GetVideoEmbedCodePromise}
+                 */
+
+            }, {
+                key: "getVideoEmbedCode",
+                value: function getVideoEmbedCode() {
+                    return this.get('videoEmbedCode');
+                }
+                /**
+                 * A promise to get the id of the video.
+                 *
+                 * @promise GetVideoIdPromise
+                 * @fulfill {number} The id of the video.
+                 */
+
+                /**
+                 * Get the id of the video.
+                 *
+                 * @return {GetVideoIdPromise}
+                 */
+
+            }, {
+                key: "getVideoId",
+                value: function getVideoId() {
+                    return this.get('videoId');
+                }
+                /**
+                 * A promise to get the title of the video.
+                 *
+                 * @promise GetVideoTitlePromise
+                 * @fulfill {number} The title of the video.
+                 */
+
+                /**
+                 * Get the title of the video.
+                 *
+                 * @return {GetVideoTitlePromise}
+                 */
+
+            }, {
+                key: "getVideoTitle",
+                value: function getVideoTitle() {
+                    return this.get('videoTitle');
+                }
+                /**
+                 * A promise to get the native width of the video.
+                 *
+                 * @promise GetVideoWidthPromise
+                 * @fulfill {number} The native width of the video.
+                 */
+
+                /**
+                 * Get the native width of the currently‐playing video. The width of
+                 * the highest‐resolution available will be used before playback begins.
+                 *
+                 * @return {GetVideoWidthPromise}
+                 */
+
+            }, {
+                key: "getVideoWidth",
+                value: function getVideoWidth() {
+                    return this.get('videoWidth');
+                }
+                /**
+                 * A promise to get the native height of the video.
+                 *
+                 * @promise GetVideoHeightPromise
+                 * @fulfill {number} The native height of the video.
+                 */
+
+                /**
+                 * Get the native height of the currently‐playing video. The height of
+                 * the highest‐resolution available will be used before playback begins.
+                 *
+                 * @return {GetVideoHeightPromise}
+                 */
+
+            }, {
+                key: "getVideoHeight",
+                value: function getVideoHeight() {
+                    return this.get('videoHeight');
+                }
+                /**
+                 * A promise to get the vimeo.com url for the video.
+                 *
+                 * @promise GetVideoUrlPromise
+                 * @fulfill {number} The vimeo.com url for the video.
+                 * @reject {PrivacyError} The url isn’t available because of the video’s privacy setting.
+                 */
+
+                /**
+                 * Get the vimeo.com url for the video.
+                 *
+                 * @return {GetVideoUrlPromise}
+                 */
+
+            }, {
+                key: "getVideoUrl",
+                value: function getVideoUrl() {
+                    return this.get('videoUrl');
+                }
+                /**
+                 * A promise to get the volume level of the player.
+                 *
+                 * @promise GetVolumePromise
+                 * @fulfill {number} The volume level of the player on a scale from 0 to 1.
+                 */
+
+                /**
+                 * Get the current volume level of the player on a scale from `0` to `1`.
+                 *
+                 * Most mobile devices do not support an independent volume from the
+                 * system volume. In those cases, this method will always return `1`.
+                 *
+                 * @return {GetVolumePromise}
+                 */
+
+            }, {
+                key: "getVolume",
+                value: function getVolume() {
+                    return this.get('volume');
+                }
+                /**
+                 * A promise to set the volume level of the player.
+                 *
+                 * @promise SetVolumePromise
+                 * @fulfill {number} The volume was set.
+                 * @reject {RangeError} The volume was less than 0 or greater than 1.
+                 */
+
+                /**
+                 * Set the volume of the player on a scale from `0` to `1`. When set
+                 * via the API, the volume level will not be synchronized to other
+                 * players or stored as the viewer’s preference.
+                 *
+                 * Most mobile devices do not support setting the volume. An error will
+                 * *not* be triggered in that situation.
+                 *
+                 * @param {number} volume
+                 * @return {SetVolumePromise}
+                 */
+
+            }, {
+                key: "setVolume",
+                value: function setVolume(volume) {
+                    return this.set('volume', volume);
+                }
+            }]);
+
+            return Player;
+        }(); // Setup embed only if this is not a node environment
+
+
+    if (!isNode) {
+        initializeEmbeds();
+        resizeEmbeds();
+    }
+
+    let cssInjected = false;
+
+    // Since the iframe can't be touched using Vimeo's way of embedding,
+    // let's add a new styling rule to have the same style as `vjs-tech`
+    function injectCss() {
+        if (cssInjected) {
+            return;
+        }
+        cssInjected = true;
+        const css = `
+    .vjs-vimeo iframe {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+    }
+  `;
+        const head = document.head || document.getElementsByTagName('head')[0];
+
+        const style = document.createElement('style');
+
+        style.type = 'text/css';
+
+        if (style.styleSheet) {
+            style.styleSheet.cssText = css;
+        } else {
+            style.appendChild(document.createTextNode(css));
+        }
+
+        head.appendChild(style);
+    }
+
+    const Tech = videojs.getTech('Tech');
+
+    /**
+     * Vimeo - Wrapper for Video Player API
+     *
+     * @param {Object=} options Object of option names and values
+     * @param {Function=} ready Ready callback function
+     * @extends Tech
+     * @class Vimeo
+     */
+    class Vimeo extends Tech {
+        constructor(options, ready) {
+            super(options, ready);
+
+            injectCss();
+            this.setPoster(options.poster);
+            this.initVimeoPlayer();
+        }
+
+        initVimeoPlayer() {
+            const vimeoOptions = {
+                url: this.options_.source.src,
+                byline: false,
+                portrait: false,
+                title: false
+            };
+
+            if (this.options_.autoplay) {
+                vimeoOptions.autoplay = true;
+            }
+            if (this.options_.height) {
+                vimeoOptions.height = this.options_.height;
+            }
+            if (this.options_.width) {
+                vimeoOptions.width = this.options_.width;
+            }
+            if (this.options_.maxheight) {
+                vimeoOptions.maxheight = this.options_.maxheight;
+            }
+            if (this.options_.maxwidth) {
+                vimeoOptions.maxwidth = this.options_.maxwidth;
+            }
+            if (this.options_.loop) {
+                vimeoOptions.loop = this.options_.loop;
+            }
+            if (this.options_.color) {
+                vimeoOptions.color = this.options_.color.replace(/^#/, '');
+            }
+
+            this._player = new Player(this.el(), vimeoOptions);
+            this.initVimeoState();
+
+            ['play', 'pause', 'ended', 'timeupdate', 'progress', 'seeked'].forEach(e => {
+                this._player.on(e, (progress) => {
+                    if (this._vimeoState.progress.duration !== progress.duration) {
+                        this.trigger('durationchange');
+                    }
+                    this._vimeoState.progress = progress;
+                    this.trigger(e);
+                });
+            });
+
+            this._player.on('pause', () => (this._vimeoState.playing = false));
+            this._player.on('play', () => {
+                this._vimeoState.playing = true;
+                this._vimeoState.ended = false;
+            });
+            this._player.on('ended', () => {
+                this._vimeoState.playing = false;
+                this._vimeoState.ended = true;
+            });
+            this._player.on('volumechange', (v) => (this._vimeoState.volume = v));
+            this._player.on('error', e => this.trigger('error', e));
+
+            this.triggerReady();
+        }
+
+        initVimeoState() {
+            const state = this._vimeoState = {
+                ended: false,
+                playing: false,
+                volume: 0,
+                progress: {
+                    seconds: 0,
+                    percent: 0,
+                    duration: 0
+                }
+            };
+
+            this._player.getCurrentTime().then(time => (state.progress.seconds = time));
+            this._player.getDuration().then(time => (state.progress.duration = time));
+            this._player.getPaused().then(paused => (state.playing = !paused));
+            this._player.getVolume().then(volume => (state.volume = volume));
+        }
+
+        createEl() {
+            const div = videojs.dom.createEl('div', {
+                id: this.options_.techId
+            });
+
+            div.style.cssText = 'width:100%;height:100%;top:0;left:0;position:absolute';
+            div.className = 'vjs-vimeo';
+
+            return div;
+        }
+
+        controls() {
+            return true;
+        }
+
+        supportsFullScreen() {
+            return true;
+        }
+
+        src() {
+            return this.options_.source;
+        }
+
+        currentSrc() {
+            return this.options_.source.src;
+        }
+
+        currentTime() {
+            return this._vimeoState.progress.seconds;
+        }
+
+        setCurrentTime(time) {
+            this._player.setCurrentTime(time);
+        }
+
+        volume() {
+            return this._vimeoState.volume;
+        }
+
+        setVolume(volume) {
+            return this._player.setVolume(volume);
+        }
+
+        duration() {
+            return this._vimeoState.progress.duration;
+        }
+
+        buffered() {
+            const progress = this._vimeoState.progress;
+
+            return videojs.createTimeRange(0, progress.percent * progress.duration);
+        }
+
+        paused() {
+            return !this._vimeoState.playing;
+        }
+
+        pause() {
+            this._player.pause();
+        }
+
+        play() {
+            this._player.play();
+        }
+
+        muted() {
+            return this._vimeoState.volume === 0;
+        }
+
+        ended() {
+            return this._vimeoState.ended;
+        }
+
+        playbackRate() {
+            return 1;
+        }
+
+    }
+
+    Vimeo.prototype.featuresTimeupdateEvents = true;
+
+    Vimeo.isSupported = function () {
+        return true;
+    };
+
+    // Add Source Handler pattern functions to this tech
+    Tech.withSourceHandlers(Vimeo);
+
+    Vimeo.nativeSourceHandler = {
+    };
+
+    /**
+     * Check if Vimeo can play the given videotype
+     *
+     * @param  {string} source    The mimetype to check
+     * @return {string}         'maybe', or '' (empty string)
+     */
+    Vimeo.nativeSourceHandler.canPlayType = function (source) {
+        if (source === 'video/vimeo') {
+            return 'maybe';
+        }
+
+        return '';
+    };
+
+    /*
+     * Check Vimeo can handle the source natively
+     *
+     * @param  {Object} source  The source object
+     * @return {String}         'maybe', or '' (empty string)
+     * @note: Copied over from YouTube — not sure this is relevant
+     */
+    Vimeo.nativeSourceHandler.canHandleSource = function (source) {
+        if (source.type) {
+            return Vimeo.nativeSourceHandler.canPlayType(source.type);
+        } else if (source.src) {
+            return Vimeo.nativeSourceHandler.canPlayType(source.src);
+        }
+
+        return '';
+    };
+
+    // @note: Copied over from YouTube — not sure this is relevant
+    Vimeo.nativeSourceHandler.handleSource = function (source, tech) {
+        tech.src(source.src);
+    };
+
+    // @note: Copied over from YouTube — not sure this is relevant
+    Vimeo.nativeSourceHandler.dispose = function () { };
+
+    Vimeo.registerSourceHandler(Vimeo.nativeSourceHandler);
+
+    // Older versions of VJS5 doesn't have the registerTech function
+    if (typeof videojs.registerTech !== 'undefined') {
+        videojs.registerTech('Vimeo', Vimeo);
+    } else {
+        videojs.registerComponent('Vimeo', Vimeo);
+    }
+
+    // Include the version number.
+    Vimeo.VERSION = '0.0.1';
+
+    return Vimeo;
+
+})));