aboutsummaryrefslogtreecommitdiffstats
path: root/dist/plyr.js.map
blob: 39c3610e8b0447c8acff25629f54574cb750ff3b (plain)
1
{"version":3,"sources":["src/js/types.js","src/js/support.js","src/js/defaults.js","src/js/utils.js","src/js/console.js","src/js/fullscreen.js","src/js/storage.js","src/js/plugins/ads.js","src/js/listeners.js","src/js/ui.js","src/js/controls.js","src/js/captions.js","src/js/plugins/youtube.js","src/js/plugins/vimeo.js","src/js/media.js","src/js/source.js","src/js/plyr.js"],"names":["range","providers","types","defaults","window","navigator","language","split","utils","input","this","instanceof","Plyr","getConstructor","Object","Number","isNaN","String","Boolean","Function","nullOrUndefined","Array","isArray","WeakMap","NodeList","Element","Text","Event","TextTrackCue","VTTCue","TextTrack","string","kind","test","array","nodeList","length","object","keys","constructor","document","documentMode","documentElement","style","userAgent","platform","url","Promise","resolve","reject","request","XMLHttpRequest","addEventListener","JSON","parse","responseText","e","Error","statusText","open","send","callback","error","current","querySelector","callbacks","push","element","createElement","errors","is","function","forEach","cb","call","event","err","src","first","getElementsByTagName","parentNode","insertBefore","id","hasId","querySelectorAll","container","toggleHidden","setAttribute","support","storage","cached","localStorage","getItem","data","content","fetch","then","empty","result","setItem","stringify","catch","updateSprite","innerHTML","body","childNodes","prefix","Math","floor","random","self","top","elements","wrapper","targets","from","reverse","index","child","cloneNode","parent","sibling","nextSibling","appendChild","type","attributes","text","setAttributes","textContent","target","removeElement","removeChild","lastChild","newChild","oldChild","replaceChild","key","sel","existingAttributes","existing","selector","s","trim","className","replace","parts","value","charAt","class","toggle","contains","classList","removeAttribute","prototype","matches","webkitMatchesSelector","mozMatchesSelector","msMatchesSelector","includes","controls","getElement","config","selectors","buttons","getElements","play","pause","restart","rewind","forward","mute","pip","airplay","settings","captions","fullscreen","progress","inputs","seek","volume","display","buffer","duration","currentTime","seekTooltip","classNames","tooltip","debug","warn","toggleNativeControls","focused","activeElement","focusable","last","trap","keyCode","getFocusElement","shiftKey","focus","preventDefault","on","off","passive","capture","Node","toggleListener","events","options","boolean","passiveListeners","bubbles","detail","CustomEvent","assign","plyr","dispatchEvent","pressed","getAttribute","state","max","toFixed","parseInt","time","displayHours","inverted","number","formatTime","format","slice","hours","getHours","mins","getMinutes","secs","getSeconds","sources","source","shift","extend","youtube","vimeo","match","RegExp","$2","parser","href","search","startsWith","parseUrl","indexOf","reduce","params","hash","val","decodeURIComponent","map","encodeURIComponent","join","fragment","createDocumentFragment","firstChild","innerText","width","height","ratio","getRatio","w","h","find","undefined","setTimeout","offsetHeight","provider","inline","api","ui","browser","getBrowser","playsInline","isIPhone","video","rangeInput","audio","webkitSetPresentationMode","WebKitPlaybackTargetAvailabilityEvent","media","isHTML5","canPlayType","isVideo","isAudio","supported","defineProperty","transitionEndEvent","matchMedia","noop","Console","enabled","console","log","bind","onChange","button","player","toggleState","active","isIos","trapFocus","toggleFallback","scrollPosition","scrollX","scrollY","scrollTo","x","y","overflow","toggleClass","fallback","Fullscreen","stopPropagation","update","native","iosNative","playing","webkitEnterFullscreen","requestFullScreen","webkitExitFullscreen","cancelFullScreen","exit","enter","inFrame","fullscreenElement","hasClass","fullscreenEnabled","webkitFullscreenEnabled","mozFullScreenEnabled","msFullscreenEnabled","some","pre","msExitFullscreen","Storage","store","json","get","removeItem","Ads","ads","initialized","blocked","tag","google","ready","loadScript","urls","googleIMA","manager","loader","cuePoints","safetyTimer","countdownTimer","listeners","startSafetyTimer","loaderPromise","managerPromise","clearSafetyTimer","setupIMA","ima","setVpaidMode","ImaSdkSettings","VpaidMode","ENABLED","setLocale","displayContainer","AdDisplayContainer","requestAds","AdsLoader","AdsManagerLoadedEvent","Type","ADS_MANAGER_LOADED","_this3","onAdsManagerLoaded","AdErrorEvent","AD_ERROR","onAdError","AdsRequest","adTagUrl","base","buildUrlParams","linearAdSlotWidth","offsetWidth","linearAdSlotHeight","nonLinearAdSlotWidth","nonLinearAdSlotHeight","forceNonLinearFullSlot","handleEventListeners","clearInterval","setInterval","_this4","getRemainingTime","label","i18n","advertisment","adsManagerLoadedEvent","AdsRenderingSettings","restoreCustomPlaybackStateOnAdBreakComplete","enablePreloading","getAdsManager","getCuePoints","cuePoint","seekElement","_this5","cuePercentage","cue","cues","left","toString","setVolume","AdEvent","onAdEvent","ad","getAd","_this6","LOADED","pollCountdown","isLinear","ALL_ADS_COMPLETED","loadAds","CONTENT_PAUSE_REQUESTED","pauseContent","CONTENT_RESUME_REQUESTED","resumeContent","STARTED","MIDPOINT","COMPLETE","IMPRESSION","CLICK","cancel","contentComplete","_this7","seekedTime","discardAdBreak","splice","resize","ViewMode","NORMAL","initialize","_this8","init","start","adError","_this9","destroy","handleKey","code","which","getKeyCode","repeat","altKey","ctrlKey","metaKey","_this","editable","togglePlay","increaseVolume","decreaseVolume","muted","toggleCaptions","loop","keyboard","global","tabFocus","hideControls","toggleControls","timeUpdate","durationUpdate","_this2","hasAudio","showPosterOnEnd","load","updateProgress","updateVolume","checkPlaying","checkLoading","clickToPlay","touch","paused","ended","disableContextMenu","set","updateSetting","speed","quality","concat","inputEvent","isIE","proxy","handlerKey","defaultHandler","customHandler","defaultPrevented","toggleMenu","form","parseFloat","showTab","toggleInvert","invertTime","isWebkit","updateRangeFill","updateSeekTooltip","hover","webkitDirectionInvertedFromDevice","direction","deltaY","deltaX","uiSupported","inject","setup","setTitle","title","isEmbed","iframe","frameTitle","stopped","loading","timers","failed","networkState","setRange","nodeValue","buffered","getPercentage","end","setProgress","invert","updateTimeDisplay","seeking","hasDuration","displayDuration","setProperty","iconUrl","svg4everybody","getIconUrl","iconPath","absolute","iconPrefix","icon","createElementNS","use","path","setAttributeNS","attr","hidden","badge","menu","buttonType","labelPressed","iconPressed","control","createIcon","createLabel","getAttributesFromSelector","suffix","played","toLowerCase","list","checked","item","radio","faux","aria-hidden","insertAdjacentHTML","tooltips","percent","clientRect","getBoundingClientRect","visible","pageX","setting","tab","tabs","pane","panes","filter","isYouTube","toggleTab","emptyElement","createMenuItem","getLabel","createBadge","getBadge","getLanguage","default","textTracks","getTracks","none","currentTrack","getCurrentTrack","track","disabled","hasTracks","tracks","toUpperCase","unshift","show","isMenuItem","isButton","clone","position","opacity","name","scrollWidth","scrollHeight","getElementById","transitions","reducedMotion","size","getTabSize","restore","propertyName","createButton","createRange","createProgress","createTime","inner","home","back","setSpeedMenu","loadSprite","seekTime","create","findElements","repaint","labels","stored","setCaptionsMenu","insertAfter","setLanguage","setCue","mode","activeCues","isVimeo","embed","enableTextTrack","setText","getCueAsHTML","caption","setAspectRatio","YT","Player","onYouTubeReadyCallbacks","onYouTubeIframeAPIReady","videoId","getVideoData","items","snippet","paddingBottom","currentId","parseYouTubeId","generateId","replaceElement","autoplay","location","message","instance","getPlaybackQuality","playbackRate","getPlaybackRate","getTitle","playVideo","pauseVideo","stop","stopVideo","getDuration","getCurrentTime","seekTo","setPlaybackRate","setPlaybackQuality","getVideoUrl","getAvailablePlaybackRates","buffering","getVideoLoadedFraction","lastBuffered","build","setQualityMenu","getAvailableQualityLevels","Vimeo","padding","offset","transform","parseVimeoId","setCurrentTime","selected","setLoop","currentSrc","all","getVideoWidth","getVideoHeight","getAspectRatio","dimensions","setAutopause","autopause","getVideoTitle","getTextTracks","stripHTML","seconds","isTouch","wrap","blankVideo","insertElement","attribute","cancelRequests","html5","check","crossorigin","poster","addStyleHook","insertElements","jQuery","original","tagName","getProviderByUrl","getUrlParams","truthy","playsinline","hasAttribute","step","webkitShowPlaybackTargetPicker","delay","isEnterFullscreen","noTransition","clearTimeout","soft","done","unload","mime","readyState","targetTime","fauxDuration","realDuration","mozHasAudio","webkitAudioDecodedByteCount","audioTracks","change","states","webkitPresentationMode"],"mappings":"uLAIA,IC2IcA,ED3IDC,SACF,gBACE,gBACF,SAGEC,SACF,cACA,SERLC,YAEO,QAGF,UAGA,YAGG,aAGC,WAGD,UAGF,SACD,WAGG,sBAIO,cAGL,gBAGE,QAGP,oBAGM,gBAGC,mBAGG,sBAGG,cAGR,aACA,eACH,wDAGG,wDAIC,mBAEL,SACA,SACA,SACA,QACA,QACA,SACA,QACA,OACA,0BAMI,mBAOE,WAEN,GACA,IACA,EACA,KACA,IACA,KACA,uBAMK,UACD,uBAKE,QACJ,qBAKE,WACEC,OAAOC,UAAUC,SAASC,MAAM,KAAK,yBAKtC,YACC,aACC,qBAKF,MACJ,kBAKL,aACA,OACA,WACA,eACA,OACA,SACA,WACA,WACA,MACA,UACA,wBAGA,WACA,UACA,uBAKS,iBACD,8BACF,aACC,gBACE,+BACH,cACE,kBACE,uBACG,wBACH,kBACF,cACF,cACE,wBACQ,kCACC,mCACA,kCACD,6BACJ,8BACF,oBACA,iBACH,gBACE,eACH,aACC,YACF,UACA,YACE,aACD,gBACI,wBACI,uBAML,uDAGA,qDAGA,uEAMH,UACA,WACC,aACE,YACD,aACC,UACH,YACE,cACE,gBACE,SACP,aACI,WACF,aACE,UACH,cACI,sBAQV,WACA,UACA,UACA,UACA,UACA,iBACA,YACA,aACA,iBACA,aACA,eACA,OACA,QACA,QACA,UACA,SACA,UACA,aACA,8BAIA,iBACA,kBACA,mBACA,iBACA,iBACA,gBACA,sBAIA,gBACA,+BAIA,kBACA,kBACA,YACA,cACA,cACA,iBACA,gBACA,gCAMU,uDACC,4BAEI,aACF,0BAEL,4BAEE,2BACC,8BACE,+BACD,+BACC,kCACH,8BACI,oCACE,+BACP,4BACI,iCACC,8BACJ,mCAGA,4BACE,6BACD,+BACG,iCACD,8CAGI,gCACH,+BACF,iCACA,+BACF,+BACE,mCAEF,2BACA,gCAEG,oDAMN,4BACA,wBACF,oBACI,qBACH,qBACI,oBACD,wBACA,wBACA,sBACF,wBACA,sBACE,qBACH,oBACE,6BACM,4BACP,uBACE,6BACI,6BACC,kC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","file":"plyr.js","sourcesContent":["// ==========================================================================\n// Plyr supported types and providers\n// ==========================================================================\n\nexport const providers = {\n    html5: 'html5',\n    youtube: 'youtube',\n    vimeo: 'vimeo',\n};\n\nexport const types = {\n    audio: 'audio',\n    video: 'video',\n};\n\nexport default { providers, types };\n","// ==========================================================================\n// Plyr support checks\n// ==========================================================================\n\nimport utils from './utils';\n\n// Check for feature support\nconst support = {\n    // Basic support\n    audio: 'canPlayType' in document.createElement('audio'),\n    video: 'canPlayType' in document.createElement('video'),\n\n    // Check for support\n    // Basic functionality vs full UI\n    check(type, provider, inline) {\n        let api = false;\n        let ui = false;\n        const browser = utils.getBrowser();\n        const playsInline = browser.isIPhone && inline && support.inline;\n\n        switch (`${provider}:${type}`) {\n            case 'html5:video':\n                api = support.video;\n                ui = api && support.rangeInput && (!browser.isIPhone || playsInline);\n                break;\n\n            case 'html5:audio':\n                api = support.audio;\n                ui = api && support.rangeInput;\n                break;\n\n            case 'youtube:video':\n                api = true;\n                ui = support.rangeInput && (!browser.isIPhone || playsInline);\n                break;\n\n            case 'vimeo:video':\n                api = true;\n                ui = support.rangeInput && !browser.isIPhone;\n                break;\n\n            default:\n                api = support.audio && support.video;\n                ui = api && support.rangeInput;\n        }\n\n        return {\n            api,\n            ui,\n        };\n    },\n\n    // Picture-in-picture support\n    // Safari only currently\n    pip: (() => {\n        const browser = utils.getBrowser();\n        return !browser.isIPhone && utils.is.function(utils.createElement('video').webkitSetPresentationMode);\n    })(),\n\n    // Airplay support\n    // Safari only currently\n    airplay: utils.is.function(window.WebKitPlaybackTargetAvailabilityEvent),\n\n    // Inline playback support\n    // https://webkit.org/blog/6784/new-video-policies-for-ios/\n    inline: 'playsInline' in document.createElement('video'),\n\n    // Check for mime type support against a player instance\n    // Credits: http://diveintohtml5.info/everything.html\n    // Related: http://www.leanbackplayer.com/test/h5mt.html\n    mime(type) {\n        const { media } = this;\n\n        try {\n            // Bail if no checking function\n            if (!this.isHTML5 || !utils.is.function(media.canPlayType)) {\n                return false;\n            }\n\n            // Type specific checks\n            if (this.isVideo) {\n                switch (type) {\n                    case 'video/webm':\n                        return media.canPlayType('video/webm; codecs=\"vp8, vorbis\"').replace(/no/, '');\n\n                    case 'video/mp4':\n                        return media.canPlayType('video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"').replace(/no/, '');\n\n                    case 'video/ogg':\n                        return media.canPlayType('video/ogg; codecs=\"theora\"').replace(/no/, '');\n\n                    default:\n                        return false;\n                }\n            } else if (this.isAudio) {\n                switch (type) {\n                    case 'audio/mpeg':\n                        return media.canPlayType('audio/mpeg;').replace(/no/, '');\n\n                    case 'audio/ogg':\n                        return media.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/no/, '');\n\n                    case 'audio/wav':\n                        return media.canPlayType('audio/wav; codecs=\"1\"').replace(/no/, '');\n\n                    default:\n                        return false;\n                }\n            }\n        } catch (e) {\n            return false;\n        }\n\n        // If we got this far, we're stuffed\n        return false;\n    },\n\n    // Check for textTracks support\n    textTracks: 'textTracks' in document.createElement('video'),\n\n    // Check for passive event listener support\n    // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md\n    // https://www.youtube.com/watch?v=NPM6172J22g\n    passiveListeners: (() => {\n        // Test via a getter in the options object to see if the passive property is accessed\n        let supported = false;\n        try {\n            const options = Object.defineProperty({}, 'passive', {\n                get() {\n                    supported = true;\n                    return null;\n                },\n            });\n            window.addEventListener('test', null, options);\n        } catch (e) {\n            // Do nothing\n        }\n\n        return supported;\n    })(),\n\n    // <input type=\"range\"> Sliders\n    rangeInput: (() => {\n        const range = document.createElement('input');\n        range.type = 'range';\n        return range.type === 'range';\n    })(),\n\n    // Touch\n    // Remember a device can be moust + touch enabled\n    touch: 'ontouchstart' in document.documentElement,\n\n    // Detect transitions support\n    transitions: utils.transitionEndEvent !== false,\n\n    // Reduced motion iOS & MacOS setting\n    // https://webkit.org/blog/7551/responsive-design-for-motion/\n    reducedMotion: 'matchMedia' in window && window.matchMedia('(prefers-reduced-motion)').matches,\n};\n\nexport default support;\n","// ==========================================================================\n// Plyr default config\n// ==========================================================================\n\nconst defaults = {\n    // Disable\n    enabled: true,\n\n    // Custom media title\n    title: '',\n\n    // Logging to console\n    debug: false,\n\n    // Auto play (if supported)\n    autoplay: false,\n\n    // Only allow one media playing at once (vimeo only)\n    autopause: true,\n\n    // Default time to skip when rewind/fast forward\n    seekTime: 10,\n\n    // Default volume\n    volume: 1,\n    muted: false,\n\n    // Pass a custom duration\n    duration: null,\n\n    // Display the media duration on load in the current time position\n    // If you have opted to display both duration and currentTime, this is ignored\n    displayDuration: true,\n\n    // Invert the current time to be a countdown\n    invertTime: true,\n\n    // Clicking the currentTime inverts it's value to show time left rather than elapsed\n    toggleInvert: true,\n\n    // Aspect ratio (for embeds)\n    ratio: '16:9',\n\n    // Click video container to play/pause\n    clickToPlay: true,\n\n    // Auto hide the controls\n    hideControls: true,\n\n    // Revert to poster on finish (HTML5 - will cause reload)\n    showPosterOnEnd: false,\n\n    // Disable the standard context menu\n    disableContextMenu: true,\n\n    // Sprite (for icons)\n    loadSprite: true,\n    iconPrefix: 'plyr',\n    iconUrl: 'https://cdn.plyr.io/3.0.0-beta.12/plyr.svg',\n\n    // Blank video (used to prevent errors on source change)\n    blankVideo: 'https://cdn.plyr.io/static/blank.mp4',\n\n    // Quality default\n    quality: {\n        default: 'default',\n        options: [\n            'hd2160',\n            'hd1440',\n            'hd1080',\n            'hd720',\n            'large',\n            'medium',\n            'small',\n            'tiny',\n            'default',\n        ],\n    },\n\n    // Set loops\n    loop: {\n        active: false,\n        // start: null,\n        // end: null,\n    },\n\n    // Speed default and options to display\n    speed: {\n        selected: 1,\n        options: [\n            0.5,\n            0.75,\n            1,\n            1.25,\n            1.5,\n            1.75,\n            2,\n        ],\n    },\n\n    // Keyboard shortcut settings\n    keyboard: {\n        focused: true,\n        global: false,\n    },\n\n    // Display tooltips\n    tooltips: {\n        controls: false,\n        seek: true,\n    },\n\n    // Captions settings\n    captions: {\n        active: false,\n        language: window.navigator.language.split('-')[0],\n    },\n\n    // Fullscreen settings\n    fullscreen: {\n        enabled: true, // Allow fullscreen?\n        fallback: true, // Fallback for vintage browsers\n        iosNative: false, // Use the native fullscreen in iOS (disables custom controls)\n    },\n\n    // Local storage\n    storage: {\n        enabled: true,\n        key: 'plyr',\n    },\n\n    // Default controls\n    controls: [\n        'play-large',\n        'play',\n        'progress',\n        'current-time',\n        'mute',\n        'volume',\n        'captions',\n        'settings',\n        'pip',\n        'airplay',\n        'fullscreen',\n    ],\n    settings: [\n        'captions',\n        'quality',\n        'speed',\n    ],\n\n    // Localisation\n    i18n: {\n        restart: 'Restart',\n        rewind: 'Rewind {seektime} secs',\n        play: 'Play',\n        pause: 'Pause',\n        forward: 'Forward {seektime} secs',\n        seek: 'Seek',\n        played: 'Played',\n        buffered: 'Buffered',\n        currentTime: 'Current time',\n        duration: 'Duration',\n        volume: 'Volume',\n        mute: 'Mute',\n        unmute: 'Unmute',\n        enableCaptions: 'Enable captions',\n        disableCaptions: 'Disable captions',\n        enterFullscreen: 'Enter fullscreen',\n        exitFullscreen: 'Exit fullscreen',\n        frameTitle: 'Player for {title}',\n        captions: 'Captions',\n        settings: 'Settings',\n        speed: 'Speed',\n        quality: 'Quality',\n        loop: 'Loop',\n        start: 'Start',\n        end: 'End',\n        all: 'All',\n        reset: 'Reset',\n        none: 'None',\n        disabled: 'Disabled',\n        advertisment: 'Ad',\n    },\n\n    // URLs\n    urls: {\n        vimeo: {\n            api: 'https://player.vimeo.com/api/player.js',\n        },\n        youtube: {\n            api: 'https://www.youtube.com/iframe_api',\n        },\n        googleIMA: {\n            api: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',\n        },\n    },\n\n    // Custom control listeners\n    listeners: {\n        seek: null,\n        play: null,\n        pause: null,\n        restart: null,\n        rewind: null,\n        forward: null,\n        mute: null,\n        volume: null,\n        captions: null,\n        fullscreen: null,\n        pip: null,\n        airplay: null,\n        speed: null,\n        quality: null,\n        loop: null,\n        language: null,\n    },\n\n    // Events to watch and bubble\n    events: [\n        // Events to watch on HTML5 media elements and bubble\n        // https://developer.mozilla.org/en/docs/Web/Guide/Events/Media_events\n        'ended',\n        'progress',\n        'stalled',\n        'playing',\n        'waiting',\n        'canplay',\n        'canplaythrough',\n        'loadstart',\n        'loadeddata',\n        'loadedmetadata',\n        'timeupdate',\n        'volumechange',\n        'play',\n        'pause',\n        'error',\n        'seeking',\n        'seeked',\n        'emptied',\n        'ratechange',\n        'cuechange',\n\n        // Custom events\n        'enterfullscreen',\n        'exitfullscreen',\n        'captionsenabled',\n        'captionsdisabled',\n        'languagechange',\n        'controlshidden',\n        'controlsshown',\n        'ready',\n\n        // YouTube\n        'statechange',\n        'qualitychange',\n        'qualityrequested',\n\n        // Ads\n        'adsloaded',\n        'adscontentpause',\n        'adsconentresume',\n        'adstarted',\n        'adsmidpoint',\n        'adscomplete',\n        'adsallcomplete',\n        'adsimpression',\n        'adsclick',\n    ],\n\n    // Selectors\n    // Change these to match your template if using custom HTML\n    selectors: {\n        editable: 'input, textarea, select, [contenteditable]',\n        container: '.plyr',\n        controls: {\n            container: null,\n            wrapper: '.plyr__controls',\n        },\n        labels: '[data-plyr]',\n        buttons: {\n            play: '[data-plyr=\"play\"]',\n            pause: '[data-plyr=\"pause\"]',\n            restart: '[data-plyr=\"restart\"]',\n            rewind: '[data-plyr=\"rewind\"]',\n            forward: '[data-plyr=\"fast-forward\"]',\n            mute: '[data-plyr=\"mute\"]',\n            captions: '[data-plyr=\"captions\"]',\n            fullscreen: '[data-plyr=\"fullscreen\"]',\n            pip: '[data-plyr=\"pip\"]',\n            airplay: '[data-plyr=\"airplay\"]',\n            settings: '[data-plyr=\"settings\"]',\n            loop: '[data-plyr=\"loop\"]',\n        },\n        inputs: {\n            seek: '[data-plyr=\"seek\"]',\n            volume: '[data-plyr=\"volume\"]',\n            speed: '[data-plyr=\"speed\"]',\n            language: '[data-plyr=\"language\"]',\n            quality: '[data-plyr=\"quality\"]',\n        },\n        display: {\n            currentTime: '.plyr__time--current',\n            duration: '.plyr__time--duration',\n            buffer: '.plyr__progress--buffer',\n            played: '.plyr__progress--played',\n            loop: '.plyr__progress--loop',\n            volume: '.plyr__volume--display',\n        },\n        progress: '.plyr__progress',\n        captions: '.plyr__captions',\n        menu: {\n            quality: '.js-plyr__menu__list--quality',\n        },\n    },\n\n    // Class hooks added to the player in different states\n    classNames: {\n        video: 'plyr__video-wrapper',\n        embed: 'plyr__video-embed',\n        ads: 'plyr__ads',\n        control: 'plyr__control',\n        type: 'plyr--{0}',\n        provider: 'plyr--{0}',\n        stopped: 'plyr--stopped',\n        playing: 'plyr--playing',\n        loading: 'plyr--loading',\n        error: 'plyr--has-error',\n        hover: 'plyr--hover',\n        tooltip: 'plyr__tooltip',\n        cues: 'plyr__cues',\n        hidden: 'plyr__sr-only',\n        hideControls: 'plyr--hide-controls',\n        isIos: 'plyr--is-ios',\n        isTouch: 'plyr--is-touch',\n        uiSupported: 'plyr--full-ui',\n        noTransition: 'plyr--no-transition',\n        menu: {\n            value: 'plyr__menu__value',\n            badge: 'plyr__badge',\n            open: 'plyr--menu-open',\n        },\n        captions: {\n            enabled: 'plyr--captions-enabled',\n            active: 'plyr--captions-active',\n        },\n        fullscreen: {\n            enabled: 'plyr--fullscreen-enabled',\n            fallback: 'plyr--fullscreen-fallback',\n        },\n        pip: {\n            supported: 'plyr--pip-supported',\n            active: 'plyr--pip-active',\n        },\n        airplay: {\n            supported: 'plyr--airplay-supported',\n            active: 'plyr--airplay-active',\n        },\n        tabFocus: 'plyr__tab-focus',\n    },\n\n    // Embed attributes\n    attributes: {\n        embed: {\n            provider: 'data-plyr-provider',\n            id: 'data-plyr-embed-id',\n        },\n    },\n\n    // API keys\n    keys: {\n        google: null,\n    },\n\n    // Advertisements plugin\n    // Tag is not required as publisher is determined by vi.ai using the domain\n    ads: {\n        enabled: false,\n    },\n};\n\nexport default defaults;\n","// ==========================================================================\n// Plyr utils\n// ==========================================================================\n\nimport support from './support';\nimport { providers } from './types';\n\nconst utils = {\n    // Check variable types\n    is: {\n        plyr(input) {\n            return this.instanceof(input, window.Plyr);\n        },\n        object(input) {\n            return this.getConstructor(input) === Object;\n        },\n        number(input) {\n            return this.getConstructor(input) === Number && !Number.isNaN(input);\n        },\n        string(input) {\n            return this.getConstructor(input) === String;\n        },\n        boolean(input) {\n            return this.getConstructor(input) === Boolean;\n        },\n        function(input) {\n            return this.getConstructor(input) === Function;\n        },\n        array(input) {\n            return !this.nullOrUndefined(input) && Array.isArray(input);\n        },\n        weakMap(input) {\n            return this.instanceof(input, window.WeakMap);\n        },\n        nodeList(input) {\n            return this.instanceof(input, window.NodeList);\n        },\n        element(input) {\n            return this.instanceof(input, window.Element);\n        },\n        textNode(input) {\n            return this.getConstructor(input) === Text;\n        },\n        event(input) {\n            return this.instanceof(input, window.Event);\n        },\n        cue(input) {\n            return this.instanceof(input, window.TextTrackCue) || this.instanceof(input, window.VTTCue);\n        },\n        track(input) {\n            return this.instanceof(input, TextTrack) || (!this.nullOrUndefined(input) && this.string(input.kind));\n        },\n        url(input) {\n            return !this.nullOrUndefined(input) && /(ftp|http|https):\\/\\/(\\w+:{0,1}\\w*@)?(\\S+)(:[0-9]+)?(\\/|\\/([\\w#!:.?+=&%@!\\-/]))?/.test(input);\n        },\n        nullOrUndefined(input) {\n            return input === null || typeof input === 'undefined';\n        },\n        empty(input) {\n            return (\n                this.nullOrUndefined(input) ||\n                ((this.string(input) || this.array(input) || this.nodeList(input)) && !input.length) ||\n                (this.object(input) && !Object.keys(input).length)\n            );\n        },\n        instanceof(input, constructor) {\n            return Boolean(input && constructor && input instanceof constructor);\n        },\n        getConstructor(input) {\n            return !this.nullOrUndefined(input) ? input.constructor : null;\n        },\n    },\n\n    // Unfortunately, due to mixed support, UA sniffing is required\n    getBrowser() {\n        return {\n            isIE: /* @cc_on!@ */ false || !!document.documentMode,\n            isWebkit: 'WebkitAppearance' in document.documentElement.style && !/Edge/.test(navigator.userAgent),\n            isIPhone: /(iPhone|iPod)/gi.test(navigator.platform),\n            isIos: /(iPad|iPhone|iPod)/gi.test(navigator.platform),\n        };\n    },\n\n    // Fetch wrapper\n    // Using XHR to avoid issues with older browsers\n    fetch(url) {\n        return new Promise((resolve, reject) => {\n            try {\n                const request = new XMLHttpRequest();\n\n                // Check for CORS support\n                if (!('withCredentials' in request)) {\n                    return;\n                }\n\n                request.addEventListener('load', () => {\n                    try {\n                        resolve(JSON.parse(request.responseText));\n                    } catch(e) {\n                        resolve(request.responseText);\n                    }\n                });\n\n                request.addEventListener('error', () => {\n                    throw new Error(request.statusText);\n                });\n\n                request.open('GET', url, true);\n                request.send();\n            } catch (e) {\n                reject(e);\n            }\n        });\n    },\n\n    // Load an external script\n    loadScript(url, callback, error) {\n        const current = document.querySelector(`script[src=\"${url}\"]`);\n\n        // Check script is not already referenced, if so wait for load\n        if (current !== null) {\n            current.callbacks = current.callbacks || [];\n            current.callbacks.push(callback);\n            return;\n        }\n\n        // Build the element\n        const element = document.createElement('script');\n\n        // Callback queue\n        element.callbacks = element.callbacks || [];\n        element.callbacks.push(callback);\n\n        // Error queue\n        element.errors = element.errors || [];\n        element.errors.push(error);\n\n        // Bind callback\n        if (utils.is.function(callback)) {\n            element.addEventListener(\n                'load',\n                event => {\n                    element.callbacks.forEach(cb => cb.call(null, event));\n                    element.callbacks = null;\n                },\n                false,\n            );\n        }\n\n        // Bind error handling\n        element.addEventListener(\n            'error',\n            event => {\n                element.errors.forEach(err => err.call(null, event));\n                element.errors = null;\n            },\n            false,\n        );\n\n        // Set the URL after binding callback\n        element.src = url;\n\n        // Inject\n        const first = document.getElementsByTagName('script')[0];\n        first.parentNode.insertBefore(element, first);\n    },\n\n    // Load an external SVG sprite\n    loadSprite(url, id) {\n        if (!utils.is.string(url)) {\n            return;\n        }\n\n        const prefix = 'cache-';\n        const hasId = utils.is.string(id);\n        let isCached = false;\n\n        function updateSprite(data) {\n            // Inject content\n            this.innerHTML = data;\n\n            // Inject the SVG to the body\n            document.body.insertBefore(this, document.body.childNodes[0]);\n        }\n\n        // Only load once\n        if (!hasId || !document.querySelectorAll(`#${id}`).length) {\n            // Create container\n            const container = document.createElement('div');\n            utils.toggleHidden(container, true);\n\n            if (hasId) {\n                container.setAttribute('id', id);\n            }\n\n            // Check in cache\n            if (support.storage) {\n                const cached = window.localStorage.getItem(prefix + id);\n                isCached = cached !== null;\n\n                if (isCached) {\n                    const data = JSON.parse(cached);\n                    updateSprite.call(container, data.content);\n                    return;\n                }\n            }\n\n            // Get the sprite\n            utils\n                .fetch(url)\n                .then(result => {\n                    if (utils.is.empty(result)) {\n                        return;\n                    }\n\n                    if (support.storage) {\n                        window.localStorage.setItem(\n                            prefix + id,\n                            JSON.stringify({\n                                content: result,\n                            }),\n                        );\n                    }\n\n                    updateSprite.call(container, result);\n                })\n                .catch(() => {});\n        }\n    },\n\n    // Generate a random ID\n    generateId(prefix) {\n        return `${prefix}-${Math.floor(Math.random() * 10000)}`;\n    },\n\n    // Determine if we're in an iframe\n    inFrame() {\n        try {\n            return window.self !== window.top;\n        } catch (e) {\n            return true;\n        }\n    },\n\n    // Wrap an element\n    wrap(elements, wrapper) {\n        // Convert `elements` to an array, if necessary.\n        const targets = elements.length ? elements : [elements];\n\n        // Loops backwards to prevent having to clone the wrapper on the\n        // first element (see `child` below).\n        Array.from(targets)\n            .reverse()\n            .forEach((element, index) => {\n                const child = index > 0 ? wrapper.cloneNode(true) : wrapper;\n\n                // Cache the current parent and sibling.\n                const parent = element.parentNode;\n                const sibling = element.nextSibling;\n\n                // Wrap the element (is automatically removed from its current\n                // parent).\n                child.appendChild(element);\n\n                // If the element had a sibling, insert the wrapper before\n                // the sibling to maintain the HTML structure; otherwise, just\n                // append it to the parent.\n                if (sibling) {\n                    parent.insertBefore(child, sibling);\n                } else {\n                    parent.appendChild(child);\n                }\n            });\n    },\n\n    // Create a DocumentFragment\n    createElement(type, attributes, text) {\n        // Create a new <element>\n        const element = document.createElement(type);\n\n        // Set all passed attributes\n        if (utils.is.object(attributes)) {\n            utils.setAttributes(element, attributes);\n        }\n\n        // Add text node\n        if (utils.is.string(text)) {\n            element.textContent = text;\n        }\n\n        // Return built element\n        return element;\n    },\n\n    // Inaert an element after another\n    insertAfter(element, target) {\n        target.parentNode.insertBefore(element, target.nextSibling);\n    },\n\n    // Insert a DocumentFragment\n    insertElement(type, parent, attributes, text) {\n        // Inject the new <element>\n        parent.appendChild(utils.createElement(type, attributes, text));\n    },\n\n    // Remove an element\n    removeElement(element) {\n        if (!utils.is.element(element) || !utils.is.element(element.parentNode)) {\n            return;\n        }\n\n        if (utils.is.nodeList(element) || utils.is.array(element)) {\n            Array.from(element).forEach(utils.removeElement);\n            return;\n        }\n\n        element.parentNode.removeChild(element);\n    },\n\n    // Remove all child elements\n    emptyElement(element) {\n        let { length } = element.childNodes;\n\n        while (length > 0) {\n            element.removeChild(element.lastChild);\n            length -= 1;\n        }\n    },\n\n    // Replace element\n    replaceElement(newChild, oldChild) {\n        if (!utils.is.element(oldChild) || !utils.is.element(oldChild.parentNode) || !utils.is.element(newChild)) {\n            return null;\n        }\n\n        oldChild.parentNode.replaceChild(newChild, oldChild);\n\n        return newChild;\n    },\n\n    // Set attributes\n    setAttributes(element, attributes) {\n        if (!utils.is.element(element) || utils.is.empty(attributes)) {\n            return;\n        }\n\n        Object.keys(attributes).forEach(key => {\n            element.setAttribute(key, attributes[key]);\n        });\n    },\n\n    // Get an attribute object from a string selector\n    getAttributesFromSelector(sel, existingAttributes) {\n        // For example:\n        // '.test' to { class: 'test' }\n        // '#test' to { id: 'test' }\n        // '[data-test=\"test\"]' to { 'data-test': 'test' }\n\n        if (!utils.is.string(sel) || utils.is.empty(sel)) {\n            return {};\n        }\n\n        const attributes = {};\n        const existing = existingAttributes;\n\n        sel.split(',').forEach(s => {\n            // Remove whitespace\n            const selector = s.trim();\n            const className = selector.replace('.', '');\n            const stripped = selector.replace(/[[\\]]/g, '');\n\n            // Get the parts and value\n            const parts = stripped.split('=');\n            const key = parts[0];\n            const value = parts.length > 1 ? parts[1].replace(/[\"']/g, '') : '';\n\n            // Get the first character\n            const start = selector.charAt(0);\n\n            switch (start) {\n                case '.':\n                    // Add to existing classname\n                    if (utils.is.object(existing) && utils.is.string(existing.class)) {\n                        existing.class += ` ${className}`;\n                    }\n\n                    attributes.class = className;\n                    break;\n\n                case '#':\n                    // ID selector\n                    attributes.id = selector.replace('#', '');\n                    break;\n\n                case '[':\n                    // Attribute selector\n                    attributes[key] = value;\n\n                    break;\n\n                default:\n                    break;\n            }\n        });\n\n        return attributes;\n    },\n\n    // Toggle class on an element\n    toggleClass(element, className, toggle) {\n        if (utils.is.element(element)) {\n            const contains = element.classList.contains(className);\n\n            element.classList[toggle ? 'add' : 'remove'](className);\n\n            return (toggle && !contains) || (!toggle && contains);\n        }\n\n        return null;\n    },\n\n    // Has class name\n    hasClass(element, className) {\n        return utils.is.element(element) && element.classList.contains(className);\n    },\n\n    // Toggle hidden attribute on an element\n    toggleHidden(element, toggle) {\n        if (!utils.is.element(element)) {\n            return;\n        }\n\n        if (toggle) {\n            element.setAttribute('hidden', '');\n        } else {\n            element.removeAttribute('hidden');\n        }\n    },\n\n    // Element matches selector\n    matches(element, selector) {\n        const prototype = { Element };\n\n        function match() {\n            return Array.from(document.querySelectorAll(selector)).includes(this);\n        }\n\n        const matches = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;\n\n        return matches.call(element, selector);\n    },\n\n    // Find all elements\n    getElements(selector) {\n        return this.elements.container.querySelectorAll(selector);\n    },\n\n    // Find a single element\n    getElement(selector) {\n        return this.elements.container.querySelector(selector);\n    },\n\n    // Find the UI controls and store references in custom controls\n    // TODO: Allow settings menus with custom controls\n    findElements() {\n        try {\n            this.elements.controls = utils.getElement.call(this, this.config.selectors.controls.wrapper);\n\n            // Buttons\n            this.elements.buttons = {\n                play: utils.getElements.call(this, this.config.selectors.buttons.play),\n                pause: utils.getElement.call(this, this.config.selectors.buttons.pause),\n                restart: utils.getElement.call(this, this.config.selectors.buttons.restart),\n                rewind: utils.getElement.call(this, this.config.selectors.buttons.rewind),\n                forward: utils.getElement.call(this, this.config.selectors.buttons.forward),\n                mute: utils.getElement.call(this, this.config.selectors.buttons.mute),\n                pip: utils.getElement.call(this, this.config.selectors.buttons.pip),\n                airplay: utils.getElement.call(this, this.config.selectors.buttons.airplay),\n                settings: utils.getElement.call(this, this.config.selectors.buttons.settings),\n                captions: utils.getElement.call(this, this.config.selectors.buttons.captions),\n                fullscreen: utils.getElement.call(this, this.config.selectors.buttons.fullscreen),\n            };\n\n            // Progress\n            this.elements.progress = utils.getElement.call(this, this.config.selectors.progress);\n\n            // Inputs\n            this.elements.inputs = {\n                seek: utils.getElement.call(this, this.config.selectors.inputs.seek),\n                volume: utils.getElement.call(this, this.config.selectors.inputs.volume),\n            };\n\n            // Display\n            this.elements.display = {\n                buffer: utils.getElement.call(this, this.config.selectors.display.buffer),\n                duration: utils.getElement.call(this, this.config.selectors.display.duration),\n                currentTime: utils.getElement.call(this, this.config.selectors.display.currentTime),\n            };\n\n            // Seek tooltip\n            if (utils.is.element(this.elements.progress)) {\n                this.elements.display.seekTooltip = this.elements.progress.querySelector(`.${this.config.classNames.tooltip}`);\n            }\n\n            return true;\n        } catch (error) {\n            // Log it\n            this.debug.warn('It looks like there is a problem with your custom controls HTML', error);\n\n            // Restore native video controls\n            this.toggleNativeControls(true);\n\n            return false;\n        }\n    },\n\n    // Get the focused element\n    getFocusElement() {\n        let focused = document.activeElement;\n\n        if (!focused || focused === document.body) {\n            focused = null;\n        } else {\n            focused = document.querySelector(':focus');\n        }\n\n        return focused;\n    },\n\n    // Trap focus inside container\n    trapFocus(element = null, toggle = false) {\n        if (!utils.is.element(element)) {\n            return;\n        }\n\n        const focusable = utils.getElements.call(this, 'button:not(:disabled), input:not(:disabled), [tabindex]');\n        const first = focusable[0];\n        const last = focusable[focusable.length - 1];\n\n        const trap = event => {\n            // Bail if not tab key or not fullscreen\n            if (event.key !== 'Tab' || event.keyCode !== 9) {\n                return;\n            }\n\n            // Get the current focused element\n            const focused = utils.getFocusElement();\n\n            if (focused === last && !event.shiftKey) {\n                // Move focus to first element that can be tabbed if Shift isn't used\n                first.focus();\n                event.preventDefault();\n            } else if (focused === first && event.shiftKey) {\n                // Move focus to last element that can be tabbed if Shift is used\n                last.focus();\n                event.preventDefault();\n            }\n        };\n\n        if (toggle) {\n            utils.on(this.elements.container, 'keydown', trap, false);\n        } else {\n            utils.off(this.elements.container, 'keydown', trap, false);\n        }\n    },\n\n    // Toggle event listener\n    toggleListener(elements, event, callback, toggle, passive, capture) {\n        // Bail if no elemetns, event, or callback\n        if (utils.is.empty(elements)  || utils.is.empty(event) || !utils.is.function(callback)) {\n            return;\n        }\n\n        // If a nodelist is passed, call itself on each node\n        if (utils.is.nodeList(elements) || utils.is.array(elements)) {\n            // Create listener for each node\n            Array.from(elements).forEach(element => {\n                if (element instanceof Node) {\n                    utils.toggleListener.call(null, element, event, callback, toggle, passive, capture);\n                }\n            });\n\n            return;\n        }\n\n        // Allow multiple events\n        const events = event.split(' ');\n\n        // Build options\n        // Default to just capture boolean\n        let options = utils.is.boolean(capture) ? capture : false;\n\n        // If passive events listeners are supported\n        if (support.passiveListeners) {\n            options = {\n                // Whether the listener can be passive (i.e. default never prevented)\n                passive: utils.is.boolean(passive) ? passive : true,\n                // Whether the listener is a capturing listener or not\n                capture: utils.is.boolean(capture) ? capture : false,\n            };\n        }\n\n        // If a single node is passed, bind the event listener\n        events.forEach(type => {\n            elements[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options);\n        });\n    },\n\n    // Bind event handler\n    on(element, events, callback, passive, capture) {\n        utils.toggleListener(element, events, callback, true, passive, capture);\n    },\n\n    // Unbind event handler\n    off(element, events, callback, passive, capture) {\n        utils.toggleListener(element, events, callback, false, passive, capture);\n    },\n\n    // Trigger event\n    dispatchEvent(element, type, bubbles, detail) {\n        // Bail if no element\n        if (!element || !type) {\n            return;\n        }\n\n        // Create and dispatch the event\n        const event = new CustomEvent(type, {\n            bubbles: utils.is.boolean(bubbles) ? bubbles : false,\n            detail: Object.assign({}, detail, {\n                plyr: utils.is.plyr(this) ? this : null,\n            }),\n        });\n\n        // Dispatch the event\n        element.dispatchEvent(event);\n    },\n\n    // Toggle aria-pressed state on a toggle button\n    // http://www.ssbbartgroup.com/blog/how-not-to-misuse-aria-states-properties-and-roles\n    toggleState(element, input) {\n        // Bail if no target\n        if (!utils.is.element(element)) {\n            return;\n        }\n\n        // Get state\n        const pressed = element.getAttribute('aria-pressed') === 'true';\n        const state = utils.is.boolean(input) ? input : !pressed;\n\n        // Set the attribute on target\n        element.setAttribute('aria-pressed', state);\n    },\n\n    // Get percentage\n    getPercentage(current, max) {\n        if (current === 0 || max === 0 || Number.isNaN(current) || Number.isNaN(max)) {\n            return 0;\n        }\n        return (current / max * 100).toFixed(2);\n    },\n\n    // Time helpers\n    getHours(value) {\n        return parseInt((value / 60 / 60) % 60, 10);\n    },\n    getMinutes(value) {\n        return parseInt((value / 60) % 60, 10);\n    },\n    getSeconds(value) {\n        return parseInt(value % 60, 10);\n    },\n\n    // Format time to UI friendly string\n    formatTime(time = 0, displayHours = false, inverted = false) {\n        // Bail if the value isn't a number\n        if (!utils.is.number(time)) {\n            return this.formatTime(null, displayHours, inverted);\n        }\n\n        // Format time component to add leading zero\n        const format = value => `0${value}`.slice(-2);\n\n        // Breakdown to hours, mins, secs\n        let hours = this.getHours(time);\n        const mins = this.getMinutes(time);\n        const secs = this.getSeconds(time);\n\n        // Do we need to display hours?\n        if (displayHours || hours > 0) {\n            hours = `${hours}:`;\n        } else {\n            hours = '';\n        }\n\n        // Render\n        return `${inverted ? '-' : ''}${hours}${format(mins)}:${format(secs)}`;\n    },\n\n    // Deep extend destination object with N more objects\n    extend(target = {}, ...sources) {\n        if (!sources.length) {\n            return target;\n        }\n\n        const source = sources.shift();\n\n        if (!utils.is.object(source)) {\n            return target;\n        }\n\n        Object.keys(source).forEach(key => {\n            if (utils.is.object(source[key])) {\n                if (!Object.keys(target).includes(key)) {\n                    Object.assign(target, { [key]: {} });\n                }\n\n                utils.extend(target[key], source[key]);\n            } else {\n                Object.assign(target, { [key]: source[key] });\n            }\n        });\n\n        return utils.extend(target, ...sources);\n    },\n\n    // Get the provider for a given URL\n    getProviderByUrl(url) {\n        // YouTube\n        if (/^(https?:\\/\\/)?(www\\.)?(youtube\\.com|youtu\\.?be)\\/.+$/.test(url)) {\n            return providers.youtube;\n        }\n\n        // Vimeo\n        if (/^https?:\\/\\/player.vimeo.com\\/video\\/\\d{8,}(?=\\b|\\/)/.test(url)) {\n            return providers.vimeo;\n        }\n\n        return null;\n    },\n\n    // Parse YouTube ID from URL\n    parseYouTubeId(url) {\n        if (utils.is.empty(url)) {\n            return null;\n        }\n\n        const regex = /^.*(youtu.be\\/|v\\/|u\\/\\w\\/|embed\\/|watch\\?v=|&v=)([^#&?]*).*/;\n        return url.match(regex) ? RegExp.$2 : url;\n    },\n\n    // Parse Vimeo ID from URL\n    parseVimeoId(url) {\n        if (utils.is.empty(url)) {\n            return null;\n        }\n\n        if (utils.is.number(Number(url))) {\n            return url;\n        }\n\n        const regex = /^.*(vimeo.com\\/|video\\/)(\\d+).*/;\n        return url.match(regex) ? RegExp.$2 : url;\n    },\n\n    // Convert a URL to a location object\n    parseUrl(url) {\n        const parser = document.createElement('a');\n        parser.href = url;\n        return parser;\n    },\n\n    // Get URL query parameters\n    getUrlParams(input) {\n        let search = input;\n\n        // Parse URL if needed\n        if (input.startsWith('http://') || input.startsWith('https://')) {\n            ({ search } = this.parseUrl(input));\n        }\n\n        if (this.is.empty(search)) {\n            return null;\n        }\n\n        const hashes = search.slice(search.indexOf('?') + 1).split('&');\n\n        return hashes.reduce((params, hash) => {\n            const [\n                key,\n                val,\n            ] = hash.split('=');\n\n            return Object.assign(params, { [key]: decodeURIComponent(val) });\n        }, {});\n    },\n\n    // Convert object to URL parameters\n    buildUrlParams(input) {\n        if (!utils.is.object(input)) {\n            return '';\n        }\n\n        return Object.keys(input)\n            .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(input[key])}`)\n            .join('&');\n    },\n\n    // Remove HTML from a string\n    stripHTML(source) {\n        const fragment = document.createDocumentFragment();\n        const element = document.createElement('div');\n        fragment.appendChild(element);\n        element.innerHTML = source;\n        return fragment.firstChild.innerText;\n    },\n\n    // Get aspect ratio for dimensions\n    getAspectRatio(width, height) {\n        const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h));\n        const ratio = getRatio(width, height);\n        return `${width / ratio}:${height / ratio}`;\n    },\n\n    // Get the transition end event\n    get transitionEndEvent() {\n        const element = document.createElement('span');\n\n        const events = {\n            WebkitTransition: 'webkitTransitionEnd',\n            MozTransition: 'transitionend',\n            OTransition: 'oTransitionEnd otransitionend',\n            transition: 'transitionend',\n        };\n\n        const type = Object.keys(events).find(event => element.style[event] !== undefined);\n\n        return utils.is.string(type) ? events[type] : false;\n    },\n\n    // Force repaint of element\n    repaint(element) {\n        window.setTimeout(() => {\n            utils.toggleHidden(element, true);\n            element.offsetHeight; // eslint-disable-line\n            utils.toggleHidden(element, false);\n        }, 0);\n    },\n};\n\nexport default utils;\n","// ==========================================================================\n// Console wrapper\n// ==========================================================================\n\nconst noop = () => {};\n\nexport default class Console {\n    constructor(enabled = false) {\n        this.enabled = window.console && enabled;\n\n        if (this.enabled) {\n            this.log('Debugging enabled');\n        }\n    }\n\n    get log() {\n        // eslint-disable-next-line no-console\n        return this.enabled ? Function.prototype.bind.call(console.log, console) : noop;\n    }\n    get warn() {\n        // eslint-disable-next-line no-console\n        return this.enabled ? Function.prototype.bind.call(console.warn, console) : noop;\n    }\n    get error() {\n        // eslint-disable-next-line no-console\n        return this.enabled ? Function.prototype.bind.call(console.error, console) : noop;\n    }\n}\n","// ==========================================================================\n// Fullscreen wrapper\n// ==========================================================================\n\nimport utils from './utils';\n\nconst browser = utils.getBrowser();\n\nfunction onChange() {\n    if (!this.enabled) {\n        return;\n    }\n\n    // Update toggle button\n    const button = this.player.elements.buttons.fullscreen;\n    if (utils.is.element(button)) {\n        utils.toggleState(button, this.active);\n    }\n\n    // Trigger an event\n    utils.dispatchEvent(this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true);\n\n    // Trap focus in container\n    if (!browser.isIos) {\n        utils.trapFocus.call(this.player, this.target, this.active);\n    }\n}\n\nfunction toggleFallback(toggle = false) {\n    // Store or restore scroll position\n    if (toggle) {\n        this.scrollPosition = {\n            x: window.scrollX || 0,\n            y: window.scrollY || 0,\n        };\n    } else {\n        window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);\n    }\n\n    // Toggle scroll\n    document.body.style.overflow = toggle ? 'hidden' : '';\n\n    // Toggle class hook\n    utils.toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);\n\n    // Toggle button and fire events\n    onChange.call(this);\n}\n\nclass Fullscreen {\n    constructor(player) {\n        // Keep reference to parent\n        this.player = player;\n\n        // Get prefix\n        this.prefix = Fullscreen.prefix;\n\n        // Scroll position\n        this.scrollPosition = { x: 0, y: 0 };\n\n        // Register event listeners\n        // Handle event (incase user presses escape etc)\n        utils.on(document, this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`, () => {\n            // TODO: Filter for target??\n            onChange.call(this);\n        });\n\n        // Fullscreen toggle on double click\n        utils.on(this.player.elements.container, 'dblclick', () => {\n            this.toggle();\n        });\n\n        // Prevent double click on controls bubbling up\n        utils.on(this.player.elements.controls, 'dblclick', event => event.stopPropagation());\n\n        // Update the UI\n        this.update();\n    }\n\n    // Determine if native supported\n    static get native() {\n        return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled);\n    }\n\n    // Get the prefix for handlers\n    static get prefix() {\n        // No prefix\n        if (utils.is.function(document.cancelFullScreen)) {\n            return false;\n        }\n\n        // Check for fullscreen support by vendor prefix\n        let value = '';\n        const prefixes = [\n            'webkit',\n            'moz',\n            'ms',\n        ];\n\n        prefixes.some(pre => {\n            if (utils.is.function(document[`${pre}CancelFullScreen`])) {\n                value = pre;\n                return true;\n            } else if (utils.is.function(document.msExitFullscreen)) {\n                value = 'ms';\n                return true;\n            }\n\n            return false;\n        });\n\n        return value;\n    }\n\n    // Determine if fullscreen is enabled\n    get enabled() {\n        const fallback = this.player.config.fullscreen.fallback && !utils.inFrame();\n\n        return (Fullscreen.native || fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo;\n    }\n\n    // Get active state\n    get active() {\n        if (!this.enabled) {\n            return false;\n        }\n\n        // Fallback using classname\n        if (!Fullscreen.native) {\n            return utils.hasClass(this.target, this.player.config.classNames.fullscreen.fallback);\n        }\n\n        const element = !this.prefix ? document.fullscreenElement : document[`${this.prefix}FullscreenElement`];\n\n        return element === this.target;\n    }\n\n    // Get target element\n    get target() {\n        return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;\n    }\n\n    // Update UI\n    update() {\n        if (this.enabled) {\n            this.player.debug.log(`${Fullscreen.native ? 'Native' : 'Fallback'} fullscreen enabled`);\n        } else {\n            this.player.debug.log('Fullscreen not supported and fallback disabled');\n        }\n\n        // Add styling hook to show button\n        utils.toggleClass(this.player.elements.container, this.player.config.classNames.fullscreen.enabled, this.enabled);\n    }\n\n    // Make an element fullscreen\n    enter() {\n        if (!this.enabled) {\n            return;\n        }\n\n        // iOS native fullscreen doesn't need the request step\n        if (browser.isIos && this.player.config.fullscreen.iosNative) {\n            if (this.player.playing) {\n                this.target.webkitEnterFullscreen();\n            }\n        } else if (!Fullscreen.native) {\n            toggleFallback.call(this, true);\n        } else if (!this.prefix) {\n            this.target.requestFullScreen();\n        } else if (!utils.is.empty(this.prefix)) {\n            this.target[`${this.prefix}${this.prefix === 'ms' ? 'RequestFullscreen' : 'RequestFullScreen'}`]();\n        }\n    }\n\n    // Bail from fullscreen\n    exit() {\n        if (!this.enabled) {\n            return;\n        }\n\n        // iOS native fullscreen\n        if (browser.isIos && this.player.config.fullscreen.iosNative) {\n            this.target.webkitExitFullscreen();\n            this.player.play();\n        } else if (!Fullscreen.native) {\n            toggleFallback.call(this, false);\n        } else if (!this.prefix) {\n            document.cancelFullScreen();\n        } else if (!utils.is.empty(this.prefix)) {\n            document[`${this.prefix}${this.prefix === 'ms' ? 'ExitFullscreen' : 'CancelFullScreen'}`]();\n        }\n    }\n\n    // Toggle state\n    toggle() {\n        if (!this.active) {\n            this.enter();\n        } else {\n            this.exit();\n        }\n    }\n}\n\nexport default Fullscreen;\n","// ==========================================================================\n// Plyr storage\n// ==========================================================================\n\nimport utils from './utils';\n\nclass Storage {\n    constructor(player) {\n        this.enabled = player.config.storage.enabled;\n        this.key = player.config.storage.key;\n    }\n\n    // Check for actual support (see if we can use it)\n    static get supported() {\n        if (!('localStorage' in window)) {\n            return false;\n        }\n\n        const test = '___test';\n\n        // Try to use it (it might be disabled, e.g. user is in private mode)\n        // see: https://github.com/sampotts/plyr/issues/131\n        try {\n            window.localStorage.setItem(test, test);\n            window.localStorage.removeItem(test);\n            return true;\n        } catch (e) {\n            return false;\n        }\n    }\n\n    get(key) {\n        const store = window.localStorage.getItem(this.key);\n\n        if (!Storage.supported || utils.is.empty(store)) {\n            return null;\n        }\n\n        const json = JSON.parse(store);\n\n        return utils.is.string(key) && key.length ? json[key] : json;\n    }\n\n    set(object) {\n        // Bail if we don't have localStorage support or it's disabled\n        if (!Storage.supported || !this.enabled) {\n            return;\n        }\n\n        // Can only store objectst\n        if (!utils.is.object(object)) {\n            return;\n        }\n\n        // Get current storage\n        let storage = this.get();\n\n        // Default to empty object\n        if (utils.is.empty(storage)) {\n            storage = {};\n        }\n\n        // Update the working copy of the values\n        utils.extend(storage, object);\n\n        // Update storage\n        window.localStorage.setItem(this.key, JSON.stringify(storage));\n    }\n}\n\nexport default Storage;\n","// ==========================================================================\n// Advertisement plugin using Google IMA HTML5 SDK\n// Create an account with our ad partner, vi here:\n// https://www.vi.ai/publisher-video-monetization/\n// ==========================================================================\n\n/* global google */\n\nimport utils from '../utils';\n\n// Build the default tag URL\nconst getTagUrl = () => {\n    const params = {\n        AV_PUBLISHERID: '58c25bb0073ef448b1087ad6',\n        AV_CHANNELID: '5a0458dc28a06145e4519d21',\n        AV_URL: '127.0.0.1:3000',\n        cb: 1,\n        AV_WIDTH: 640,\n        AV_HEIGHT: 480,\n    };\n\n    const base = 'https://go.aniview.com/api/adserver6/vast/';\n\n    return `${base}?${utils.buildUrlParams(params)}`;\n};\n\nclass Ads {\n    /**\n     * Ads constructor.\n     * @param {object} player\n     * @return {Ads}\n     */\n    constructor(player) {\n        this.player = player;\n        this.enabled = player.config.ads.enabled;\n        this.playing = false;\n        this.initialized = false;\n        this.blocked = false;\n        this.enabled = utils.is.url(player.config.ads.tag);\n\n        // Check if a tag URL is provided.\n        if (!this.enabled) {\n            return;\n        }\n\n        // Check if the Google IMA3 SDK is loaded or load it ourselves\n        if (!utils.is.object(window.google)) {\n            utils.loadScript(\n                player.config.urls.googleIMA.api,\n                () => {\n                    this.ready();\n                },\n                () => {\n                    // Script failed to load or is blocked\n                    this.blocked = true;\n                    this.player.debug.log('Ads error: Google IMA SDK failed to load');\n                },\n            );\n        } else {\n            this.ready();\n        }\n    }\n\n    /**\n     * Get the ads instance ready.\n     */\n    ready() {\n        this.elements = {\n            container: null,\n            displayContainer: null,\n        };\n        this.manager = null;\n        this.loader = null;\n        this.cuePoints = null;\n        this.events = {};\n        this.safetyTimer = null;\n        this.countdownTimer = null;\n\n        // Set listeners on the Plyr instance\n        this.listeners();\n\n        // Start ticking our safety timer. If the whole advertisement\n        // thing doesn't resolve within our set time; we bail\n        this.startSafetyTimer(12000, 'ready()');\n\n        // Setup a simple promise to resolve if the IMA loader is ready\n        this.loaderPromise = new Promise(resolve => {\n            this.on('ADS_LOADER_LOADED', () => resolve());\n        });\n\n        // Setup a promise to resolve if the IMA manager is ready\n        this.managerPromise = new Promise(resolve => {\n            this.on('ADS_MANAGER_LOADED', () => resolve());\n        });\n\n        // Clear the safety timer\n        this.managerPromise.then(() => {\n            this.clearSafetyTimer('onAdsManagerLoaded()');\n        });\n\n        // Setup the IMA SDK\n        this.setupIMA();\n    }\n\n    /**\n     * In order for the SDK to display ads for our video, we need to tell it where to put them,\n     * so here we define our ad container. This div is set up to render on top of the video player.\n     * Using the code below, we tell the SDK to render ads within that div. We also provide a\n     * handle to the content video player - the SDK will poll the current time of our player to\n     * properly place mid-rolls. After we create the ad display container, we initialize it. On\n     * mobile devices, this initialization is done as the result of a user action.\n     */\n    setupIMA() {\n        // Create the container for our advertisements\n        this.elements.container = utils.createElement('div', {\n            class: this.player.config.classNames.ads,\n            hidden: '',\n        });\n        this.player.elements.container.appendChild(this.elements.container);\n\n        // So we can run VPAID2\n        google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);\n\n        // Set language\n        google.ima.settings.setLocale(this.player.config.ads.language);\n\n        // We assume the adContainer is the video container of the plyr element\n        // that will house the ads\n        this.elements.displayContainer = new google.ima.AdDisplayContainer(this.elements.container);\n\n        // Request video ads to be pre-loaded\n        this.requestAds();\n    }\n\n    /**\n     * Request advertisements\n     */\n    requestAds() {\n        const { container } = this.player.elements;\n\n        try {\n            // Create ads loader\n            this.loader = new google.ima.AdsLoader(this.elements.displayContainer);\n\n            // Listen and respond to ads loaded and error events\n            this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, event => this.onAdsManagerLoaded(event), false);\n            this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error), false);\n\n            // Request video ads\n            const request = new google.ima.AdsRequest();\n            request.adTagUrl = getTagUrl();\n\n            // Specify the linear and nonlinear slot sizes. This helps the SDK\n            // to select the correct creative if multiple are returned\n            request.linearAdSlotWidth = container.offsetWidth;\n            request.linearAdSlotHeight = container.offsetHeight;\n            request.nonLinearAdSlotWidth = container.offsetWidth;\n            request.nonLinearAdSlotHeight = container.offsetHeight;\n\n            // We only overlay ads as we only support video.\n            request.forceNonLinearFullSlot = false;\n\n            this.loader.requestAds(request);\n\n            this.handleEventListeners('ADS_LOADER_LOADED');\n        } catch (e) {\n            this.onAdError(e);\n        }\n    }\n\n    /**\n     * Update the ad countdown\n     * @param {boolean} start\n     */\n    pollCountdown(start = false) {\n        if (!start) {\n            window.clearInterval(this.countdownTimer);\n            this.elements.container.removeAttribute('data-badge-text');\n            return;\n        }\n\n        const update = () => {\n            const time = utils.formatTime(this.manager.getRemainingTime());\n            const label = `${this.player.config.i18n.advertisment} - ${time}`;\n            this.elements.container.setAttribute('data-badge-text', label);\n        };\n\n        this.countdownTimer = window.setInterval(update, 100);\n    }\n\n    /**\n     * This method is called whenever the ads are ready inside the AdDisplayContainer\n     * @param {Event} adsManagerLoadedEvent\n     */\n    onAdsManagerLoaded(adsManagerLoadedEvent) {\n        // Get the ads manager\n        const settings = new google.ima.AdsRenderingSettings();\n\n        // Tell the SDK to save and restore content video state on our behalf\n        settings.restoreCustomPlaybackStateOnAdBreakComplete = true;\n        settings.enablePreloading = true;\n\n        // The SDK is polling currentTime on the contentPlayback. And needs a duration\n        // so it can determine when to start the mid- and post-roll\n        this.manager = adsManagerLoadedEvent.getAdsManager(this.player, settings);\n\n        // Get the cue points for any mid-rolls by filtering out the pre- and post-roll\n        this.cuePoints = this.manager.getCuePoints();\n\n        // Add advertisement cue's within the time line if available\n        this.cuePoints.forEach(cuePoint => {\n            if (cuePoint !== 0 && cuePoint !== -1) {\n                const seekElement = this.player.elements.progress;\n\n                if (seekElement) {\n                    const cuePercentage = 100 / this.player.duration * cuePoint;\n                    const cue = utils.createElement('span', {\n                        class: this.player.config.classNames.cues,\n                    });\n\n                    cue.style.left = `${cuePercentage.toString()}%`;\n                    seekElement.appendChild(cue);\n                }\n            }\n        });\n\n        // Get skippable state\n        // TODO: Skip button\n        // this.manager.getAdSkippableState();\n\n        // Set volume to match player\n        this.manager.setVolume(this.player.volume);\n\n        // Add listeners to the required events\n        // Advertisement error events\n        this.manager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error));\n\n        // Advertisement regular events\n        Object.keys(google.ima.AdEvent.Type).forEach(type => {\n            this.manager.addEventListener(google.ima.AdEvent.Type[type], event => this.onAdEvent(event));\n        });\n\n        // Resolve our adsManager\n        this.handleEventListeners('ADS_MANAGER_LOADED');\n    }\n\n    /**\n     * This is where all the event handling takes place. Retrieve the ad from the event. Some\n     * events (e.g. ALL_ADS_COMPLETED) don't have the ad object associated\n     * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type\n     * @param {Event} event\n     */\n    onAdEvent(event) {\n        const { container } = this.player.elements;\n\n        // Retrieve the ad from the event. Some events (e.g. ALL_ADS_COMPLETED)\n        // don't have ad object associated\n        const ad = event.getAd();\n\n        // Proxy event\n        const dispatchEvent = type => {\n            utils.dispatchEvent.call(this.player, this.player.media, `ads${type}`);\n        };\n\n        switch (event.type) {\n            case google.ima.AdEvent.Type.LOADED:\n                // This is the first event sent for an ad - it is possible to determine whether the\n                // ad is a video ad or an overlay\n                this.handleEventListeners('LOADED');\n\n                // Bubble event\n                dispatchEvent('loaded');\n\n                // Start countdown\n                this.pollCountdown(true);\n\n                if (!ad.isLinear()) {\n                    // Position AdDisplayContainer correctly for overlay\n                    ad.width = container.offsetWidth;\n                    ad.height = container.offsetHeight;\n                }\n\n                // console.info('Ad type: ' + event.getAd().getAdPodInfo().getPodIndex());\n                // console.info('Ad time: ' + event.getAd().getAdPodInfo().getTimeOffset());\n                break;\n\n            case google.ima.AdEvent.Type.ALL_ADS_COMPLETED:\n                // All ads for the current videos are done. We can now request new advertisements\n                // in case the video is re-played\n                this.handleEventListeners('ALL_ADS_COMPLETED');\n\n                // Fire event\n                dispatchEvent('allcomplete');\n\n                // TODO: Example for what happens when a next video in a playlist would be loaded.\n                // So here we load a new video when all ads are done.\n                // Then we load new ads within a new adsManager. When the video\n                // Is started - after - the ads are loaded, then we get ads.\n                // You can also easily test cancelling and reloading by running\n                // player.ads.cancel() and player.ads.play from the console I guess.\n                // this.player.source = {\n                //     type: 'video',\n                //     title: 'View From A Blue Moon',\n                //     sources: [{\n                //         src:\n                // 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4', type:\n                // 'video/mp4', }], poster:\n                // 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg', tracks:\n                // [ { kind: 'captions', label: 'English', srclang: 'en', src:\n                // 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt',\n                // default: true, }, { kind: 'captions', label: 'French', srclang: 'fr', src:\n                // 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt', }, ],\n                // };\n\n                // TODO: So there is still this thing where a video should only be allowed to start\n                // playing when the IMA SDK is ready or has failed\n\n                this.loadAds();\n                break;\n\n            case google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED:\n                // This event indicates the ad has started - the video player can adjust the UI,\n                // for example display a pause button and remaining time. Fired when content should\n                // be paused. This usually happens right before an ad is about to cover the content\n                this.handleEventListeners('CONTENT_PAUSE_REQUESTED');\n\n                dispatchEvent('contentpause');\n\n                this.pauseContent();\n\n                break;\n\n            case google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED:\n                // This event indicates the ad has finished - the video player can perform\n                // appropriate UI actions, such as removing the timer for remaining time detection.\n                // Fired when content should be resumed. This usually happens when an ad finishes\n                // or collapses\n                this.handleEventListeners('CONTENT_RESUME_REQUESTED');\n\n                dispatchEvent('contentresume');\n\n                this.pollCountdown();\n\n                this.resumeContent();\n\n                break;\n\n            case google.ima.AdEvent.Type.STARTED:\n                dispatchEvent('started');\n                break;\n\n            case google.ima.AdEvent.Type.MIDPOINT:\n                dispatchEvent('midpoint');\n                break;\n\n            case google.ima.AdEvent.Type.COMPLETE:\n                dispatchEvent('complete');\n                break;\n\n            case google.ima.AdEvent.Type.IMPRESSION:\n                dispatchEvent('impression');\n                break;\n\n            case google.ima.AdEvent.Type.CLICK:\n                dispatchEvent('click');\n                break;\n\n            default:\n                break;\n        }\n    }\n\n    /**\n     * Any ad error handling comes through here\n     * @param {Event} event\n     */\n    onAdError(event) {\n        this.cancel();\n        this.player.debug.log('Ads error', event);\n    }\n\n    /**\n     * Setup hooks for Plyr and window events. This ensures\n     * the mid- and post-roll launch at the correct time. And\n     * resize the advertisement when the player resizes\n     */\n    listeners() {\n        const { container } = this.player.elements;\n        let time;\n\n        // Add listeners to the required events\n        this.player.on('ended', () => {\n            this.loader.contentComplete();\n        });\n\n        this.player.on('seeking', () => {\n            time = this.player.currentTime;\n            return time;\n        });\n\n        this.player.on('seeked', () => {\n            const seekedTime = this.player.currentTime;\n\n            this.cuePoints.forEach((cuePoint, index) => {\n                if (time < cuePoint && cuePoint < seekedTime) {\n                    this.manager.discardAdBreak();\n                    this.cuePoints.splice(index, 1);\n                }\n            });\n        });\n\n        // Listen to the resizing of the window. And resize ad accordingly\n        // TODO: eventually implement ResizeObserver\n        window.addEventListener('resize', () => {\n            this.manager.resize(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL);\n        });\n    }\n\n    /**\n     * Initialize the adsManager and start playing advertisements\n     */\n    play() {\n        const { container } = this.player.elements;\n\n        if (!this.managerPromise) {\n            return;\n        }\n\n        // Play the requested advertisement whenever the adsManager is ready\n        this.managerPromise.then(() => {\n            // Initialize the container. Must be done via a user action on mobile devices\n            this.elements.displayContainer.initialize();\n\n            try {\n                if (!this.initialized) {\n                    // Initialize the ads manager. Ad rules playlist will start at this time\n                    this.manager.init(container.offsetWidth, container.offsetHeight, google.ima.ViewMode.NORMAL);\n\n                    // Call play to start showing the ad. Single video and overlay ads will\n                    // start at this time; the call will be ignored for ad rules\n                    this.manager.start();\n                }\n\n                this.initialized = true;\n            } catch (adError) {\n                // An error may be thrown if there was a problem with the\n                // VAST response\n                this.onAdError(adError);\n            }\n        });\n    }\n\n    /**\n     * Resume our video.\n     */\n    resumeContent() {\n        // Hide our ad container\n        utils.toggleHidden(this.elements.container, true);\n\n        // Ad is stopped\n        this.playing = false;\n\n        // Play our video\n        if (this.player.currentTime < this.player.duration) {\n            this.player.play();\n        }\n    }\n\n    /**\n     * Pause our video\n     */\n    pauseContent() {\n        // Show our ad container.\n        utils.toggleHidden(this.elements.container, false);\n\n        // Ad is playing.\n        this.playing = true;\n\n        // Pause our video.\n        this.player.pause();\n    }\n\n    /**\n     * Destroy the adsManager so we can grab new ads after this. If we don't then we're not\n     * allowed to call new ads based on google policies, as they interpret this as an accidental\n     * video requests. https://developers.google.com/interactive-\n     * media-ads/docs/sdks/android/faq#8\n     */\n    cancel() {\n        // Pause our video\n        if (this.initialized) {\n            this.resumeContent();\n        }\n\n        // Tell our instance that we're done for now\n        this.handleEventListeners('ERROR');\n\n        // Re-create our adsManager\n        this.loadAds();\n    }\n\n    /**\n     * Re-create our adsManager\n     */\n    loadAds() {\n        // Tell our adsManager to go bye bye\n        this.managerPromise.then(() => {\n            // Destroy our adsManager\n            if (this.manager) {\n                this.manager.destroy();\n            }\n\n            // Re-set our adsManager promises\n            this.managerPromise = new Promise(resolve => {\n                this.on('ADS_MANAGER_LOADED', () => resolve());\n                this.player.debug.log(this.manager);\n            });\n\n            // Now request some new advertisements\n            this.requestAds();\n        });\n    }\n\n    /**\n     * Handles callbacks after an ad event was invoked\n     * @param {string} event - Event type\n     */\n    handleEventListeners(event) {\n        if (utils.is.function(this.events[event])) {\n            this.events[event].call(this);\n        }\n    }\n\n    /**\n     * Add event listeners\n     * @param {string} event - Event type\n     * @param {function} callback - Callback for when event occurs\n     * @return {Ads}\n     */\n    on(event, callback) {\n        this.events[event] = callback;\n        return this;\n    }\n\n    /**\n     * Setup a safety timer for when the ad network doesn't respond for whatever reason.\n     * The advertisement has 12 seconds to get its things together. We stop this timer when the\n     * advertisement is playing, or when a user action is required to start, then we clear the\n     * timer on ad ready\n     * @param {number} time\n     * @param {string} from\n     */\n    startSafetyTimer(time, from) {\n        this.player.debug.log(`Safety timer invoked from: ${from}`);\n\n        this.safetyTimer = window.setTimeout(() => {\n            this.cancel();\n            this.clearSafetyTimer('startSafetyTimer()');\n        }, time);\n    }\n\n    /**\n     * Clear our safety timer(s)\n     * @param {string} from\n     */\n    clearSafetyTimer(from) {\n        if (!utils.is.nullOrUndefined(this.safetyTimer)) {\n            this.player.debug.log(`Safety timer cleared from: ${from}`);\n\n            clearTimeout(this.safetyTimer);\n            this.safetyTimer = null;\n        }\n    }\n}\n\nexport default Ads;\n","// ==========================================================================\n// Plyr Event Listeners\n// ==========================================================================\n\nimport support from './support';\nimport utils from './utils';\nimport controls from './controls';\nimport ui from './ui';\n\n// Sniff out the browser\nconst browser = utils.getBrowser();\n\nconst listeners = {\n    // Global listeners\n    global() {\n        let last = null;\n\n        // Get the key code for an event\n        const getKeyCode = event => (event.keyCode ? event.keyCode : event.which);\n\n        // Handle key press\n        const handleKey = event => {\n            const code = getKeyCode(event);\n            const pressed = event.type === 'keydown';\n            const repeat = pressed && code === last;\n\n            // Bail if a modifier key is set\n            if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {\n                return;\n            }\n\n            // If the event is bubbled from the media element\n            // Firefox doesn't get the keycode for whatever reason\n            if (!utils.is.number(code)) {\n                return;\n            }\n\n            // Seek by the number keys\n            const seekByKey = () => {\n                // Divide the max duration into 10th's and times by the number value\n                this.currentTime = this.duration / 10 * (code - 48);\n            };\n\n            // Handle the key on keydown\n            // Reset on keyup\n            if (pressed) {\n                // Which keycodes should we prevent default\n                const preventDefault = [\n                    48,\n                    49,\n                    50,\n                    51,\n                    52,\n                    53,\n                    54,\n                    56,\n                    57,\n                    32,\n                    75,\n                    38,\n                    40,\n                    77,\n                    39,\n                    37,\n                    70,\n                    67,\n                    73,\n                    76,\n                    79,\n                ];\n\n                // Check focused element\n                // and if the focused element is not editable (e.g. text input)\n                // and any that accept key input http://webaim.org/techniques/keyboard/\n                const focused = utils.getFocusElement();\n                if (utils.is.element(focused) && utils.matches(focused, this.config.selectors.editable)) {\n                    return;\n                }\n\n                // If the code is found prevent default (e.g. prevent scrolling for arrows)\n                if (preventDefault.includes(code)) {\n                    event.preventDefault();\n                    event.stopPropagation();\n                }\n\n                switch (code) {\n                    case 48:\n                    case 49:\n                    case 50:\n                    case 51:\n                    case 52:\n                    case 53:\n                    case 54:\n                    case 55:\n                    case 56:\n                    case 57:\n                        // 0-9\n                        if (!repeat) {\n                            seekByKey();\n                        }\n                        break;\n\n                    case 32:\n                    case 75:\n                        // Space and K key\n                        if (!repeat) {\n                            this.togglePlay();\n                        }\n                        break;\n\n                    case 38:\n                        // Arrow up\n                        this.increaseVolume(0.1);\n                        break;\n\n                    case 40:\n                        // Arrow down\n                        this.decreaseVolume(0.1);\n                        break;\n\n                    case 77:\n                        // M key\n                        if (!repeat) {\n                            this.muted = !this.muted;\n                        }\n                        break;\n\n                    case 39:\n                        // Arrow forward\n                        this.forward();\n                        break;\n\n                    case 37:\n                        // Arrow back\n                        this.rewind();\n                        break;\n\n                    case 70:\n                        // F key\n                        this.fullscreen.toggle();\n                        break;\n\n                    case 67:\n                        // C key\n                        if (!repeat) {\n                            this.toggleCaptions();\n                        }\n                        break;\n\n                    case 76:\n                        // L key\n                        this.loop = !this.loop;\n                        break;\n\n                    /* case 73:\n                        this.setLoop('start');\n                        break;\n\n                    case 76:\n                        this.setLoop();\n                        break;\n\n                    case 79:\n                        this.setLoop('end');\n                        break; */\n\n                    default:\n                        break;\n                }\n\n                // Escape is handle natively when in full screen\n                // So we only need to worry about non native\n                if (!this.fullscreen.enabled && this.fullscreen.active && code === 27) {\n                    this.fullscreen.toggle();\n                }\n\n                // Store last code for next cycle\n                last = code;\n            } else {\n                last = null;\n            }\n        };\n\n        // Keyboard shortcuts\n        if (this.config.keyboard.global) {\n            utils.on(window, 'keydown keyup', handleKey, false);\n        } else if (this.config.keyboard.focused) {\n            utils.on(this.elements.container, 'keydown keyup', handleKey, false);\n        }\n\n        // Detect tab focus\n        // Remove class on blur/focusout\n        utils.on(this.elements.container, 'focusout', event => {\n            utils.toggleClass(event.target, this.config.classNames.tabFocus, false);\n        });\n\n        // Add classname to tabbed elements\n        utils.on(this.elements.container, 'keydown', event => {\n            if (event.keyCode !== 9) {\n                return;\n            }\n\n            // Delay the adding of classname until the focus has changed\n            // This event fires before the focusin event\n            window.setTimeout(() => {\n                utils.toggleClass(utils.getFocusElement(), this.config.classNames.tabFocus, true);\n            }, 0);\n        });\n\n        // Toggle controls visibility based on mouse movement\n        if (this.config.hideControls) {\n            // Toggle controls on mouse events and entering fullscreen\n            utils.on(this.elements.container, 'mouseenter mouseleave mousemove touchstart touchend touchmove enterfullscreen exitfullscreen', event => {\n                this.toggleControls(event);\n            });\n        }\n    },\n\n    // Listen for media events\n    media() {\n        // Time change on media\n        utils.on(this.media, 'timeupdate seeking', event => ui.timeUpdate.call(this, event));\n\n        // Display duration\n        utils.on(this.media, 'durationchange loadedmetadata', event => ui.durationUpdate.call(this, event));\n\n        // Check for audio tracks on load\n        // We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point\n        utils.on(this.media, 'loadeddata', () => {\n            utils.toggleHidden(this.elements.volume, !this.hasAudio);\n            utils.toggleHidden(this.elements.buttons.mute, !this.hasAudio);\n        });\n\n        // Handle the media finishing\n        utils.on(this.media, 'ended', () => {\n            // Show poster on end\n            if (this.isHTML5 && this.isVideo && this.config.showPosterOnEnd) {\n                // Restart\n                this.restart();\n\n                // Re-load media\n                this.media.load();\n            }\n        });\n\n        // Check for buffer progress\n        utils.on(this.media, 'progress playing', event => ui.updateProgress.call(this, event));\n\n        // Handle native mute\n        utils.on(this.media, 'volumechange', event => ui.updateVolume.call(this, event));\n\n        // Handle native play/pause\n        utils.on(this.media, 'playing play pause ended', event => ui.checkPlaying.call(this, event));\n\n        // Loading\n        utils.on(this.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(this, event));\n\n        // Check if media failed to load\n        // utils.on(this.media, 'play', event => ui.checkFailed.call(this, event));\n\n        // Click video\n        if (this.supported.ui && this.config.clickToPlay && !this.isAudio) {\n            // Re-fetch the wrapper\n            const wrapper = utils.getElement.call(this, `.${this.config.classNames.video}`);\n\n            // Bail if there's no wrapper (this should never happen)\n            if (!utils.is.element(wrapper)) {\n                return;\n            }\n\n            // On click play, pause ore restart\n            utils.on(wrapper, 'click', () => {\n                // Touch devices will just show controls (if we're hiding controls)\n                if (this.config.hideControls && support.touch && !this.paused) {\n                    return;\n                }\n\n                if (this.paused) {\n                    this.play();\n                } else if (this.ended) {\n                    this.restart();\n                    this.play();\n                } else {\n                    this.pause();\n                }\n            });\n        }\n\n        // Disable right click\n        if (this.supported.ui && this.config.disableContextMenu) {\n            utils.on(\n                this.media,\n                'contextmenu',\n                event => {\n                    event.preventDefault();\n                },\n                false,\n            );\n        }\n\n        // Volume change\n        utils.on(this.media, 'volumechange', () => {\n            // Save to storage\n            this.storage.set({ volume: this.volume, muted: this.muted });\n        });\n\n        // Speed change\n        utils.on(this.media, 'ratechange', () => {\n            // Update UI\n            controls.updateSetting.call(this, 'speed');\n\n            // Save to storage\n            this.storage.set({ speed: this.speed });\n        });\n\n        // Quality change\n        utils.on(this.media, 'qualitychange', () => {\n            // Update UI\n            controls.updateSetting.call(this, 'quality');\n\n            // Save to storage\n            this.storage.set({ quality: this.quality });\n        });\n\n        // Caption language change\n        utils.on(this.media, 'languagechange', () => {\n            // Update UI\n            controls.updateSetting.call(this, 'captions');\n\n            // Save to storage\n            this.storage.set({ language: this.language });\n        });\n\n        // Captions toggle\n        utils.on(this.media, 'captionsenabled captionsdisabled', () => {\n            // Update UI\n            controls.updateSetting.call(this, 'captions');\n\n            // Save to storage\n            this.storage.set({ captions: this.captions.active });\n        });\n\n        // Proxy events to container\n        // Bubble up key events for Edge\n        utils.on(this.media, this.config.events.concat([\n            'keyup',\n            'keydown',\n        ]).join(' '), event => {\n            let detail = {};\n\n            // Get error details from media\n            if (event.type === 'error') {\n                detail = this.media.error;\n            }\n\n            utils.dispatchEvent.call(this, this.elements.container, event.type, true, detail);\n        });\n    },\n\n    // Listen for control events\n    controls() {\n        // IE doesn't support input event, so we fallback to change\n        const inputEvent = browser.isIE ? 'change' : 'input';\n\n        // Trigger custom and default handlers\n        const proxy = (event, handlerKey, defaultHandler) => {\n            const customHandler = this.config.listeners[handlerKey];\n\n            // Execute custom handler\n            if (utils.is.function(customHandler)) {\n                customHandler.call(this, event);\n            }\n\n            // Only call default handler if not prevented in custom handler\n            if (!event.defaultPrevented && utils.is.function(defaultHandler)) {\n                defaultHandler.call(this, event);\n            }\n        };\n\n        // Play/pause toggle\n        utils.on(this.elements.buttons.play, 'click', event =>\n            proxy(event, 'play', () => {\n                this.togglePlay();\n            }),\n        );\n\n        // Pause\n        utils.on(this.elements.buttons.restart, 'click', event =>\n            proxy(event, 'restart', () => {\n                this.restart();\n            }),\n        );\n\n        // Rewind\n        utils.on(this.elements.buttons.rewind, 'click', event =>\n            proxy(event, 'rewind', () => {\n                this.rewind();\n            }),\n        );\n\n        // Rewind\n        utils.on(this.elements.buttons.forward, 'click', event =>\n            proxy(event, 'forward', () => {\n                this.forward();\n            }),\n        );\n\n        // Mute toggle\n        utils.on(this.elements.buttons.mute, 'click', event =>\n            proxy(event, 'mute', () => {\n                this.muted = !this.muted;\n            }),\n        );\n\n        // Captions toggle\n        utils.on(this.elements.buttons.captions, 'click', event =>\n            proxy(event, 'captions', () => {\n                this.toggleCaptions();\n            }),\n        );\n\n        // Fullscreen toggle\n        utils.on(this.elements.buttons.fullscreen, 'click', event =>\n            proxy(event, 'fullscreen', () => {\n                this.fullscreen.toggle();\n            }),\n        );\n\n        // Picture-in-Picture\n        utils.on(this.elements.buttons.pip, 'click', event =>\n            proxy(event, 'pip', () => {\n                this.pip = 'toggle';\n            }),\n        );\n\n        // Airplay\n        utils.on(this.elements.buttons.airplay, 'click', event =>\n            proxy(event, 'airplay', () => {\n                this.airplay();\n            }),\n        );\n\n        // Settings menu\n        utils.on(this.elements.buttons.settings, 'click', event => {\n            controls.toggleMenu.call(this, event);\n        });\n\n        // Click anywhere closes menu\n        utils.on(document.documentElement, 'click', event => {\n            controls.toggleMenu.call(this, event);\n        });\n\n        // Settings menu\n        utils.on(this.elements.settings.form, 'click', event => {\n            event.stopPropagation();\n\n            // Settings menu items - use event delegation as items are added/removed\n            if (utils.matches(event.target, this.config.selectors.inputs.language)) {\n                proxy(event, 'language', () => {\n                    this.language = event.target.value;\n                });\n            } else if (utils.matches(event.target, this.config.selectors.inputs.quality)) {\n                proxy(event, 'quality', () => {\n                    this.quality = event.target.value;\n                });\n            } else if (utils.matches(event.target, this.config.selectors.inputs.speed)) {\n                proxy(event, 'speed', () => {\n                    this.speed = parseFloat(event.target.value);\n                });\n            } else {\n                controls.showTab.call(this, event);\n            }\n        });\n\n        // Seek\n        utils.on(this.elements.inputs.seek, inputEvent, event =>\n            proxy(event, 'seek', () => {\n                this.currentTime = event.target.value / event.target.max * this.duration;\n            }),\n        );\n\n        // Current time invert\n        // Only if one time element is used for both currentTime and duration\n        if (this.config.toggleInvert && !utils.is.element(this.elements.display.duration)) {\n            utils.on(this.elements.display.currentTime, 'click', () => {\n                // Do nothing if we're at the start\n                if (this.currentTime === 0) {\n                    return;\n                }\n\n                this.config.invertTime = !this.config.invertTime;\n                ui.timeUpdate.call(this);\n            });\n        }\n\n        // Volume\n        utils.on(this.elements.inputs.volume, inputEvent, event =>\n            proxy(event, 'volume', () => {\n                this.volume = event.target.value;\n            }),\n        );\n\n        // Polyfill for lower fill in <input type=\"range\"> for webkit\n        if (browser.isWebkit) {\n            utils.on(utils.getElements.call(this, 'input[type=\"range\"]'), 'input', event => {\n                controls.updateRangeFill.call(this, event.target);\n            });\n        }\n\n        // Seek tooltip\n        utils.on(this.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this, event));\n\n        // Toggle controls visibility based on mouse movement\n        if (this.config.hideControls) {\n            // Watch for cursor over controls so they don't hide when trying to interact\n            utils.on(this.elements.controls, 'mouseenter mouseleave', event => {\n                this.elements.controls.hover = event.type === 'mouseenter';\n            });\n\n            // Watch for cursor over controls so they don't hide when trying to interact\n            utils.on(this.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {\n                this.elements.controls.pressed = [\n                    'mousedown',\n                    'touchstart',\n                ].includes(event.type);\n            });\n\n            // Focus in/out on controls\n            utils.on(this.elements.controls, 'focusin focusout', event => {\n                this.toggleControls(event);\n            });\n        }\n\n        // Mouse wheel for volume\n        utils.on(\n            this.elements.inputs.volume,\n            'wheel',\n            event =>\n                proxy(event, 'volume', () => {\n                    // Detect \"natural\" scroll - suppored on OS X Safari only\n                    // Other browsers on OS X will be inverted until support improves\n                    const inverted = event.webkitDirectionInvertedFromDevice;\n                    const step = 1 / 50;\n                    let direction = 0;\n\n                    // Scroll down (or up on natural) to decrease\n                    if (event.deltaY < 0 || event.deltaX > 0) {\n                        if (inverted) {\n                            this.decreaseVolume(step);\n                            direction = -1;\n                        } else {\n                            this.increaseVolume(step);\n                            direction = 1;\n                        }\n                    }\n\n                    // Scroll up (or down on natural) to increase\n                    if (event.deltaY > 0 || event.deltaX < 0) {\n                        if (inverted) {\n                            this.increaseVolume(step);\n                            direction = 1;\n                        } else {\n                            this.decreaseVolume(step);\n                            direction = -1;\n                        }\n                    }\n\n                    // Don't break page scrolling at max and min\n                    if ((direction === 1 && this.media.volume < 1) || (direction === -1 && this.media.volume > 0)) {\n                        event.preventDefault();\n                    }\n                }),\n            false,\n        );\n    },\n};\n\nexport default listeners;\n","// ==========================================================================\n// Plyr UI\n// ==========================================================================\n\nimport utils from './utils';\nimport captions from './captions';\nimport controls from './controls';\nimport listeners from './listeners';\n\nconst ui = {\n    addStyleHook() {\n        utils.toggleClass(this.elements.container, this.config.selectors.container.replace('.', ''), true);\n        utils.toggleClass(this.elements.container, this.config.classNames.uiSupported, this.supported.ui);\n    },\n\n    // Toggle native HTML5 media controls\n    toggleNativeControls(toggle = false) {\n        if (toggle && this.isHTML5) {\n            this.media.setAttribute('controls', '');\n        } else {\n            this.media.removeAttribute('controls');\n        }\n    },\n\n    // Setup the UI\n    build() {\n        // Re-attach media element listeners\n        // TODO: Use event bubbling\n        listeners.media.call(this);\n\n        // Don't setup interface if no support\n        if (!this.supported.ui) {\n            this.debug.warn(`Basic support only for ${this.provider} ${this.type}`);\n\n            // Restore native controls\n            ui.toggleNativeControls.call(this, true);\n\n            // Bail\n            return;\n        }\n\n        // Inject custom controls if not present\n        if (!utils.is.element(this.elements.controls)) {\n            // Inject custom controls\n            controls.inject.call(this);\n\n            // Re-attach control listeners\n            listeners.controls.call(this);\n        }\n\n        // If there's no controls, bail\n        if (!utils.is.element(this.elements.controls)) {\n            return;\n        }\n\n        // Remove native controls\n        ui.toggleNativeControls.call(this);\n\n        // Captions\n        captions.setup.call(this);\n\n        // Reset volume\n        this.volume = null;\n\n        // Reset mute state\n        this.muted = null;\n\n        // Reset speed\n        this.speed = null;\n\n        // Reset loop state\n        this.loop = null;\n\n        // Reset quality options\n        this.options.quality = [];\n\n        // Reset time display\n        ui.timeUpdate.call(this);\n\n        // Update the UI\n        ui.checkPlaying.call(this);\n\n        // Ready for API calls\n        this.ready = true;\n\n        // Ready event at end of execution stack\n        utils.dispatchEvent.call(this, this.media, 'ready');\n\n        // Set the title\n        ui.setTitle.call(this);\n    },\n\n    // Setup aria attribute for play and iframe title\n    setTitle() {\n        // Find the current text\n        let label = this.config.i18n.play;\n\n        // If there's a media title set, use that for the label\n        if (utils.is.string(this.config.title) && !utils.is.empty(this.config.title)) {\n            label += `, ${this.config.title}`;\n\n            // Set container label\n            this.elements.container.setAttribute('aria-label', this.config.title);\n        }\n\n        // If there's a play button, set label\n        if (utils.is.nodeList(this.elements.buttons.play)) {\n            Array.from(this.elements.buttons.play).forEach(button => {\n                button.setAttribute('aria-label', label);\n            });\n        }\n\n        // Set iframe title\n        // https://github.com/sampotts/plyr/issues/124\n        if (this.isEmbed) {\n            const iframe = utils.getElement.call(this, 'iframe');\n\n            if (!utils.is.element(iframe)) {\n                return;\n            }\n\n            // Default to media type\n            const title = !utils.is.empty(this.config.title) ? this.config.title : 'video';\n\n            iframe.setAttribute('title', this.config.i18n.frameTitle.replace('{title}', title));\n        }\n    },\n\n    // Check playing state\n    checkPlaying() {\n        // Class hooks\n        utils.toggleClass(this.elements.container, this.config.classNames.playing, this.playing);\n        utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.paused);\n\n        // Set aria state\n        if (utils.is.nodeList(this.elements.buttons.play)) {\n            Array.from(this.elements.buttons.play).forEach(button => utils.toggleState(button, this.playing));\n        }\n\n        // Toggle controls\n        this.toggleControls(!this.playing);\n    },\n\n    // Check if media is loading\n    checkLoading(event) {\n        this.loading = [\n            'stalled',\n            'waiting',\n        ].includes(event.type);\n\n        // Clear timer\n        clearTimeout(this.timers.loading);\n\n        // Timer to prevent flicker when seeking\n        this.timers.loading = setTimeout(() => {\n            // Toggle container class hook\n            utils.toggleClass(this.elements.container, this.config.classNames.loading, this.loading);\n\n            // Show controls if loading, hide if done\n            this.toggleControls(this.loading);\n        }, this.loading ? 250 : 0);\n    },\n\n    // Check if media failed to load\n    checkFailed() {\n        // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/networkState\n        this.failed = this.media.networkState === 3;\n\n        if (this.failed) {\n            utils.toggleClass(this.elements.container, this.config.classNames.loading, false);\n            utils.toggleClass(this.elements.container, this.config.classNames.error, true);\n        }\n\n        // Clear timer\n        clearTimeout(this.timers.failed);\n\n        // Timer to prevent flicker when seeking\n        this.timers.loading = setTimeout(() => {\n            // Toggle container class hook\n            utils.toggleClass(this.elements.container, this.config.classNames.loading, this.loading);\n\n            // Show controls if loading, hide if done\n            this.toggleControls(this.loading);\n        }, this.loading ? 250 : 0);\n    },\n\n    // Update volume UI and storage\n    updateVolume() {\n        if (!this.supported.ui) {\n            return;\n        }\n\n        // Update range\n        if (utils.is.element(this.elements.inputs.volume)) {\n            ui.setRange.call(this, this.elements.inputs.volume, this.muted ? 0 : this.volume);\n        }\n\n        // Update mute state\n        if (utils.is.element(this.elements.buttons.mute)) {\n            utils.toggleState(this.elements.buttons.mute, this.muted || this.volume === 0);\n        }\n    },\n\n    // Update seek value and lower fill\n    setRange(target, value = 0) {\n        if (!utils.is.element(target)) {\n            return;\n        }\n\n        // eslint-disable-next-line\n        target.value = value;\n\n        // Webkit range fill\n        controls.updateRangeFill.call(this, target);\n    },\n\n    // Set <progress> value\n    setProgress(target, input) {\n        const value = utils.is.number(input) ? input : 0;\n        const progress = utils.is.element(target) ? target : this.elements.display.buffer;\n\n        // Update value and label\n        if (utils.is.element(progress)) {\n            progress.value = value;\n\n            // Update text label inside\n            const label = progress.getElementsByTagName('span')[0];\n            if (utils.is.element(label)) {\n                label.childNodes[0].nodeValue = value;\n            }\n        }\n    },\n\n    // Update <progress> elements\n    updateProgress(event) {\n        if (!this.supported.ui || !utils.is.event(event)) {\n            return;\n        }\n\n        let value = 0;\n\n        if (event) {\n            switch (event.type) {\n                // Video playing\n                case 'timeupdate':\n                case 'seeking':\n                    value = utils.getPercentage(this.currentTime, this.duration);\n\n                    // Set seek range value only if it's a 'natural' time event\n                    if (event.type === 'timeupdate') {\n                        ui.setRange.call(this, this.elements.inputs.seek, value);\n                    }\n\n                    break;\n\n                // Check buffer status\n                case 'playing':\n                case 'progress':\n                    value = (() => {\n                        const { buffered } = this.media;\n\n                        if (buffered && buffered.length) {\n                            // HTML5\n                            return utils.getPercentage(buffered.end(0), this.duration);\n                        } else if (utils.is.number(buffered)) {\n                            // YouTube returns between 0 and 1\n                            return buffered * 100;\n                        }\n\n                        return 0;\n                    })();\n\n                    ui.setProgress.call(this, this.elements.display.buffer, value);\n\n                    break;\n\n                default:\n                    break;\n            }\n        }\n    },\n\n    // Update the displayed time\n    updateTimeDisplay(target = null, time = 0, inverted = false) {\n        // Bail if there's no element to display or the value isn't a number\n        if (!utils.is.element(target) || !utils.is.number(time)) {\n            return;\n        }\n\n        // Always display hours if duration is over an hour\n        const displayHours = utils.getHours(this.duration) > 0;\n\n        // eslint-disable-next-line no-param-reassign\n        target.textContent = utils.formatTime(time, displayHours, inverted);\n    },\n\n    // Handle time change event\n    timeUpdate(event) {\n        // Only invert if only one time element is displayed and used for both duration and currentTime\n        const invert = !utils.is.element(this.elements.display.duration) && this.config.invertTime;\n\n        // Duration\n        ui.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert);\n\n        // Ignore updates while seeking\n        if (event && event.type === 'timeupdate' && this.media.seeking) {\n            return;\n        }\n\n        // Playing progress\n        ui.updateProgress.call(this, event);\n    },\n\n    // Show the duration on metadataloaded\n    durationUpdate() {\n        if (!this.supported.ui) {\n            return;\n        }\n\n        // If there's a spot to display duration\n        const hasDuration = utils.is.element(this.elements.display.duration);\n\n        // If there's only one time display, display duration there\n        if (!hasDuration && this.config.displayDuration && this.paused) {\n            ui.updateTimeDisplay.call(this, this.elements.display.currentTime, this.duration);\n        }\n\n        // If there's a duration element, update content\n        if (hasDuration) {\n            ui.updateTimeDisplay.call(this, this.elements.display.duration, this.duration);\n        }\n\n        // Update the tooltip (if visible)\n        controls.updateSeekTooltip.call(this);\n    },\n};\n\nexport default ui;\n","// ==========================================================================\n// Plyr controls\n// ==========================================================================\n\nimport support from './support';\nimport utils from './utils';\nimport ui from './ui';\nimport captions from './captions';\n\n// Sniff out the browser\nconst browser = utils.getBrowser();\n\nconst controls = {\n    // Webkit polyfill for lower fill range\n    updateRangeFill(target) {\n        // WebKit only\n        if (!browser.isWebkit) {\n            return;\n        }\n\n        // Get range from event if event passed\n        const range = utils.is.event(target) ? target.target : target;\n\n        // Needs to be a valid <input type='range'>\n        if (!utils.is.element(range) || range.getAttribute('type') !== 'range') {\n            return;\n        }\n\n        // Set CSS custom property\n        range.style.setProperty('--value', `${range.value / range.max * 100}%`);\n    },\n\n    // Get icon URL\n    getIconUrl() {\n        return {\n            url: this.config.iconUrl,\n            absolute: this.config.iconUrl.indexOf('http') === 0 || (browser.isIE && !window.svg4everybody),\n        };\n    },\n\n    // Create <svg> icon\n    createIcon(type, attributes) {\n        const namespace = 'http://www.w3.org/2000/svg';\n        const iconUrl = controls.getIconUrl.call(this);\n        const iconPath = `${!iconUrl.absolute ? iconUrl.url : ''}#${this.config.iconPrefix}`;\n\n        // Create <svg>\n        const icon = document.createElementNS(namespace, 'svg');\n        utils.setAttributes(\n            icon,\n            utils.extend(attributes, {\n                role: 'presentation',\n            })\n        );\n\n        // Create the <use> to reference sprite\n        const use = document.createElementNS(namespace, 'use');\n        const path = `${iconPath}-${type}`;\n\n        // Set `href` attributes\n        // https://github.com/sampotts/plyr/issues/460\n        // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href\n        if ('href' in use) {\n            use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);\n        } else {\n            use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);\n        }\n\n        // Add <use> to <svg>\n        icon.appendChild(use);\n\n        return icon;\n    },\n\n    // Create hidden text label\n    createLabel(type, attr) {\n        let text = this.config.i18n[type];\n        const attributes = Object.assign({}, attr);\n\n        switch (type) {\n            case 'pip':\n                text = 'PIP';\n                break;\n\n            case 'airplay':\n                text = 'AirPlay';\n                break;\n\n            default:\n                break;\n        }\n\n        if ('class' in attributes) {\n            attributes.class += ` ${this.config.classNames.hidden}`;\n        } else {\n            attributes.class = this.config.classNames.hidden;\n        }\n\n        return utils.createElement('span', attributes, text);\n    },\n\n    // Create a badge\n    createBadge(text) {\n        if (utils.is.empty(text)) {\n            return null;\n        }\n\n        const badge = utils.createElement('span', {\n            class: this.config.classNames.menu.value,\n        });\n\n        badge.appendChild(\n            utils.createElement(\n                'span',\n                {\n                    class: this.config.classNames.menu.badge,\n                },\n                text\n            )\n        );\n\n        return badge;\n    },\n\n    // Create a <button>\n    createButton(buttonType, attr) {\n        const button = utils.createElement('button');\n        const attributes = Object.assign({}, attr);\n        let type = buttonType;\n\n        let toggle = false;\n        let label;\n        let icon;\n        let labelPressed;\n        let iconPressed;\n\n        if (!('type' in attributes)) {\n            attributes.type = 'button';\n        }\n\n        if ('class' in attributes) {\n            if (attributes.class.includes(this.config.classNames.control)) {\n                attributes.class += ` ${this.config.classNames.control}`;\n            }\n        } else {\n            attributes.class = this.config.classNames.control;\n        }\n\n        // Large play button\n        switch (type) {\n            case 'play':\n                toggle = true;\n                label = 'play';\n                labelPressed = 'pause';\n                icon = 'play';\n                iconPressed = 'pause';\n                break;\n\n            case 'mute':\n                toggle = true;\n                label = 'mute';\n                labelPressed = 'unmute';\n                icon = 'volume';\n                iconPressed = 'muted';\n                break;\n\n            case 'captions':\n                toggle = true;\n                label = 'enableCaptions';\n                labelPressed = 'disableCaptions';\n                icon = 'captions-off';\n                iconPressed = 'captions-on';\n                break;\n\n            case 'fullscreen':\n                toggle = true;\n                label = 'enterFullscreen';\n                labelPressed = 'exitFullscreen';\n                icon = 'enter-fullscreen';\n                iconPressed = 'exit-fullscreen';\n                break;\n\n            case 'play-large':\n                attributes.class += ` ${this.config.classNames.control}--overlaid`;\n                type = 'play';\n                label = 'play';\n                icon = 'play';\n                break;\n\n            default:\n                label = type;\n                icon = type;\n        }\n\n        // Setup toggle icon and labels\n        if (toggle) {\n            // Icon\n            button.appendChild(controls.createIcon.call(this, iconPressed, { class: 'icon--pressed' }));\n            button.appendChild(controls.createIcon.call(this, icon, { class: 'icon--not-pressed' }));\n\n            // Label/Tooltip\n            button.appendChild(controls.createLabel.call(this, labelPressed, { class: 'label--pressed' }));\n            button.appendChild(controls.createLabel.call(this, label, { class: 'label--not-pressed' }));\n\n            // Add aria attributes\n            attributes['aria-pressed'] = false;\n            attributes['aria-label'] = this.config.i18n[label];\n        } else {\n            button.appendChild(controls.createIcon.call(this, icon));\n            button.appendChild(controls.createLabel.call(this, label));\n        }\n\n        // Merge attributes\n        utils.extend(attributes, utils.getAttributesFromSelector(this.config.selectors.buttons[type], attributes));\n\n        utils.setAttributes(button, attributes);\n\n        // We have multiple play buttons\n        if (type === 'play') {\n            if (!utils.is.array(this.elements.buttons[type])) {\n                this.elements.buttons[type] = [];\n            }\n\n            this.elements.buttons[type].push(button);\n        } else {\n            this.elements.buttons[type] = button;\n        }\n\n        return button;\n    },\n\n    // Create an <input type='range'>\n    createRange(type, attributes) {\n        // Seek label\n        const label = utils.createElement(\n            'label',\n            {\n                for: attributes.id,\n                class: this.config.classNames.hidden,\n            },\n            this.config.i18n[type]\n        );\n\n        // Seek input\n        const input = utils.createElement(\n            'input',\n            utils.extend(\n                utils.getAttributesFromSelector(this.config.selectors.inputs[type]),\n                {\n                    type: 'range',\n                    min: 0,\n                    max: 100,\n                    step: 0.01,\n                    value: 0,\n                    autocomplete: 'off',\n                },\n                attributes\n            )\n        );\n\n        this.elements.inputs[type] = input;\n\n        // Set the fill for webkit now\n        controls.updateRangeFill.call(this, input);\n\n        return {\n            label,\n            input,\n        };\n    },\n\n    // Create a <progress>\n    createProgress(type, attributes) {\n        const progress = utils.createElement(\n            'progress',\n            utils.extend(\n                utils.getAttributesFromSelector(this.config.selectors.display[type]),\n                {\n                    min: 0,\n                    max: 100,\n                    value: 0,\n                },\n                attributes\n            )\n        );\n\n        // Create the label inside\n        if (type !== 'volume') {\n            progress.appendChild(utils.createElement('span', null, '0'));\n\n            let suffix = '';\n            switch (type) {\n                case 'played':\n                    suffix = this.config.i18n.played;\n                    break;\n\n                case 'buffer':\n                    suffix = this.config.i18n.buffered;\n                    break;\n\n                default:\n                    break;\n            }\n\n            progress.textContent = `% ${suffix.toLowerCase()}`;\n        }\n\n        this.elements.display[type] = progress;\n\n        return progress;\n    },\n\n    // Create time display\n    createTime(type) {\n        const container = utils.createElement('div', {\n            class: 'plyr__time',\n        });\n\n        container.appendChild(\n            utils.createElement(\n                'span',\n                {\n                    class: this.config.classNames.hidden,\n                },\n                this.config.i18n[type]\n            )\n        );\n\n        container.appendChild(utils.createElement('span', utils.getAttributesFromSelector(this.config.selectors.display[type]), '00:00'));\n\n        this.elements.display[type] = container;\n\n        return container;\n    },\n\n    // Create a settings menu item\n    createMenuItem(value, list, type, title, badge = null, checked = false) {\n        const item = utils.createElement('li');\n\n        const label = utils.createElement('label', {\n            class: this.config.classNames.control,\n        });\n\n        const radio = utils.createElement(\n            'input',\n            utils.extend(utils.getAttributesFromSelector(this.config.selectors.inputs[type]), {\n                type: 'radio',\n                name: `plyr-${type}`,\n                value,\n                checked,\n                class: 'plyr__sr-only',\n            })\n        );\n\n        const faux = utils.createElement('span', { 'aria-hidden': true });\n\n        label.appendChild(radio);\n        label.appendChild(faux);\n        label.insertAdjacentHTML('beforeend', title);\n\n        if (utils.is.element(badge)) {\n            label.appendChild(badge);\n        }\n\n        item.appendChild(label);\n        list.appendChild(item);\n    },\n\n    // Update hover tooltip for seeking\n    updateSeekTooltip(event) {\n        // Bail if setting not true\n        if (\n            !this.config.tooltips.seek ||\n            !utils.is.element(this.elements.inputs.seek) ||\n            !utils.is.element(this.elements.display.seekTooltip) ||\n            this.duration === 0\n        ) {\n            return;\n        }\n\n        // Calculate percentage\n        let percent = 0;\n        const clientRect = this.elements.inputs.seek.getBoundingClientRect();\n        const visible = `${this.config.classNames.tooltip}--visible`;\n\n        // Determine percentage, if already visible\n        if (utils.is.event(event)) {\n            percent = 100 / clientRect.width * (event.pageX - clientRect.left);\n        } else if (utils.hasClass(this.elements.display.seekTooltip, visible)) {\n            percent = parseFloat(this.elements.display.seekTooltip.style.left, 10);\n        } else {\n            return;\n        }\n\n        // Set bounds\n        if (percent < 0) {\n            percent = 0;\n        } else if (percent > 100) {\n            percent = 100;\n        }\n\n        // Display the time a click would seek to\n        ui.updateTimeDisplay.call(this, this.elements.display.seekTooltip, this.duration / 100 * percent);\n\n        // Set position\n        this.elements.display.seekTooltip.style.left = `${percent}%`;\n\n        // Show/hide the tooltip\n        // If the event is a moues in/out and percentage is inside bounds\n        if (utils.is.event(event) && [\n            'mouseenter',\n            'mouseleave',\n        ].includes(event.type)) {\n            utils.toggleClass(this.elements.display.seekTooltip, visible, event.type === 'mouseenter');\n        }\n    },\n\n    // Hide/show a tab\n    toggleTab(setting, toggle) {\n        const tab = this.elements.settings.tabs[setting];\n        const pane = this.elements.settings.panes[setting];\n\n        utils.toggleHidden(tab, !toggle);\n        utils.toggleHidden(pane, !toggle);\n    },\n\n    // Set the YouTube quality menu\n    // TODO: Support for HTML5\n    setQualityMenu(options) {\n        const type = 'quality';\n        const list = this.elements.settings.panes.quality.querySelector('ul');\n\n        // Set options if passed and filter based on config\n        if (utils.is.array(options)) {\n            this.options.quality = options.filter(quality => this.config.quality.options.includes(quality));\n        } else {\n            this.options.quality = this.config.quality.options;\n        }\n\n        // Toggle the pane and tab\n        const toggle = !utils.is.empty(this.options.quality) && this.isYouTube;\n        controls.toggleTab.call(this, type, toggle);\n\n        // If we're hiding, nothing more to do\n        if (!toggle) {\n            return;\n        }\n\n        // Empty the menu\n        utils.emptyElement(list);\n\n        // Get the badge HTML for HD, 4K etc\n        const getBadge = quality => {\n            let label = '';\n\n            switch (quality) {\n                case 'hd2160':\n                    label = '4K';\n                    break;\n\n                case 'hd1440':\n                    label = 'WQHD';\n                    break;\n\n                case 'hd1080':\n                    label = 'HD';\n                    break;\n\n                case 'hd720':\n                    label = 'HD';\n                    break;\n\n                default:\n                    break;\n            }\n\n            if (!label.length) {\n                return null;\n            }\n\n            return controls.createBadge.call(this, label);\n        };\n\n        this.options.quality.forEach(quality =>\n            controls.createMenuItem.call(this, quality, list, type, controls.getLabel.call(this, 'quality', quality), getBadge(quality))\n        );\n\n        controls.updateSetting.call(this, type, list);\n    },\n\n    // Translate a value into a nice label\n    // TODO: Localisation\n    getLabel(setting, value) {\n        switch (setting) {\n            case 'speed':\n                return value === 1 ? 'Normal' : `${value}&times;`;\n\n            case 'quality':\n                switch (value) {\n                    case 'hd2160':\n                        return '2160P';\n                    case 'hd1440':\n                        return '1440P';\n                    case 'hd1080':\n                        return '1080P';\n                    case 'hd720':\n                        return '720P';\n                    case 'large':\n                        return '480P';\n                    case 'medium':\n                        return '360P';\n                    case 'small':\n                        return '240P';\n                    case 'tiny':\n                        return 'Tiny';\n                    case 'default':\n                        return 'Auto';\n                    default:\n                        return value;\n                }\n\n            case 'captions':\n                return controls.getLanguage.call(this);\n\n            default:\n                return null;\n        }\n    },\n\n    // Update the selected setting\n    updateSetting(setting, container) {\n        const pane = this.elements.settings.panes[setting];\n        let value = null;\n        let list = container;\n\n        switch (setting) {\n            case 'captions':\n                value = this.captions.active ? this.captions.language : '';\n                break;\n\n            default:\n                value = this[setting];\n\n                // Get default\n                if (utils.is.empty(value)) {\n                    value = this.config[setting].default;\n                }\n\n                // Unsupported value\n                if (!this.options[setting].includes(value)) {\n                    this.debug.warn(`Unsupported value of '${value}' for ${setting}`);\n                    return;\n                }\n\n                // Disabled value\n                if (!this.config[setting].options.includes(value)) {\n                    this.debug.warn(`Disabled value of '${value}' for ${setting}`);\n                    return;\n                }\n\n                break;\n        }\n\n        // Get the list if we need to\n        if (!utils.is.element(list)) {\n            list = pane && pane.querySelector('ul');\n        }\n\n        // Update the label\n        if (!utils.is.empty(value)) {\n            const label = this.elements.settings.tabs[setting].querySelector(`.${this.config.classNames.menu.value}`);\n            label.innerHTML = controls.getLabel.call(this, setting, value);\n        }\n\n        // Find the radio option\n        const target = list && list.querySelector(`input[value=\"${value}\"]`);\n\n        if (utils.is.element(target)) {\n            // Check it\n            target.checked = true;\n        }\n    },\n\n    // Set the looping options\n    /* setLoopMenu() {\n        const options = ['start', 'end', 'all', 'reset'];\n        const list = this.elements.settings.panes.loop.querySelector('ul');\n\n        // Show the pane and tab\n        utils.toggleHidden(this.elements.settings.tabs.loop, false);\n        utils.toggleHidden(this.elements.settings.panes.loop, false);\n\n        // Toggle the pane and tab\n        const toggle = !utils.is.empty(this.loop.options);\n        controls.toggleTab.call(this, 'loop', toggle);\n\n        // Empty the menu\n        utils.emptyElement(list);\n\n        options.forEach(option => {\n            const item = utils.createElement('li');\n\n            const button = utils.createElement(\n                'button',\n                utils.extend(utils.getAttributesFromSelector(this.config.selectors.buttons.loop), {\n                    type: 'button',\n                    class: this.config.classNames.control,\n                    'data-plyr-loop-action': option,\n                }),\n                this.config.i18n[option]\n            );\n\n            if (['start', 'end'].includes(option)) {\n                const badge = controls.createBadge.call(this, '00:00');\n                button.appendChild(badge);\n            }\n\n            item.appendChild(button);\n            list.appendChild(item);\n        });\n    }, */\n\n    // Get current selected caption language\n    // TODO: rework this to user the getter in the API?\n    getLanguage() {\n        if (!this.supported.ui) {\n            return null;\n        }\n\n        if (!support.textTracks || !captions.getTracks.call(this).length) {\n            return this.config.i18n.none;\n        }\n\n        if (this.captions.active) {\n            const currentTrack = captions.getCurrentTrack.call(this);\n\n            if (utils.is.track(currentTrack)) {\n                return currentTrack.label;\n            }\n        }\n\n        return this.config.i18n.disabled;\n    },\n\n    // Set a list of available captions languages\n    setCaptionsMenu() {\n        // TODO: Captions or language? Currently it's mixed\n        const type = 'captions';\n        const list = this.elements.settings.panes.captions.querySelector('ul');\n\n        // Toggle the pane and tab\n        const hasTracks = captions.getTracks.call(this).length;\n        controls.toggleTab.call(this, type, hasTracks);\n\n        // Empty the menu\n        utils.emptyElement(list);\n\n        // If there's no captions, bail\n        if (!hasTracks) {\n            return;\n        }\n\n        // Re-map the tracks into just the data we need\n        const tracks = captions.getTracks.call(this).map(track => ({\n            language: track.language,\n            label: !utils.is.empty(track.label) ? track.label : track.language.toUpperCase(),\n        }));\n\n        // Add the \"None\" option to turn off captions\n        tracks.unshift({\n            language: '',\n            label: this.config.i18n.none,\n        });\n\n        // Generate options\n        tracks.forEach(track => {\n            controls.createMenuItem.call(\n                this,\n                track.language,\n                list,\n                'language',\n                track.label || track.language,\n                controls.createBadge.call(this, track.language.toUpperCase()),\n                track.language.toLowerCase() === this.captions.language.toLowerCase()\n            );\n        });\n\n        controls.updateSetting.call(this, type, list);\n    },\n\n    // Set a list of available captions languages\n    setSpeedMenu() {\n        const type = 'speed';\n\n        // Set the default speeds\n        if (!utils.is.object(this.options.speed) || !Object.keys(this.options.speed).length) {\n            this.options.speed = [\n                0.5,\n                0.75,\n                1,\n                1.25,\n                1.5,\n                1.75,\n                2,\n            ];\n        }\n\n        // Set options if passed and filter based on config\n        this.options.speed = this.options.speed.filter(speed => this.config.speed.options.includes(speed));\n\n        // Toggle the pane and tab\n        const toggle = !utils.is.empty(this.options.speed);\n        controls.toggleTab.call(this, type, toggle);\n\n        // If we're hiding, nothing more to do\n        if (!toggle) {\n            return;\n        }\n\n        // Get the list to populate\n        const list = this.elements.settings.panes.speed.querySelector('ul');\n\n        // Show the pane and tab\n        utils.toggleHidden(this.elements.settings.tabs.speed, false);\n        utils.toggleHidden(this.elements.settings.panes.speed, false);\n\n        // Empty the menu\n        utils.emptyElement(list);\n\n        // Create items\n        this.options.speed.forEach(speed => controls.createMenuItem.call(this, speed, list, type, controls.getLabel.call(this, 'speed', speed)));\n\n        controls.updateSetting.call(this, type, list);\n    },\n\n    // Show/hide menu\n    toggleMenu(event) {\n        const { form } = this.elements.settings;\n        const button = this.elements.buttons.settings;\n        const show = utils.is.boolean(event) ? event : utils.is.element(form) && form.getAttribute('aria-hidden') === 'true';\n\n        if (utils.is.event(event)) {\n            const isMenuItem = utils.is.element(form) && form.contains(event.target);\n            const isButton = event.target === this.elements.buttons.settings;\n\n            // If the click was inside the form or if the click\n            // wasn't the button or menu item and we're trying to\n            // show the menu (a doc click shouldn't show the menu)\n            if (isMenuItem || (!isMenuItem && !isButton && show)) {\n                return;\n            }\n\n            // Prevent the toggle being caught by the doc listener\n            if (isButton) {\n                event.stopPropagation();\n            }\n        }\n\n        // Set form and button attributes\n        if (utils.is.element(button)) {\n            button.setAttribute('aria-expanded', show);\n        }\n\n        if (utils.is.element(form)) {\n            form.setAttribute('aria-hidden', !show);\n            utils.toggleClass(this.elements.container, this.config.classNames.menu.open, show);\n\n            if (show) {\n                form.removeAttribute('tabindex');\n            } else {\n                form.setAttribute('tabindex', -1);\n            }\n        }\n    },\n\n    // Get the natural size of a tab\n    getTabSize(tab) {\n        const clone = tab.cloneNode(true);\n        clone.style.position = 'absolute';\n        clone.style.opacity = 0;\n        clone.setAttribute('aria-hidden', false);\n\n        // Prevent input's being unchecked due to the name being identical\n        Array.from(clone.querySelectorAll('input[name]')).forEach(input => {\n            const name = input.getAttribute('name');\n            input.setAttribute('name', `${name}-clone`);\n        });\n\n        // Append to parent so we get the \"real\" size\n        tab.parentNode.appendChild(clone);\n\n        // Get the sizes before we remove\n        const width = clone.scrollWidth;\n        const height = clone.scrollHeight;\n\n        // Remove from the DOM\n        utils.removeElement(clone);\n\n        return {\n            width,\n            height,\n        };\n    },\n\n    // Toggle Menu\n    showTab(event) {\n        const { menu } = this.elements.settings;\n        const tab = event.target;\n        const show = tab.getAttribute('aria-expanded') === 'false';\n        const pane = document.getElementById(tab.getAttribute('aria-controls'));\n\n        // Nothing to show, bail\n        if (!utils.is.element(pane)) {\n            return;\n        }\n\n        // Are we targetting a tab? If not, bail\n        const isTab = pane.getAttribute('role') === 'tabpanel';\n        if (!isTab) {\n            return;\n        }\n\n        // Hide all other tabs\n        // Get other tabs\n        const current = menu.querySelector('[role=\"tabpanel\"][aria-hidden=\"false\"]');\n        const container = current.parentNode;\n\n        // Set other toggles to be expanded false\n        Array.from(menu.querySelectorAll(`[aria-controls=\"${current.getAttribute('id')}\"]`)).forEach(toggle => {\n            toggle.setAttribute('aria-expanded', false);\n        });\n\n        // If we can do fancy animations, we'll animate the height/width\n        if (support.transitions && !support.reducedMotion) {\n            // Set the current width as a base\n            container.style.width = `${current.scrollWidth}px`;\n            container.style.height = `${current.scrollHeight}px`;\n\n            // Get potential sizes\n            const size = controls.getTabSize.call(this, pane);\n\n            // Restore auto height/width\n            const restore = e => {\n                // We're only bothered about height and width on the container\n                if (e.target !== container || ![\n                    'width',\n                    'height',\n                ].includes(e.propertyName)) {\n                    return;\n                }\n\n                // Revert back to auto\n                container.style.width = '';\n                container.style.height = '';\n\n                // Only listen once\n                utils.off(container, utils.transitionEndEvent, restore);\n            };\n\n            // Listen for the transition finishing and restore auto height/width\n            utils.on(container, utils.transitionEndEvent, restore);\n\n            // Set dimensions to target\n            container.style.width = `${size.width}px`;\n            container.style.height = `${size.height}px`;\n        }\n\n        // Set attributes on current tab\n        current.setAttribute('aria-hidden', true);\n        current.setAttribute('tabindex', -1);\n\n        // Set attributes on target\n        pane.setAttribute('aria-hidden', !show);\n        tab.setAttribute('aria-expanded', show);\n        pane.removeAttribute('tabindex');\n\n        // Focus the first item\n        pane.querySelectorAll('button:not(:disabled), input:not(:disabled), [tabindex]')[0].focus();\n    },\n\n    // Build the default HTML\n    // TODO: Set order based on order in the config.controls array?\n    create(data) {\n        // Do nothing if we want no controls\n        if (utils.is.empty(this.config.controls)) {\n            return null;\n        }\n\n        // Create the container\n        const container = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.controls.wrapper));\n\n        // Restart button\n        if (this.config.controls.includes('restart')) {\n            container.appendChild(controls.createButton.call(this, 'restart'));\n        }\n\n        // Rewind button\n        if (this.config.controls.includes('rewind')) {\n            container.appendChild(controls.createButton.call(this, 'rewind'));\n        }\n\n        // Play/Pause button\n        if (this.config.controls.includes('play')) {\n            container.appendChild(controls.createButton.call(this, 'play'));\n        }\n\n        // Fast forward button\n        if (this.config.controls.includes('fast-forward')) {\n            container.appendChild(controls.createButton.call(this, 'fast-forward'));\n        }\n\n        // Progress\n        if (this.config.controls.includes('progress')) {\n            const progress = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.progress));\n\n            // Seek range slider\n            const seek = controls.createRange.call(this, 'seek', {\n                id: `plyr-seek-${data.id}`,\n            });\n            progress.appendChild(seek.label);\n            progress.appendChild(seek.input);\n\n            // Buffer progress\n            progress.appendChild(controls.createProgress.call(this, 'buffer'));\n\n            // TODO: Add loop display indicator\n\n            // Seek tooltip\n            if (this.config.tooltips.seek) {\n                const tooltip = utils.createElement(\n                    'span',\n                    {\n                        role: 'tooltip',\n                        class: this.config.classNames.tooltip,\n                    },\n                    '00:00'\n                );\n\n                progress.appendChild(tooltip);\n                this.elements.display.seekTooltip = tooltip;\n            }\n\n            this.elements.progress = progress;\n            container.appendChild(this.elements.progress);\n        }\n\n        // Media current time display\n        if (this.config.controls.includes('current-time')) {\n            container.appendChild(controls.createTime.call(this, 'currentTime'));\n        }\n\n        // Media duration display\n        if (this.config.controls.includes('duration')) {\n            container.appendChild(controls.createTime.call(this, 'duration'));\n        }\n\n        // Toggle mute button\n        if (this.config.controls.includes('mute')) {\n            container.appendChild(controls.createButton.call(this, 'mute'));\n        }\n\n        // Volume range control\n        if (this.config.controls.includes('volume')) {\n            const volume = utils.createElement('div', {\n                class: 'plyr__volume',\n            });\n\n            // Set the attributes\n            const attributes = {\n                max: 1,\n                step: 0.05,\n                value: this.config.volume,\n            };\n\n            // Create the volume range slider\n            const range = controls.createRange.call(\n                this,\n                'volume',\n                utils.extend(attributes, {\n                    id: `plyr-volume-${data.id}`,\n                })\n            );\n            volume.appendChild(range.label);\n            volume.appendChild(range.input);\n\n            this.elements.volume = volume;\n\n            container.appendChild(volume);\n        }\n\n        // Toggle captions button\n        if (this.config.controls.includes('captions')) {\n            container.appendChild(controls.createButton.call(this, 'captions'));\n        }\n\n        // Settings button / menu\n        if (this.config.controls.includes('settings') && !utils.is.empty(this.config.settings)) {\n            const menu = utils.createElement('div', {\n                class: 'plyr__menu',\n            });\n\n            menu.appendChild(\n                controls.createButton.call(this, 'settings', {\n                    id: `plyr-settings-toggle-${data.id}`,\n                    'aria-haspopup': true,\n                    'aria-controls': `plyr-settings-${data.id}`,\n                    'aria-expanded': false,\n                })\n            );\n\n            const form = utils.createElement('form', {\n                class: 'plyr__menu__container',\n                id: `plyr-settings-${data.id}`,\n                'aria-hidden': true,\n                'aria-labelled-by': `plyr-settings-toggle-${data.id}`,\n                role: 'tablist',\n                tabindex: -1,\n            });\n\n            const inner = utils.createElement('div');\n\n            const home = utils.createElement('div', {\n                id: `plyr-settings-${data.id}-home`,\n                'aria-hidden': false,\n                'aria-labelled-by': `plyr-settings-toggle-${data.id}`,\n                role: 'tabpanel',\n            });\n\n            // Create the tab list\n            const tabs = utils.createElement('ul', {\n                role: 'tablist',\n            });\n\n            // Build the tabs\n            this.config.settings.forEach(type => {\n                const tab = utils.createElement('li', {\n                    role: 'tab',\n                    hidden: '',\n                });\n\n                const button = utils.createElement(\n                    'button',\n                    utils.extend(utils.getAttributesFromSelector(this.config.selectors.buttons.settings), {\n                        type: 'button',\n                        class: `${this.config.classNames.control} ${this.config.classNames.control}--forward`,\n                        id: `plyr-settings-${data.id}-${type}-tab`,\n                        'aria-haspopup': true,\n                        'aria-controls': `plyr-settings-${data.id}-${type}`,\n                        'aria-expanded': false,\n                    }),\n                    this.config.i18n[type]\n                );\n\n                const value = utils.createElement('span', {\n                    class: this.config.classNames.menu.value,\n                });\n\n                // Speed contains HTML entities\n                value.innerHTML = data[type];\n\n                button.appendChild(value);\n                tab.appendChild(button);\n                tabs.appendChild(tab);\n\n                this.elements.settings.tabs[type] = tab;\n            });\n\n            home.appendChild(tabs);\n            inner.appendChild(home);\n\n            // Build the panes\n            this.config.settings.forEach(type => {\n                const pane = utils.createElement('div', {\n                    id: `plyr-settings-${data.id}-${type}`,\n                    'aria-hidden': true,\n                    'aria-labelled-by': `plyr-settings-${data.id}-${type}-tab`,\n                    role: 'tabpanel',\n                    tabindex: -1,\n                    hidden: '',\n                });\n\n                const back = utils.createElement(\n                    'button',\n                    {\n                        type: 'button',\n                        class: `${this.config.classNames.control} ${this.config.classNames.control}--back`,\n                        'aria-haspopup': true,\n                        'aria-controls': `plyr-settings-${data.id}-home`,\n                        'aria-expanded': false,\n                    },\n                    this.config.i18n[type]\n                );\n\n                pane.appendChild(back);\n\n                const options = utils.createElement('ul');\n\n                pane.appendChild(options);\n                inner.appendChild(pane);\n\n                this.elements.settings.panes[type] = pane;\n            });\n\n            form.appendChild(inner);\n            menu.appendChild(form);\n            container.appendChild(menu);\n\n            this.elements.settings.form = form;\n            this.elements.settings.menu = menu;\n        }\n\n        // Picture in picture button\n        if (this.config.controls.includes('pip') && support.pip) {\n            container.appendChild(controls.createButton.call(this, 'pip'));\n        }\n\n        // Airplay button\n        if (this.config.controls.includes('airplay') && support.airplay) {\n            container.appendChild(controls.createButton.call(this, 'airplay'));\n        }\n\n        // Toggle fullscreen button\n        if (this.config.controls.includes('fullscreen')) {\n            container.appendChild(controls.createButton.call(this, 'fullscreen'));\n        }\n\n        // Larger overlaid play button\n        if (this.config.controls.includes('play-large')) {\n            this.elements.container.appendChild(controls.createButton.call(this, 'play-large'));\n        }\n\n        this.elements.controls = container;\n\n        if (this.config.controls.includes('settings') && this.config.settings.includes('speed')) {\n            controls.setSpeedMenu.call(this);\n        }\n\n        return container;\n    },\n\n    // Insert controls\n    inject() {\n        // Sprite\n        if (this.config.loadSprite) {\n            const icon = controls.getIconUrl.call(this);\n\n            // Only load external sprite using AJAX\n            if (icon.absolute) {\n                utils.loadSprite(icon.url, 'sprite-plyr');\n            }\n        }\n\n        // Create a unique ID\n        this.id = Math.floor(Math.random() * 10000);\n\n        // Null by default\n        let container = null;\n        this.elements.controls = null;\n\n        // HTML or Element passed as the option\n        if (utils.is.string(this.config.controls) || utils.is.element(this.config.controls)) {\n            container = this.config.controls;\n        } else if (utils.is.function(this.config.controls)) {\n            // A custom function to build controls\n            // The function can return a HTMLElement or String\n            container = this.config.controls({\n                id: this.id,\n                seektime: this.config.seekTime,\n                title: this.config.title,\n            });\n        } else {\n            // Create controls\n            container = controls.create.call(this, {\n                id: this.id,\n                seektime: this.config.seekTime,\n                speed: this.speed,\n                quality: this.quality,\n                captions: controls.getLanguage.call(this),\n                // TODO: Looping\n                // loop: 'None',\n            });\n        }\n\n        // Controls container\n        let target;\n\n        // Inject to custom location\n        if (utils.is.string(this.config.selectors.controls.container)) {\n            target = document.querySelector(this.config.selectors.controls.container);\n        }\n\n        // Inject into the container by default\n        if (!utils.is.element(target)) {\n            target = this.elements.container;\n        }\n\n        // Inject controls HTML\n        if (utils.is.element(container)) {\n            target.appendChild(container);\n        } else {\n            target.insertAdjacentHTML('beforeend', container);\n        }\n\n        // Find the elements if need be\n        if (!utils.is.element(this.elements.controls)) {\n            utils.findElements.call(this);\n        }\n\n        // Edge sometimes doesn't finish the paint so force a redraw\n        if (window.navigator.userAgent.includes('Edge')) {\n            utils.repaint(target);\n        }\n\n        // Setup tooltips\n        if (this.config.tooltips.controls) {\n            const labels = utils.getElements.call(\n                this,\n                [\n                    this.config.selectors.controls.wrapper,\n                    ' ',\n                    this.config.selectors.labels,\n                    ' .',\n                    this.config.classNames.hidden,\n                ].join('')\n            );\n\n            Array.from(labels).forEach(label => {\n                utils.toggleClass(label, this.config.classNames.hidden, false);\n                utils.toggleClass(label, this.config.classNames.tooltip, true);\n                label.setAttribute('role', 'tooltip');\n            });\n        }\n    },\n};\n\nexport default controls;\n","// ==========================================================================\n// Plyr Captions\n// ==========================================================================\n\nimport support from './support';\nimport utils from './utils';\nimport controls from './controls';\n\nconst captions = {\n    // Setup captions\n    setup() {\n        // Requires UI support\n        if (!this.supported.ui) {\n            return;\n        }\n\n        // Set default language if not set\n        const stored = this.storage.get('language');\n\n        if (!utils.is.empty(stored)) {\n            this.captions.language = stored;\n        }\n\n        if (utils.is.empty(this.captions.language)) {\n            this.captions.language = this.config.captions.language.toLowerCase();\n        }\n\n        // Set captions enabled state if not set\n        if (!utils.is.boolean(this.captions.active)) {\n            const active = this.storage.get('captions');\n\n            if (utils.is.boolean(active)) {\n                this.captions.active = active;\n            } else {\n                this.captions.active = this.config.captions.active;\n            }\n        }\n\n        // Only Vimeo and HTML5 video supported at this point\n        if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {\n            // Clear menu and hide\n            if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {\n                controls.setCaptionsMenu.call(this);\n            }\n\n            return;\n        }\n\n        // Inject the container\n        if (!utils.is.element(this.elements.captions)) {\n            this.elements.captions = utils.createElement('div', utils.getAttributesFromSelector(this.config.selectors.captions));\n\n            utils.insertAfter(this.elements.captions, this.elements.wrapper);\n        }\n\n        // Set the class hook\n        utils.toggleClass(this.elements.container, this.config.classNames.captions.enabled, !utils.is.empty(captions.getTracks.call(this)));\n\n        // If no caption file exists, hide container for caption text\n        if (utils.is.empty(captions.getTracks.call(this))) {\n            return;\n        }\n\n        // Set language\n        captions.setLanguage.call(this);\n\n        // Enable UI\n        captions.show.call(this);\n\n        // Set available languages in list\n        if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {\n            controls.setCaptionsMenu.call(this);\n        }\n    },\n\n    // Set the captions language\n    setLanguage() {\n        // Setup HTML5 track rendering\n        if (this.isHTML5 && this.isVideo) {\n            captions.getTracks.call(this).forEach(track => {\n                // Show track\n                utils.on(track, 'cuechange', event => captions.setCue.call(this, event));\n\n                // Turn off native caption rendering to avoid double captions\n                // eslint-disable-next-line\n                track.mode = 'hidden';\n            });\n\n            // Get current track\n            const currentTrack = captions.getCurrentTrack.call(this);\n\n            // Check if suported kind\n            if (utils.is.track(currentTrack)) {\n                // If we change the active track while a cue is already displayed we need to update it\n                if (Array.from(currentTrack.activeCues || []).length) {\n                    captions.setCue.call(this, currentTrack);\n                }\n            }\n        } else if (this.isVimeo && this.captions.active) {\n            this.embed.enableTextTrack(this.language);\n        }\n    },\n\n    // Get the tracks\n    getTracks() {\n        // Return empty array at least\n        if (utils.is.nullOrUndefined(this.media)) {\n            return [];\n        }\n\n        // Only get accepted kinds\n        return Array.from(this.media.textTracks || []).filter(track => [\n            'captions',\n            'subtitles',\n        ].includes(track.kind));\n    },\n\n    // Get the current track for the current language\n    getCurrentTrack() {\n        return captions.getTracks.call(this).find(track => track.language.toLowerCase() === this.language);\n    },\n\n    // Display active caption if it contains text\n    setCue(input) {\n        // Get the track from the event if needed\n        const track = utils.is.event(input) ? input.target : input;\n        const { activeCues } = track;\n        const active = activeCues.length && activeCues[0];\n        const currentTrack = captions.getCurrentTrack.call(this);\n\n        // Only display current track\n        if (track !== currentTrack) {\n            return;\n        }\n\n        // Display a cue, if there is one\n        if (utils.is.cue(active)) {\n            captions.setText.call(this, active.getCueAsHTML());\n        } else {\n            captions.setText.call(this, null);\n        }\n\n        utils.dispatchEvent.call(this, this.media, 'cuechange');\n    },\n\n    // Set the current caption\n    setText(input) {\n        // Requires UI\n        if (!this.supported.ui) {\n            return;\n        }\n\n        if (utils.is.element(this.elements.captions)) {\n            const content = utils.createElement('span');\n\n            // Empty the container\n            utils.emptyElement(this.elements.captions);\n\n            // Default to empty\n            const caption = !utils.is.nullOrUndefined(input) ? input : '';\n\n            // Set the span content\n            if (utils.is.string(caption)) {\n                content.textContent = caption.trim();\n            } else {\n                content.appendChild(caption);\n            }\n\n            // Set new caption text\n            this.elements.captions.appendChild(content);\n        } else {\n            this.debug.warn('No captions element to render to');\n        }\n    },\n\n    // Display captions container and button (for initialization)\n    show() {\n        // If there's no caption toggle, bail\n        if (!utils.is.element(this.elements.buttons.captions)) {\n            return;\n        }\n\n        // Try to load the value from storage\n        let active = this.storage.get('captions');\n\n        // Otherwise fall back to the default config\n        if (!utils.is.boolean(active)) {\n            ({ active } = this.config.captions);\n        } else {\n            this.captions.active = active;\n        }\n\n        if (active) {\n            utils.toggleClass(this.elements.container, this.config.classNames.captions.active, true);\n            utils.toggleState(this.elements.buttons.captions, true);\n        }\n    },\n};\n\nexport default captions;\n","// ==========================================================================\n// YouTube plugin\n// ==========================================================================\n\nimport utils from './../utils';\nimport controls from './../controls';\nimport ui from './../ui';\n\nconst youtube = {\n    setup() {\n        // Add embed class for responsive\n        utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);\n\n        // Set aspect ratio\n        youtube.setAspectRatio.call(this);\n\n        // Setup API\n        if (utils.is.object(window.YT) && utils.is.function(window.YT.Player)) {\n            youtube.ready.call(this);\n        } else {\n            // Load the API\n            utils.loadScript(this.config.urls.youtube.api);\n\n            // Setup callback for the API\n            // YouTube has it's own system of course...\n            window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];\n\n            // Add to queue\n            window.onYouTubeReadyCallbacks.push(() => {\n                youtube.ready.call(this);\n            });\n\n            // Set callback to process queue\n            window.onYouTubeIframeAPIReady = () => {\n                window.onYouTubeReadyCallbacks.forEach(callback => {\n                    callback();\n                });\n            };\n        }\n    },\n\n    // Get the media title\n    getTitle(videoId) {\n        // Try via undocumented API method first\n        // This method disappears now and then though...\n        // https://github.com/sampotts/plyr/issues/709\n        if (utils.is.function(this.embed.getVideoData)) {\n            const { title } = this.embed.getVideoData();\n\n            if (utils.is.empty(title)) {\n                this.config.title = title;\n                ui.setTitle.call(this);\n                return;\n            }\n        }\n\n        // Or via Google API\n        const key = this.config.keys.google;\n        if (utils.is.string(key) && !utils.is.empty(key)) {\n            const url = `https://www.googleapis.com/youtube/v3/videos?id=${videoId}&key=${key}&fields=items(snippet(title))&part=snippet`;\n\n            utils\n                .fetch(url)\n                .then(result => {\n                    if (utils.is.object(result)) {\n                        this.config.title = result.items[0].snippet.title;\n                        ui.setTitle.call(this);\n                    }\n                })\n                .catch(() => {});\n        }\n    },\n\n    // Set aspect ratio\n    setAspectRatio() {\n        const ratio = this.config.ratio.split(':');\n        this.elements.wrapper.style.paddingBottom = `${100 / ratio[0] * ratio[1]}%`;\n    },\n\n    // API ready\n    ready() {\n        const player = this;\n\n        // Ignore already setup (race condition)\n        const currentId = player.media.getAttribute('id');\n        if (!utils.is.empty(currentId) && currentId.startsWith('youtube-')) {\n            return;\n        }\n\n        // Get the source URL or ID\n        let source = player.media.getAttribute('src');\n\n        // Get from <div> if needed\n        if (utils.is.empty(source)) {\n            source = player.media.getAttribute(this.config.attributes.embed.id);\n        }\n\n        // Replace the <iframe> with a <div> due to YouTube API issues\n        const videoId = utils.parseYouTubeId(source);\n        const id = utils.generateId(player.provider);\n        const container = utils.createElement('div', { id });\n        player.media = utils.replaceElement(container, player.media);\n\n        // Setup instance\n        // https://developers.google.com/youtube/iframe_api_reference\n        player.embed = new window.YT.Player(id, {\n            videoId,\n            playerVars: {\n                autoplay: player.config.autoplay ? 1 : 0, // Autoplay\n                controls: player.supported.ui ? 0 : 1, // Only show controls if not fully supported\n                rel: 0, // No related vids\n                showinfo: 0, // Hide info\n                iv_load_policy: 3, // Hide annotations\n                modestbranding: 1, // Hide logos as much as possible (they still show one in the corner when paused)\n                disablekb: 1, // Disable keyboard as we handle it\n                playsinline: 1, // Allow iOS inline playback\n\n                // Tracking for stats\n                // origin: window ? `${window.location.protocol}//${window.location.host}` : null,\n                widget_referrer: window ? window.location.href : null,\n\n                // Captions are flaky on YouTube\n                cc_load_policy: player.captions.active ? 1 : 0,\n                cc_lang_pref: player.config.captions.language,\n            },\n            events: {\n                onError(event) {\n                    // If we've already fired an error, don't do it again\n                    // YouTube fires onError twice\n                    if (utils.is.object(player.media.error)) {\n                        return;\n                    }\n\n                    const detail = {\n                        code: event.data,\n                    };\n\n                    // Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError\n                    switch (event.data) {\n                        case 2:\n                            detail.message =\n                                'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.';\n                            break;\n\n                        case 5:\n                            detail.message =\n                                'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.';\n                            break;\n\n                        case 100:\n                            detail.message =\n                                'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.';\n                            break;\n\n                        case 101:\n                        case 150:\n                            detail.message = 'The owner of the requested video does not allow it to be played in embedded players.';\n                            break;\n\n                        default:\n                            detail.message = 'An unknown error occured';\n                            break;\n                    }\n\n                    player.media.error = detail;\n\n                    utils.dispatchEvent.call(player, player.media, 'error');\n                },\n                onPlaybackQualityChange(event) {\n                    // Get the instance\n                    const instance = event.target;\n\n                    // Get current quality\n                    player.media.quality = instance.getPlaybackQuality();\n\n                    utils.dispatchEvent.call(player, player.media, 'qualitychange');\n                },\n                onPlaybackRateChange(event) {\n                    // Get the instance\n                    const instance = event.target;\n\n                    // Get current speed\n                    player.media.playbackRate = instance.getPlaybackRate();\n\n                    utils.dispatchEvent.call(player, player.media, 'ratechange');\n                },\n                onReady(event) {\n                    // Get the instance\n                    const instance = event.target;\n\n                    // Get the title\n                    youtube.getTitle.call(player, videoId);\n\n                    // Create a faux HTML5 API using the YouTube API\n                    player.media.play = () => {\n                        instance.playVideo();\n                        player.media.paused = false;\n                    };\n\n                    player.media.pause = () => {\n                        instance.pauseVideo();\n                        player.media.paused = true;\n                    };\n\n                    player.media.stop = () => {\n                        instance.stopVideo();\n                        player.media.paused = true;\n                    };\n\n                    player.media.duration = instance.getDuration();\n                    player.media.paused = true;\n\n                    // Seeking\n                    player.media.currentTime = 0;\n                    Object.defineProperty(player.media, 'currentTime', {\n                        get() {\n                            return Number(instance.getCurrentTime());\n                        },\n                        set(time) {\n                            // Set seeking flag\n                            player.media.seeking = true;\n\n                            // Trigger seeking\n                            utils.dispatchEvent.call(player, player.media, 'seeking');\n\n                            // Seek after events sent\n                            instance.seekTo(time);\n                        },\n                    });\n\n                    // Playback speed\n                    Object.defineProperty(player.media, 'playbackRate', {\n                        get() {\n                            return instance.getPlaybackRate();\n                        },\n                        set(input) {\n                            instance.setPlaybackRate(input);\n                        },\n                    });\n\n                    // Quality\n                    Object.defineProperty(player.media, 'quality', {\n                        get() {\n                            return instance.getPlaybackQuality();\n                        },\n                        set(input) {\n                            // Trigger request event\n                            utils.dispatchEvent.call(player, player.media, 'qualityrequested', false, {\n                                quality: input,\n                            });\n\n                            instance.setPlaybackQuality(input);\n                        },\n                    });\n\n                    // Volume\n                    let { volume } = player.config;\n                    Object.defineProperty(player.media, 'volume', {\n                        get() {\n                            return volume;\n                        },\n                        set(input) {\n                            volume = input;\n                            instance.setVolume(volume * 100);\n                            utils.dispatchEvent.call(player, player.media, 'volumechange');\n                        },\n                    });\n\n                    // Muted\n                    let { muted } = player.config;\n                    Object.defineProperty(player.media, 'muted', {\n                        get() {\n                            return muted;\n                        },\n                        set(input) {\n                            const toggle = utils.is.boolean(input) ? input : muted;\n                            muted = toggle;\n                            instance[toggle ? 'mute' : 'unMute']();\n                            utils.dispatchEvent.call(player, player.media, 'volumechange');\n                        },\n                    });\n\n                    // Source\n                    Object.defineProperty(player.media, 'currentSrc', {\n                        get() {\n                            return instance.getVideoUrl();\n                        },\n                    });\n\n                    // Ended\n                    Object.defineProperty(player.media, 'ended', {\n                        get() {\n                            return player.currentTime === player.duration;\n                        },\n                    });\n\n                    // Get available speeds\n                    player.options.speed = instance.getAvailablePlaybackRates();\n\n                    // Set the tabindex to avoid focus entering iframe\n                    if (player.supported.ui) {\n                        player.media.setAttribute('tabindex', -1);\n                    }\n\n                    utils.dispatchEvent.call(player, player.media, 'timeupdate');\n                    utils.dispatchEvent.call(player, player.media, 'durationchange');\n\n                    // Reset timer\n                    window.clearInterval(player.timers.buffering);\n\n                    // Setup buffering\n                    player.timers.buffering = window.setInterval(() => {\n                        // Get loaded % from YouTube\n                        player.media.buffered = instance.getVideoLoadedFraction();\n\n                        // Trigger progress only when we actually buffer something\n                        if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) {\n                            utils.dispatchEvent.call(player, player.media, 'progress');\n                        }\n\n                        // Set last buffer point\n                        player.media.lastBuffered = player.media.buffered;\n\n                        // Bail if we're at 100%\n                        if (player.media.buffered === 1) {\n                            window.clearInterval(player.timers.buffering);\n\n                            // Trigger event\n                            utils.dispatchEvent.call(player, player.media, 'canplaythrough');\n                        }\n                    }, 200);\n\n                    // Rebuild UI\n                    window.setTimeout(() => ui.build.call(player), 50);\n                },\n                onStateChange(event) {\n                    // Get the instance\n                    const instance = event.target;\n\n                    // Reset timer\n                    window.clearInterval(player.timers.playing);\n\n                    // Handle events\n                    // -1   Unstarted\n                    // 0    Ended\n                    // 1    Playing\n                    // 2    Paused\n                    // 3    Buffering\n                    // 5    Video cued\n                    switch (event.data) {\n                        case 0:\n                            player.media.paused = true;\n\n                            // YouTube doesn't support loop for a single video, so mimick it.\n                            if (player.media.loop) {\n                                // YouTube needs a call to `stopVideo` before playing again\n                                instance.stopVideo();\n                                instance.playVideo();\n                            } else {\n                                utils.dispatchEvent.call(player, player.media, 'ended');\n                            }\n\n                            break;\n\n                        case 1:\n                            // If we were seeking, fire seeked event\n                            if (player.media.seeking) {\n                                utils.dispatchEvent.call(player, player.media, 'seeked');\n                            }\n                            player.media.seeking = false;\n\n                            // Only fire play if paused before\n                            if (player.media.paused) {\n                                utils.dispatchEvent.call(player, player.media, 'play');\n                            }\n                            player.media.paused = false;\n\n                            utils.dispatchEvent.call(player, player.media, 'playing');\n\n                            // Poll to get playback progress\n                            player.timers.playing = window.setInterval(() => {\n                                utils.dispatchEvent.call(player, player.media, 'timeupdate');\n                            }, 50);\n\n                            // Check duration again due to YouTube bug\n                            // https://github.com/sampotts/plyr/issues/374\n                            // https://code.google.com/p/gdata-issues/issues/detail?id=8690\n                            if (player.media.duration !== instance.getDuration()) {\n                                player.media.duration = instance.getDuration();\n                                utils.dispatchEvent.call(player, player.media, 'durationchange');\n                            }\n\n                            // Get quality\n                            controls.setQualityMenu.call(player, instance.getAvailableQualityLevels());\n\n                            break;\n\n                        case 2:\n                            player.media.paused = true;\n\n                            utils.dispatchEvent.call(player, player.media, 'pause');\n\n                            break;\n\n                        default:\n                            break;\n                    }\n\n                    utils.dispatchEvent.call(player, player.elements.container, 'statechange', false, {\n                        code: event.data,\n                    });\n                },\n            },\n        });\n    },\n};\n\nexport default youtube;\n","// ==========================================================================\n// Vimeo plugin\n// ==========================================================================\n\nimport utils from './../utils';\nimport captions from './../captions';\nimport ui from './../ui';\n\nconst vimeo = {\n    setup() {\n        // Add embed class for responsive\n        utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);\n\n        // Set intial ratio\n        vimeo.setAspectRatio.call(this);\n\n        // Load the API if not already\n        if (!utils.is.object(window.Vimeo)) {\n            utils.loadScript(this.config.urls.vimeo.api, () => {\n                vimeo.ready.call(this);\n            });\n        } else {\n            vimeo.ready.call(this);\n        }\n    },\n\n    // Set aspect ratio\n    // For Vimeo we have an extra 300% height <div> to hide the standard controls and UI\n    setAspectRatio(input) {\n        const ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');\n        const padding = 100 / ratio[0] * ratio[1];\n        const height = 200;\n        const offset = (height - padding) / (height / 50);\n        this.elements.wrapper.style.paddingBottom = `${padding}%`;\n        this.media.style.transform = `translateY(-${offset}%)`;\n    },\n\n    // API Ready\n    ready() {\n        const player = this;\n\n        // Get Vimeo params for the iframe\n        const options = {\n            loop: player.config.loop.active,\n            autoplay: player.autoplay,\n            byline: false,\n            portrait: false,\n            title: false,\n            speed: true,\n            transparent: 0,\n            gesture: 'media',\n        };\n        const params = utils.buildUrlParams(options);\n\n        // Get the source URL or ID\n        let source = player.media.getAttribute('src');\n\n        // Get from <div> if needed\n        if (utils.is.empty(source)) {\n            source = player.media.getAttribute(this.config.attributes.embed.id);\n        }\n\n        const id = utils.parseVimeoId(source);\n\n        // Build an iframe\n        const iframe = utils.createElement('iframe');\n        const src = `https://player.vimeo.com/video/${id}?${params}`;\n        iframe.setAttribute('src', src);\n        iframe.setAttribute('allowfullscreen', '');\n        iframe.setAttribute('allowtransparency', '');\n        iframe.setAttribute('allow', 'autoplay');\n\n        // Inject the package\n        const wrapper = utils.createElement('div');\n        wrapper.appendChild(iframe);\n        player.media = utils.replaceElement(wrapper, player.media);\n\n        // Setup instance\n        // https://github.com/vimeo/player.js\n        player.embed = new window.Vimeo.Player(iframe);\n\n        player.media.paused = true;\n        player.media.currentTime = 0;\n\n        // Create a faux HTML5 API using the Vimeo API\n        player.media.play = () => {\n            player.embed.play().then(() => {\n                player.media.paused = false;\n            });\n        };\n\n        player.media.pause = () => {\n            player.embed.pause().then(() => {\n                player.media.paused = true;\n            });\n        };\n\n        player.media.stop = () => {\n            player.embed.stop().then(() => {\n                player.media.paused = true;\n                player.currentTime = 0;\n            });\n        };\n\n        // Seeking\n        let { currentTime } = player.media;\n        Object.defineProperty(player.media, 'currentTime', {\n            get() {\n                return currentTime;\n            },\n            set(time) {\n                // Get current paused state\n                // Vimeo will automatically play on seek\n                const { paused } = player.media;\n\n                // Set seeking flag\n                player.media.seeking = true;\n\n                // Trigger seeking\n                utils.dispatchEvent.call(player, player.media, 'seeking');\n\n                // Seek after events\n                player.embed.setCurrentTime(time);\n\n                // Restore pause state\n                if (paused) {\n                    player.pause();\n                }\n            },\n        });\n\n        // Playback speed\n        let speed = player.config.speed.selected;\n        Object.defineProperty(player.media, 'playbackRate', {\n            get() {\n                return speed;\n            },\n            set(input) {\n                player.embed.setPlaybackRate(input).then(() => {\n                    speed = input;\n                    utils.dispatchEvent.call(player, player.media, 'ratechange');\n                });\n            },\n        });\n\n        // Volume\n        let { volume } = player.config;\n        Object.defineProperty(player.media, 'volume', {\n            get() {\n                return volume;\n            },\n            set(input) {\n                player.embed.setVolume(input).then(() => {\n                    volume = input;\n                    utils.dispatchEvent.call(player, player.media, 'volumechange');\n                });\n            },\n        });\n\n        // Muted\n        let { muted } = player.config;\n        Object.defineProperty(player.media, 'muted', {\n            get() {\n                return muted;\n            },\n            set(input) {\n                const toggle = utils.is.boolean(input) ? input : false;\n\n                player.embed.setVolume(toggle ? 0 : player.config.volume).then(() => {\n                    muted = toggle;\n                    utils.dispatchEvent.call(player, player.media, 'volumechange');\n                });\n            },\n        });\n\n        // Loop\n        let { loop } = player.config;\n        Object.defineProperty(player.media, 'loop', {\n            get() {\n                return loop;\n            },\n            set(input) {\n                const toggle = utils.is.boolean(input) ? input : player.config.loop.active;\n\n                player.embed.setLoop(toggle).then(() => {\n                    loop = toggle;\n                });\n            },\n        });\n\n        // Source\n        let currentSrc;\n        player.embed.getVideoUrl().then(value => {\n            currentSrc = value;\n        });\n        Object.defineProperty(player.media, 'currentSrc', {\n            get() {\n                return currentSrc;\n            },\n        });\n\n        // Ended\n        Object.defineProperty(player.media, 'ended', {\n            get() {\n                return player.currentTime === player.duration;\n            },\n        });\n\n        // Set aspect ratio based on video size\n        Promise.all([\n            player.embed.getVideoWidth(),\n            player.embed.getVideoHeight(),\n        ]).then(dimensions => {\n            const ratio = utils.getAspectRatio(dimensions[0], dimensions[1]);\n            vimeo.setAspectRatio.call(this, ratio);\n        });\n\n        // Set autopause\n        player.embed.setAutopause(player.config.autopause).then(state => {\n            player.config.autopause = state;\n        });\n\n        // Get title\n        player.embed.getVideoTitle().then(title => {\n            player.config.title = title;\n            ui.setTitle.call(this);\n        });\n\n        // Get current time\n        player.embed.getCurrentTime().then(value => {\n            currentTime = value;\n            utils.dispatchEvent.call(player, player.media, 'timeupdate');\n        });\n\n        // Get duration\n        player.embed.getDuration().then(value => {\n            player.media.duration = value;\n            utils.dispatchEvent.call(player, player.media, 'durationchange');\n        });\n\n        // Get captions\n        player.embed.getTextTracks().then(tracks => {\n            player.media.textTracks = tracks;\n            captions.setup.call(player);\n        });\n\n        player.embed.on('cuechange', data => {\n            let cue = null;\n\n            if (data.cues.length) {\n                cue = utils.stripHTML(data.cues[0].text);\n            }\n\n            captions.setText.call(player, cue);\n        });\n\n        player.embed.on('loaded', () => {\n            if (utils.is.element(player.embed.element) && player.supported.ui) {\n                const frame = player.embed.element;\n\n                // Fix keyboard focus issues\n                // https://github.com/sampotts/plyr/issues/317\n                frame.setAttribute('tabindex', -1);\n            }\n        });\n\n        player.embed.on('play', () => {\n            // Only fire play if paused before\n            if (player.media.paused) {\n                utils.dispatchEvent.call(player, player.media, 'play');\n            }\n            player.media.paused = false;\n            utils.dispatchEvent.call(player, player.media, 'playing');\n        });\n\n        player.embed.on('pause', () => {\n            player.media.paused = true;\n            utils.dispatchEvent.call(player, player.media, 'pause');\n        });\n\n        player.embed.on('timeupdate', data => {\n            player.media.seeking = false;\n            currentTime = data.seconds;\n            utils.dispatchEvent.call(player, player.media, 'timeupdate');\n        });\n\n        player.embed.on('progress', data => {\n            player.media.buffered = data.percent;\n            utils.dispatchEvent.call(player, player.media, 'progress');\n\n            // Check all loaded\n            if (parseInt(data.percent, 10) === 1) {\n                utils.dispatchEvent.call(player, player.media, 'canplaythrough');\n            }\n        });\n\n        player.embed.on('seeked', () => {\n            player.media.seeking = false;\n            utils.dispatchEvent.call(player, player.media, 'seeked');\n            utils.dispatchEvent.call(player, player.media, 'play');\n        });\n\n        player.embed.on('ended', () => {\n            player.media.paused = true;\n            utils.dispatchEvent.call(player, player.media, 'ended');\n        });\n\n        player.embed.on('error', detail => {\n            player.media.error = detail;\n            utils.dispatchEvent.call(player, player.media, 'error');\n        });\n\n        // Rebuild UI\n        window.setTimeout(() => ui.build.call(player), 0);\n    },\n};\n\nexport default vimeo;\n","// ==========================================================================\n// Plyr Media\n// ==========================================================================\n\nimport support from './support';\nimport utils from './utils';\nimport youtube from './plugins/youtube';\nimport vimeo from './plugins/vimeo';\nimport ui from './ui';\n\n// Sniff out the browser\nconst browser = utils.getBrowser();\n\nconst media = {\n    // Setup media\n    setup() {\n        // If there's no media, bail\n        if (!this.media) {\n            this.debug.warn('No media element found!');\n            return;\n        }\n\n        // Add type class\n        utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true);\n\n        // Add provider class\n        utils.toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true);\n\n        // Add video class for embeds\n        // This will require changes if audio embeds are added\n        if (this.isEmbed) {\n            utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', 'video'), true);\n        }\n\n        if (this.supported.ui) {\n            // Check for picture-in-picture support\n            utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);\n\n            // Check for airplay support\n            utils.toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);\n\n            // If there's no autoplay attribute, assume the video is stopped and add state class\n            utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.config.autoplay);\n\n            // Add iOS class\n            utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);\n\n            // Add touch class\n            utils.toggleClass(this.elements.container, this.config.classNames.isTouch, support.touch);\n        }\n\n        // Inject the player wrapper\n        if (this.isVideo) {\n            // Create the wrapper div\n            this.elements.wrapper = utils.createElement('div', {\n                class: this.config.classNames.video,\n            });\n\n            // Wrap the video in a container\n            utils.wrap(this.media, this.elements.wrapper);\n        }\n\n        if (this.isEmbed) {\n            switch (this.provider) {\n                case 'youtube':\n                    youtube.setup.call(this);\n                    break;\n\n                case 'vimeo':\n                    vimeo.setup.call(this);\n                    break;\n\n                default:\n                    break;\n            }\n        } else if (this.isHTML5) {\n            ui.setTitle.call(this);\n        }\n    },\n\n    // Cancel current network requests\n    // See https://github.com/sampotts/plyr/issues/174\n    cancelRequests() {\n        if (!this.isHTML5) {\n            return;\n        }\n\n        // Remove child sources\n        utils.removeElement(this.media.querySelectorAll('source'));\n\n        // Set blank video src attribute\n        // This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error\n        // Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection\n        this.media.setAttribute('src', this.config.blankVideo);\n\n        // Load the new empty source\n        // This will cancel existing requests\n        // See https://github.com/sampotts/plyr/issues/174\n        this.media.load();\n\n        // Debugging\n        this.debug.log('Cancelled network requests');\n    },\n};\n\nexport default media;\n","// ==========================================================================\n// Plyr source update\n// ==========================================================================\n\nimport { providers } from './types';\nimport utils from './utils';\nimport media from './media';\nimport ui from './ui';\nimport support from './support';\n\nconst source = {\n    // Add elements to HTML5 media (source, tracks, etc)\n    insertElements(type, attributes) {\n        if (utils.is.string(attributes)) {\n            utils.insertElement(type, this.media, {\n                src: attributes,\n            });\n        } else if (utils.is.array(attributes)) {\n            attributes.forEach(attribute => {\n                utils.insertElement(type, this.media, attribute);\n            });\n        }\n    },\n\n    // Update source\n    // Sources are not checked for support so be careful\n    change(input) {\n        if (!utils.is.object(input) || !('sources' in input) || !input.sources.length) {\n            this.debug.warn('Invalid source format');\n            return;\n        }\n\n        // Cancel current network requests\n        media.cancelRequests.call(this);\n\n        // Destroy instance and re-setup\n        this.destroy.call(\n            this,\n            () => {\n                // TODO: Reset menus here\n\n                // Remove elements\n                utils.removeElement(this.media);\n                this.media = null;\n\n                // Reset class name\n                if (utils.is.element(this.elements.container)) {\n                    this.elements.container.removeAttribute('class');\n                }\n\n                // Set the type and provider\n                this.type = input.type;\n                this.provider = !utils.is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5;\n\n                // Check for support\n                this.supported = support.check(this.type, this.provider, this.config.inline);\n\n                // Create new markup\n                switch (`${this.provider}:${this.type}`) {\n                    case 'html5:video':\n                        this.media = utils.createElement('video');\n                        break;\n\n                    case 'html5:audio':\n                        this.media = utils.createElement('audio');\n                        break;\n\n                    case 'youtube:video':\n                    case 'vimeo:video':\n                        this.media = utils.createElement('div', {\n                            src: input.sources[0].src,\n                        });\n                        break;\n\n                    default:\n                        break;\n                }\n\n                // Inject the new element\n                this.elements.container.appendChild(this.media);\n\n                // Autoplay the new source?\n                if (utils.is.boolean(input.autoplay)) {\n                    this.config.autoplay = input.autoplay;\n                }\n\n                // Set attributes for audio and video\n                if (this.isHTML5) {\n                    if (this.config.crossorigin) {\n                        this.media.setAttribute('crossorigin', '');\n                    }\n                    if (this.config.autoplay) {\n                        this.media.setAttribute('autoplay', '');\n                    }\n                    if ('poster' in input) {\n                        this.media.setAttribute('poster', input.poster);\n                    }\n                    if (this.config.loop.active) {\n                        this.media.setAttribute('loop', '');\n                    }\n                    if (this.config.muted) {\n                        this.media.setAttribute('muted', '');\n                    }\n                    if (this.config.inline) {\n                        this.media.setAttribute('playsinline', '');\n                    }\n                }\n\n                // Restore class hook\n                ui.addStyleHook.call(this);\n\n                // Set new sources for html5\n                if (this.isHTML5) {\n                    source.insertElements.call(this, 'source', input.sources);\n                }\n\n                // Set video title\n                this.config.title = input.title;\n\n                // Set up from scratch\n                media.setup.call(this);\n\n                // HTML5 stuff\n                if (this.isHTML5) {\n                    // Setup captions\n                    if ('tracks' in input) {\n                        source.insertElements.call(this, 'track', input.tracks);\n                    }\n\n                    // Load HTML5 sources\n                    this.media.load();\n                }\n\n                // If HTML5 or embed but not fully supported, setupInterface and call ready now\n                if (this.isHTML5 || (this.isEmbed && !this.supported.ui)) {\n                    // Setup interface\n                    ui.build.call(this);\n                }\n\n                // Update the fullscreen support\n                this.fullscreen.update();\n            },\n            true,\n        );\n    },\n};\n\nexport default source;\n","// ==========================================================================\n// Plyr\n// plyr.js v3.0.0-beta.12\n// https://github.com/sampotts/plyr\n// License: The MIT License (MIT)\n// ==========================================================================\n\nimport { providers, types } from './types';\nimport defaults from './defaults';\nimport support from './support';\nimport utils from './utils';\n\nimport Console from './console';\nimport Fullscreen from './fullscreen';\nimport Storage from './storage';\nimport Ads from './plugins/ads';\n\nimport captions from './captions';\nimport controls from './controls';\nimport listeners from './listeners';\nimport media from './media';\nimport source from './source';\nimport ui from './ui';\n\n// Private properties\n// TODO: Use a WeakMap for private globals\n// const globals = new WeakMap();\n\n// Plyr instance\nclass Plyr {\n    constructor(target, options) {\n        this.timers = {};\n\n        // State\n        this.ready = false;\n        this.loading = false;\n        this.failed = false;\n\n        // Set the media element\n        this.media = target;\n\n        // String selector passed\n        if (utils.is.string(this.media)) {\n            this.media = document.querySelectorAll(this.media);\n        }\n\n        // jQuery, NodeList or Array passed, use first element\n        if ((window.jQuery && this.media instanceof jQuery) || utils.is.nodeList(this.media) || utils.is.array(this.media)) {\n            // eslint-disable-next-line\n            this.media = this.media[0];\n        }\n\n        // Set config\n        this.config = utils.extend(\n            {},\n            defaults,\n            options,\n            (() => {\n                try {\n                    return JSON.parse(this.media.getAttribute('data-plyr-config'));\n                } catch (e) {\n                    return {};\n                }\n            })(),\n        );\n\n        // Elements cache\n        this.elements = {\n            container: null,\n            buttons: {},\n            display: {},\n            progress: {},\n            inputs: {},\n            settings: {\n                menu: null,\n                panes: {},\n                tabs: {},\n            },\n            captions: null,\n        };\n\n        // Captions\n        this.captions = {\n            active: null,\n            currentTrack: null,\n        };\n\n        // Fullscreen\n        this.fullscreen = {\n            active: false,\n        };\n\n        // Options\n        this.options = {\n            speed: [],\n            quality: [],\n        };\n\n        // Debugging\n        // TODO: move to globals\n        this.debug = new Console(this.config.debug);\n\n        // Log config options and support\n        this.debug.log('Config', this.config);\n        this.debug.log('Support', support);\n\n        // We need an element to setup\n        if (utils.is.nullOrUndefined(this.media) || !utils.is.element(this.media)) {\n            this.debug.error('Setup failed: no suitable element passed');\n            return;\n        }\n\n        // Bail if the element is initialized\n        if (this.media.plyr) {\n            this.debug.warn('Target already setup');\n            return;\n        }\n\n        // Bail if not enabled\n        if (!this.config.enabled) {\n            this.debug.error('Setup failed: disabled by config');\n            return;\n        }\n\n        // Bail if disabled or no basic support\n        // You may want to disable certain UAs etc\n        if (!support.check().api) {\n            this.debug.error('Setup failed: no support');\n            return;\n        }\n\n        // Cache original element state for .destroy()\n        this.elements.original = this.media.cloneNode(true);\n\n        // Set media type based on tag or data attribute\n        // Supported: video, audio, vimeo, youtube\n        const type = this.media.tagName.toLowerCase();\n\n        // Embed properties\n        let iframe = null;\n        let url = null;\n        let params = null;\n\n        // Different setup based on type\n        switch (type) {\n            case 'div':\n                // Find the frame\n                iframe = this.media.querySelector('iframe');\n\n                // <iframe> type\n                if (utils.is.element(iframe)) {\n                    // Detect provider\n                    url = iframe.getAttribute('src');\n                    this.provider = utils.getProviderByUrl(url);\n\n                    // Rework elements\n                    this.elements.container = this.media;\n                    this.media = iframe;\n\n                    // Reset classname\n                    this.elements.container.className = '';\n\n                    // Get attributes from URL and set config\n                    params = utils.getUrlParams(url);\n                    if (!utils.is.empty(params)) {\n                        const truthy = [\n                            '1',\n                            'true',\n                        ];\n\n                        if (truthy.includes(params.autoplay)) {\n                            this.config.autoplay = true;\n                        }\n                        if (truthy.includes(params.playsinline)) {\n                            this.config.inline = true;\n                        }\n                        if (truthy.includes(params.loop)) {\n                            this.config.loop.active = true;\n                        }\n                    }\n                } else {\n                    // <div> with attributes\n                    this.provider = this.media.getAttribute(this.config.attributes.embed.provider);\n\n                    // Remove attribute\n                    this.media.removeAttribute(this.config.attributes.embed.provider);\n                }\n\n                // Unsupported or missing provider\n                if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {\n                    this.debug.error('Setup failed: Invalid provider');\n                    return;\n                }\n\n                // Audio will come later for external providers\n                this.type = types.video;\n\n                break;\n\n            case 'video':\n            case 'audio':\n                this.type = type;\n                this.provider = providers.html5;\n\n                // Get config from attributes\n                if (this.media.hasAttribute('crossorigin')) {\n                    this.config.crossorigin = true;\n                }\n                if (this.media.hasAttribute('autoplay')) {\n                    this.config.autoplay = true;\n                }\n                if (this.media.hasAttribute('playsinline')) {\n                    this.config.inline = true;\n                }\n                if (this.media.hasAttribute('muted')) {\n                    this.config.muted = true;\n                }\n                if (this.media.hasAttribute('loop')) {\n                    this.config.loop.active = true;\n                }\n\n                break;\n\n            default:\n                this.debug.error('Setup failed: unsupported type');\n                return;\n        }\n\n        // Check for support again but with type\n        this.supported = support.check(this.type, this.provider, this.config.inline);\n\n        // If no support for even API, bail\n        if (!this.supported.api) {\n            this.debug.error('Setup failed: no support');\n            return;\n        }\n\n        // Setup local storage for user settings\n        this.storage = new Storage(this);\n\n        // Store reference\n        this.media.plyr = this;\n\n        // Wrap media\n        if (!utils.is.element(this.elements.container)) {\n            this.elements.container = utils.createElement('div');\n            utils.wrap(this.media, this.elements.container);\n        }\n\n        // Allow focus to be captured\n        this.elements.container.setAttribute('tabindex', 0);\n\n        // Global listeners\n        listeners.global.call(this);\n\n        // Add style hook\n        ui.addStyleHook.call(this);\n\n        // Setup media\n        media.setup.call(this);\n\n        // Listen for events if debugging\n        if (this.config.debug) {\n            utils.on(this.elements.container, this.config.events.join(' '), event => {\n                this.debug.log(`event: ${event.type}`);\n            });\n        }\n\n        // Setup interface\n        // If embed but not fully supported, build interface now to avoid flash of controls\n        if (this.isHTML5 || (this.isEmbed && !this.supported.ui)) {\n            ui.build.call(this);\n        }\n\n        // Setup fullscreen\n        this.fullscreen = new Fullscreen(this);\n\n        // Setup ads if provided\n        this.ads = new Ads(this);\n    }\n\n    // ---------------------------------------\n    // API\n    // ---------------------------------------\n\n    /**\n     * Types and provider helpers\n     */\n    get isHTML5() {\n        return this.provider === providers.html5;\n    }\n    get isEmbed() {\n        return this.isYouTube || this.isVimeo;\n    }\n    get isYouTube() {\n        return this.provider === providers.youtube;\n    }\n    get isVimeo() {\n        return this.provider === providers.vimeo;\n    }\n    get isVideo() {\n        return this.type === types.video;\n    }\n    get isAudio() {\n        return this.type === types.audio;\n    }\n\n    /**\n     * Play the media, or play the advertisement (if they are not blocked)\n     */\n    play() {\n        // TODO: Always return a promise?\n        if (this.ads.enabled && !this.ads.initialized && !this.ads.blocked) {\n            this.ads.play();\n            return null;\n        }\n\n        // Return the promise (for HTML5)\n        return this.media.play();\n    }\n\n    /**\n     * Pause the media\n     */\n    pause() {\n        if (!this.playing) {\n            return;\n        }\n\n        this.media.pause();\n    }\n\n    /**\n     * Get paused state\n     */\n    get paused() {\n        return this.media.paused;\n    }\n\n    /**\n     * Get playing state\n     */\n    get playing() {\n        return !this.paused && !this.ended && (this.isHTML5 ? this.media.readyState > 2 : true);\n    }\n\n    /**\n     * Get ended state\n     */\n    get ended() {\n        return this.media.ended;\n    }\n\n    /**\n     * Toggle playback based on current status\n     * @param {boolean} input\n     */\n    togglePlay(input) {\n        // Toggle based on current state if nothing passed\n        const toggle = utils.is.boolean(input) ? input : !this.playing;\n\n        if (toggle) {\n            this.play();\n        } else {\n            this.pause();\n        }\n    }\n\n    /**\n     * Stop playback\n     */\n    stop() {\n        this.restart();\n        this.pause();\n    }\n\n    /**\n     * Restart playback\n     */\n    restart() {\n        this.currentTime = 0;\n    }\n\n    /**\n     * Rewind\n     * @param {number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime\n     */\n    rewind(seekTime) {\n        this.currentTime = this.currentTime - (utils.is.number(seekTime) ? seekTime : this.config.seekTime);\n    }\n\n    /**\n     * Fast forward\n     * @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime\n     */\n    forward(seekTime) {\n        this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime);\n    }\n\n    /**\n     * Seek to a time\n     * @param {number} input - where to seek to in seconds. Defaults to 0 (the start)\n     */\n    set currentTime(input) {\n        let targetTime = 0;\n\n        if (utils.is.number(input)) {\n            targetTime = input;\n        }\n\n        // Normalise targetTime\n        if (targetTime < 0) {\n            targetTime = 0;\n        } else if (targetTime > this.duration) {\n            targetTime = this.duration;\n        }\n\n        // Set\n        this.media.currentTime = targetTime.toFixed(4);\n\n        // Logging\n        this.debug.log(`Seeking to ${this.currentTime} seconds`);\n    }\n\n    /**\n     * Get current time\n     */\n    get currentTime() {\n        return Number(this.media.currentTime);\n    }\n\n    /**\n     * Get seeking status\n     */\n    get seeking() {\n        return this.media.seeking;\n    }\n\n    /**\n     * Get the duration of the current media\n     */\n    get duration() {\n        // Faux duration set via config\n        const fauxDuration = parseInt(this.config.duration, 10);\n\n        // True duration\n        const realDuration = Number(this.media.duration);\n\n        // If custom duration is funky, use regular duration\n        return !Number.isNaN(fauxDuration) ? fauxDuration : realDuration;\n    }\n\n    /**\n     * Set the player volume\n     * @param {number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage\n     */\n    set volume(value) {\n        let volume = value;\n        const max = 1;\n        const min = 0;\n\n        if (utils.is.string(volume)) {\n            volume = Number(volume);\n        }\n\n        // Load volume from storage if no value specified\n        if (!utils.is.number(volume)) {\n            volume = this.storage.get('volume');\n        }\n\n        // Use config if all else fails\n        if (!utils.is.number(volume)) {\n            ({ volume } = this.config);\n        }\n\n        // Maximum is volumeMax\n        if (volume > max) {\n            volume = max;\n        }\n        // Minimum is volumeMin\n        if (volume < min) {\n            volume = min;\n        }\n\n        // Update config\n        this.config.volume = volume;\n\n        // Set the player volume\n        this.media.volume = volume;\n\n        // If muted, and we're increasing volume, reset muted state\n        if (this.muted && volume > 0) {\n            this.muted = false;\n        }\n    }\n\n    /**\n     * Get the current player volume\n     */\n    get volume() {\n        return this.media.volume;\n    }\n\n    /**\n     * Increase volume\n     * @param {boolean} step - How much to decrease by (between 0 and 1)\n     */\n    increaseVolume(step) {\n        const volume = this.media.muted ? 0 : this.volume;\n        this.volume = volume + (utils.is.number(step) ? step : 1);\n    }\n\n    /**\n     * Decrease volume\n     * @param {boolean} step - How much to decrease by (between 0 and 1)\n     */\n    decreaseVolume(step) {\n        const volume = this.media.muted ? 0 : this.volume;\n        this.volume = volume - (utils.is.number(step) ? step : 1);\n    }\n\n    /**\n     * Set muted state\n     * @param {boolean} mute\n     */\n    set muted(mute) {\n        let toggle = mute;\n\n        // Load muted state from storage\n        if (!utils.is.boolean(toggle)) {\n            toggle = this.storage.get('muted');\n        }\n\n        // Use config if all else fails\n        if (!utils.is.boolean(toggle)) {\n            toggle = this.config.muted;\n        }\n\n        // Update config\n        this.config.muted = toggle;\n\n        // Set mute on the player\n        this.media.muted = toggle;\n    }\n\n    /**\n     * Get current muted state\n     */\n    get muted() {\n        return this.media.muted;\n    }\n\n    /**\n     * Check if the media has audio\n     */\n    get hasAudio() {\n        // Assume yes for all non HTML5 (as we can't tell...)\n        if (!this.isHTML5) {\n            return true;\n        }\n\n        if (this.isAudio) {\n            return true;\n        }\n\n        // Get audio tracks\n        return this.media.mozHasAudio || Boolean(this.media.webkitAudioDecodedByteCount) || Boolean(this.media.audioTracks && this.media.audioTracks.length);\n    }\n\n    /**\n     * Set playback speed\n     * @param {decimal} speed - the speed of playback (0.5-2.0)\n     */\n    set speed(input) {\n        let speed = null;\n\n        if (utils.is.number(input)) {\n            speed = input;\n        }\n\n        if (!utils.is.number(speed)) {\n            speed = this.storage.get('speed');\n        }\n\n        if (!utils.is.number(speed)) {\n            speed = this.config.speed.selected;\n        }\n\n        // Set min/max\n        if (speed < 0.1) {\n            speed = 0.1;\n        }\n        if (speed > 2.0) {\n            speed = 2.0;\n        }\n\n        if (!this.config.speed.options.includes(speed)) {\n            this.debug.warn(`Unsupported speed (${speed})`);\n            return;\n        }\n\n        // Update config\n        this.config.speed.selected = speed;\n\n        // Set media speed\n        this.media.playbackRate = speed;\n    }\n\n    /**\n     * Get current playback speed\n     */\n    get speed() {\n        return this.media.playbackRate;\n    }\n\n    /**\n     * Set playback quality\n     * Currently YouTube only\n     * @param {string} input - Quality level\n     */\n    set quality(input) {\n        let quality = null;\n\n        if (utils.is.string(input)) {\n            quality = input;\n        }\n\n        if (!utils.is.string(quality)) {\n            quality = this.storage.get('quality');\n        }\n\n        if (!utils.is.string(quality)) {\n            quality = this.config.quality.selected;\n        }\n\n        if (!this.options.quality.includes(quality)) {\n            this.debug.warn(`Unsupported quality option (${quality})`);\n            return;\n        }\n\n        // Update config\n        this.config.quality.selected = quality;\n\n        // Set quality\n        this.media.quality = quality;\n    }\n\n    /**\n     * Get current quality level\n     */\n    get quality() {\n        return this.media.quality;\n    }\n\n    /**\n     * Toggle loop\n     * TODO: Finish fancy new logic. Set the indicator on load as user may pass loop as config\n     * @param {boolean} input - Whether to loop or not\n     */\n    set loop(input) {\n        const toggle = utils.is.boolean(input) ? input : this.config.loop.active;\n        this.config.loop.active = toggle;\n        this.media.loop = toggle;\n\n        // Set default to be a true toggle\n        /* const type = ['start', 'end', 'all', 'none', 'toggle'].includes(input) ? input : 'toggle';\n\n        switch (type) {\n            case 'start':\n                if (this.config.loop.end && this.config.loop.end <= this.currentTime) {\n                    this.config.loop.end = null;\n                }\n                this.config.loop.start = this.currentTime;\n                // this.config.loop.indicator.start = this.elements.display.played.value;\n                break;\n\n            case 'end':\n                if (this.config.loop.start >= this.currentTime) {\n                    return this;\n                }\n                this.config.loop.end = this.currentTime;\n                // this.config.loop.indicator.end = this.elements.display.played.value;\n                break;\n\n            case 'all':\n                this.config.loop.start = 0;\n                this.config.loop.end = this.duration - 2;\n                this.config.loop.indicator.start = 0;\n                this.config.loop.indicator.end = 100;\n                break;\n\n            case 'toggle':\n                if (this.config.loop.active) {\n                    this.config.loop.start = 0;\n                    this.config.loop.end = null;\n                } else {\n                    this.config.loop.start = 0;\n                    this.config.loop.end = this.duration - 2;\n                }\n                break;\n\n            default:\n                this.config.loop.start = 0;\n                this.config.loop.end = null;\n                break;\n        } */\n    }\n\n    /**\n     * Get current loop state\n     */\n    get loop() {\n        return this.media.loop;\n    }\n\n    /**\n     * Set new media source\n     * @param {object} input - The new source object (see docs)\n     */\n    set source(input) {\n        source.change.call(this, input);\n    }\n\n    /**\n     * Get current source\n     */\n    get source() {\n        return this.media.currentSrc;\n    }\n\n    /**\n     * Set the poster image for a HTML5 video\n     * @param {input} - the URL for the new poster image\n     */\n    set poster(input) {\n        if (!this.isHTML5 || !this.isVideo) {\n            this.debug.warn('Poster can only be set on HTML5 video');\n            return;\n        }\n\n        if (utils.is.string(input)) {\n            this.media.setAttribute('poster', input);\n        }\n    }\n\n    /**\n     * Get the current poster image\n     */\n    get poster() {\n        if (!this.isHTML5 || !this.isVideo) {\n            return null;\n        }\n\n        return this.media.getAttribute('poster');\n    }\n\n    /**\n     * Set the autoplay state\n     * @param {boolean} input - Whether to autoplay or not\n     */\n    set autoplay(input) {\n        const toggle = utils.is.boolean(input) ? input : this.config.autoplay;\n        this.config.autoplay = toggle;\n    }\n\n    /**\n     * Get the current autoplay state\n     */\n    get autoplay() {\n        return this.config.autoplay;\n    }\n\n    /**\n     * Toggle captions\n     * @param {boolean} input - Whether to enable captions\n     */\n    toggleCaptions(input) {\n        // If there's no full support, or there's no caption toggle\n        if (!this.supported.ui || !utils.is.element(this.elements.buttons.captions)) {\n            return;\n        }\n\n        // If the method is called without parameter, toggle based on current value\n        const show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;\n\n        // Nothing to change...\n        if (this.captions.active === show) {\n            return;\n        }\n\n        // Set global\n        this.captions.active = show;\n\n        // Toggle state\n        utils.toggleState(this.elements.buttons.captions, this.captions.active);\n\n        // Add class hook\n        utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.captions.active);\n\n        // Trigger an event\n        utils.dispatchEvent.call(this, this.media, this.captions.active ? 'captionsenabled' : 'captionsdisabled');\n    }\n\n    /**\n     * Set the captions language\n     * @param {string} - Two character ISO language code (e.g. EN, FR, PT, etc)\n     */\n    set language(input) {\n        // Nothing specified\n        if (!utils.is.string(input)) {\n            return;\n        }\n\n        // Toggle captions based on input\n        this.toggleCaptions(!utils.is.empty(input));\n\n        // If empty string is passed, assume disable captions\n        if (utils.is.empty(input)) {\n            return;\n        }\n\n        // Normalize\n        const language = input.toLowerCase();\n\n        // If nothing to change, bail\n        if (this.language === language) {\n            return;\n        }\n\n        // Update config\n        this.captions.language = language;\n\n        // Clear caption\n        captions.setText.call(this, null);\n\n        // Update captions\n        captions.setLanguage.call(this);\n\n        // Trigger an event\n        utils.dispatchEvent.call(this, this.media, 'languagechange');\n    }\n\n    /**\n     * Get the current captions language\n     */\n    get language() {\n        return this.captions.language;\n    }\n\n    /**\n     * Toggle picture-in-picture playback on WebKit/MacOS\n     * TODO: update player with state, support, enabled\n     * TODO: detect outside changes\n     */\n    set pip(input) {\n        const states = {\n            pip: 'picture-in-picture',\n            inline: 'inline',\n        };\n\n        // Bail if no support\n        if (!support.pip) {\n            return;\n        }\n\n        // Toggle based on current state if not passed\n        const toggle = utils.is.boolean(input) ? input : this.pip === states.inline;\n\n        // Toggle based on current state\n        this.media.webkitSetPresentationMode(toggle ? states.pip : states.inline);\n    }\n\n    /**\n     * Get the current picture-in-picture state\n     */\n    get pip() {\n        if (!support.pip) {\n            return null;\n        }\n\n        return this.media.webkitPresentationMode;\n    }\n\n    /**\n     * Trigger the airplay dialog\n     * TODO: update player with state, support, enabled\n     */\n    airplay() {\n        // Show dialog if supported\n        if (support.airplay) {\n            this.media.webkitShowPlaybackTargetPicker();\n        }\n    }\n\n    /**\n     * Toggle the player controls\n     * @param {boolean} toggle - Whether to show the controls\n     */\n    toggleControls(toggle) {\n        // We need controls of course...\n        if (!utils.is.element(this.elements.controls)) {\n            return;\n        }\n\n        // Don't hide if no UI support or it's audio\n        if (!this.supported.ui || this.isAudio) {\n            return;\n        }\n\n        let delay = 0;\n        let show = toggle;\n        let isEnterFullscreen = false;\n\n        // Get toggle state if not set\n        if (!utils.is.boolean(toggle)) {\n            if (utils.is.event(toggle)) {\n                // Is the enter fullscreen event\n                isEnterFullscreen = toggle.type === 'enterfullscreen';\n\n                // Whether to show controls\n                show = [\n                    'mouseenter',\n                    'mousemove',\n                    'touchstart',\n                    'touchmove',\n                    'focusin',\n                ].includes(toggle.type);\n\n                // Delay hiding on move events\n                if ([\n                    'mousemove',\n                    'touchmove',\n                    'touchend',\n                ].includes(toggle.type)) {\n                    delay = 2000;\n                }\n\n                // Delay a little more for keyboard users\n                if (toggle.type === 'focusin') {\n                    delay = 3000;\n                    utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, true);\n                }\n            } else {\n                show = utils.hasClass(this.elements.container, this.config.classNames.hideControls);\n            }\n        }\n\n        // Clear timer on every call\n        window.clearTimeout(this.timers.controls);\n\n        // If the mouse is not over the controls, set a timeout to hide them\n        if (show || this.paused || this.loading) {\n            // Check if controls toggled\n            const toggled = utils.toggleClass(this.elements.container, this.config.classNames.hideControls, false);\n\n            // Trigger event\n            if (toggled) {\n                utils.dispatchEvent.call(this, this.media, 'controlsshown');\n            }\n\n            // Always show controls when paused or if touch\n            if (this.paused || this.loading) {\n                return;\n            }\n\n            // Delay for hiding on touch\n            if (support.touch) {\n                delay = 3000;\n            }\n        }\n\n        // If toggle is false or if we're playing (regardless of toggle),\n        // then set the timer to hide the controls\n        if (!show || this.playing) {\n            this.timers.controls = window.setTimeout(() => {\n                /* this.debug.warn({\n                    pressed: this.elements.controls.pressed,\n                    hover: this.elements.controls.pressed,\n                    playing: this.playing,\n                    paused: this.paused,\n                    loading: this.loading,\n                }); */\n\n                // If the mouse is over the controls (and not entering fullscreen), bail\n                if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) {\n                    return;\n                }\n\n                // Restore transition behaviour\n                if (!utils.hasClass(this.elements.container, this.config.classNames.hideControls)) {\n                    utils.toggleClass(this.elements.controls, this.config.classNames.noTransition, false);\n                }\n\n                // Check if controls toggled\n                const toggled = utils.toggleClass(this.elements.container, this.config.classNames.hideControls, true);\n\n                // Trigger event and close menu\n                if (toggled) {\n                    utils.dispatchEvent.call(this, this.media, 'controlshidden');\n\n                    if (this.config.controls.includes('settings') && !utils.is.empty(this.config.settings)) {\n                        controls.toggleMenu.call(this, false);\n                    }\n                }\n            }, delay);\n        }\n    }\n\n    /**\n     * Add event listeners\n     * @param {string} event - Event type\n     * @param {function} callback - Callback for when event occurs\n     */\n    on(event, callback) {\n        utils.on(this.elements.container, event, callback);\n    }\n\n    /**\n     * Remove event listeners\n     * @param {string} event - Event type\n     * @param {function} callback - Callback for when event occurs\n     */\n    off(event, callback) {\n        utils.off(this.elements.container, event, callback);\n    }\n\n    /**\n     * Destroy an instance\n     * Event listeners are removed when elements are removed\n     * http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory\n     * @param {function} callback - Callback for when destroy is complete\n     * @param {boolean} soft - Whether it's a soft destroy (for source changes etc)\n     */\n    destroy(callback, soft = false) {\n        const done = () => {\n            // Reset overflow (incase destroyed while in fullscreen)\n            document.body.style.overflow = '';\n\n            // GC for embed\n            this.embed = null;\n\n            // If it's a soft destroy, make minimal changes\n            if (soft) {\n                if (Object.keys(this.elements).length) {\n                    // Remove elements\n                    utils.removeElement(this.elements.buttons.play);\n                    utils.removeElement(this.elements.captions);\n                    utils.removeElement(this.elements.controls);\n                    utils.removeElement(this.elements.wrapper);\n\n                    // Clear for GC\n                    this.elements.buttons.play = null;\n                    this.elements.captions = null;\n                    this.elements.controls = null;\n                    this.elements.wrapper = null;\n                }\n\n                // Callback\n                if (utils.is.function(callback)) {\n                    callback();\n                }\n            } else {\n                // Replace the container with the original element provided\n                utils.replaceElement(this.elements.original, this.elements.container);\n\n                // Event\n                utils.dispatchEvent.call(this, this.elements.original, 'destroyed', true);\n\n                // Callback\n                if (utils.is.function(callback)) {\n                    callback.call(this.elements.original);\n                }\n\n                // Clear for GC\n                this.elements = null;\n            }\n        };\n\n        // Type specific stuff\n        switch (`${this.provider}:${this.type}`) {\n            case 'html5:video':\n            case 'html5:audio':\n                // Restore native video controls\n                ui.toggleNativeControls.call(this, true);\n\n                // Clean up\n                done();\n\n                break;\n\n            case 'youtube:video':\n                // Clear timers\n                window.clearInterval(this.timers.buffering);\n                window.clearInterval(this.timers.playing);\n\n                // Destroy YouTube API\n                if (this.embed !== null) {\n                    this.embed.destroy();\n                }\n\n                // Clean up\n                done();\n\n                break;\n\n            case 'vimeo:video':\n                // Destroy Vimeo API\n                // then clean up (wait, to prevent postmessage errors)\n                if (this.embed !== null) {\n                    this.embed.unload().then(done);\n                }\n\n                // Vimeo does not always return\n                window.setTimeout(done, 200);\n\n                break;\n\n            default:\n                break;\n        }\n    }\n\n    /**\n     * Check for support for a mime type (HTML5 only)\n     * @param {string} type - Mime type\n     */\n    supports(type) {\n        return support.mime.call(this, type);\n    }\n\n    /**\n     * Check for support\n     * @param {string} type - Player type (audio/video)\n     * @param {string} provider - Provider (html5/youtube/vimeo)\n     * @param {bool} inline - Where player has `playsinline` sttribute\n     */\n    static supported(type, provider, inline) {\n        return support.check(type, provider, inline);\n    }\n\n    /**\n     * Load an SVG sprite into the page\n     * @param {string} url - URL for the SVG sprite\n     * @param {string} [id] - Unique ID\n     */\n    static loadSprite(url, id) {\n        return utils.loadSprite(url, id);\n    }\n}\n\nexport default Plyr;\n"]}