demo/dist/demo.js
function ready() {
	    var player = this; // Ignore already setup (race condition)

	    var currentId = player.media.getAttribute('id');

	    if (!is$1.empty(currentId) && currentId.startsWith('youtube-')) {
	      return;
	    } // Get the source URL or ID


	    var source = player.media.getAttribute('src'); // Get from <div> if needed

	    if (is$1.empty(source)) {
	      source = player.media.getAttribute(this.config.attributes.embed.id);
	    } // Replace the <iframe> with a <div> due to YouTube API issues


	    var videoId = parseId$1(source);
	    var id = generateId(player.provider); // Get poster, if already set

	    var poster = player.poster; // Replace media element

	    var container = createElement('div', {
	      id: id,
	      poster: poster
	    });
	    player.media = replaceElement(container, player.media); // Id to poster wrapper

	    var posterSrc = function posterSrc(format) {
	      return "https://i.ytimg.com/vi/".concat(videoId, "/").concat(format, "default.jpg");
	    }; // Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide)


	    loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded
	    .catch(function () {
	      return loadImage(posterSrc('sd'), 121);
	    }) // 480p padded 4:3
	    .catch(function () {
	      return loadImage(posterSrc('hq'));
	    }) // 360p padded 4:3. Always exists
	    .then(function (image) {
	      return ui.setPoster.call(player, image.src);
	    }).then(function (posterSrc) {
	      // If the image is padded, use background-size "cover" instead (like youtube does too with their posters)
	      if (!posterSrc.includes('maxres')) {
	        player.elements.poster.style.backgroundSize = 'cover';
	      }
	    }).catch(function () {});
	    var config = player.config.youtube; // Setup instance
	    // https://developers.google.com/youtube/iframe_api_reference

	    player.embed = new window.YT.Player(id, {
	      videoId: videoId,
	      host: getHost(config),
	      playerVars: extend({}, {
	        autoplay: player.config.autoplay ? 1 : 0,
	        // Autoplay
	        hl: player.config.hl,
	        // iframe interface language
	        controls: player.supported.ui ? 0 : 1,
	        // Only show controls if not fully supported
	        disablekb: 1,
	        // Disable keyboard as we handle it
	        playsinline: !player.config.fullscreen.iosNative ? 1 : 0,
	        // Allow iOS inline playback
	        // Captions are flaky on YouTube
	        cc_load_policy: player.captions.active ? 1 : 0,
	        cc_lang_pref: player.config.captions.language,
	        // Tracking for stats
	        widget_referrer: window ? window.location.href : null
	      }, config),
	      events: {
	        onError: function onError(event) {
	          // YouTube may fire onError twice, so only handle it once
	          if (!player.media.error) {
	            var code = event.data; // Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError

	            var message = {
	              2: '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.',
	              5: 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.',
	              100: 'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.',
	              101: 'The owner of the requested video does not allow it to be played in embedded players.',
	              150: 'The owner of the requested video does not allow it to be played in embedded players.'
	            }[code] || 'An unknown error occured';
	            player.media.error = {
	              code: code,
	              message: message
	            };
	            triggerEvent.call(player, player.media, 'error');
	          }
	        },
	        onPlaybackRateChange: function onPlaybackRateChange(event) {
	          // Get the instance
	          var instance = event.target; // Get current speed

	          player.media.playbackRate = instance.getPlaybackRate();
	          triggerEvent.call(player, player.media, 'ratechange');
	        },
	        onReady: function onReady(event) {
	          // Bail if onReady has already been called. See issue #1108
	          if (is$1.function(player.media.play)) {
	            return;
	          } // Get the instance


	          var instance = event.target; // Get the title

	          youtube.getTitle.call(player, videoId); // Create a faux HTML5 API using the YouTube API

	          player.media.play = function () {
	            assurePlaybackState$1.call(player, true);
	            instance.playVideo();
	          };

	          player.media.pause = function () {
	            assurePlaybackState$1.call(player, false);
	            instance.pauseVideo();
	          };

	          player.media.stop = function () {
	            instance.stopVideo();
	          };

	          player.media.duration = instance.getDuration();
	          player.media.paused = true; // Seeking

	          player.media.currentTime = 0;
	          Object.defineProperty(player.media, 'currentTime', {
	            get: function get() {
	              return Number(instance.getCurrentTime());
	            },
	            set: function set(time) {
	              // If paused and never played, mute audio preventively (YouTube starts playing on seek if the video hasn't been played yet).
	              if (player.paused && !player.embed.hasPlayed) {
	                player.embed.mute();
	              } // Set seeking state and trigger event


	              player.media.seeking = true;
	              triggerEvent.call(player, player.media, 'seeking'); // Seek after events sent

	              instance.seekTo(time);
	            }
	          }); // Playback speed

	          Object.defineProperty(player.media, 'playbackRate', {
	            get: function get() {
	              return instance.getPlaybackRate();
	            },
	            set: function set(input) {
	              instance.setPlaybackRate(input);
	            }
	          }); // Volume

	          var volume = player.config.volume;
	          Object.defineProperty(player.media, 'volume', {
	            get: function get() {
	              return volume;
	            },
	            set: function set(input) {
	              volume = input;
	              instance.setVolume(volume * 100);
	              triggerEvent.call(player, player.media, 'volumechange');
	            }
	          }); // Muted

	          var muted = player.config.muted;
	          Object.defineProperty(player.media, 'muted', {
	            get: function get() {
	              return muted;
	            },
	            set: function set(input) {
	              var toggle = is$1.boolean(input) ? input : muted;
	              muted = toggle;
	              instance[toggle ? 'mute' : 'unMute']();
	              triggerEvent.call(player, player.media, 'volumechange');
	            }
	          }); // Source

	          Object.defineProperty(player.media, 'currentSrc', {
	            get: function get() {
	              return instance.getVideoUrl();
	            }
	          }); // Ended

	          Object.defineProperty(player.media, 'ended', {
	            get: function get() {
	              return player.currentTime === player.duration;
	            }
	          }); // Get available speeds

	          player.options.speed = instance.getAvailablePlaybackRates(); // Set the tabindex to avoid focus entering iframe

	          if (player.supported.ui) {
	            player.media.setAttribute('tabindex', -1);
	          }

	          triggerEvent.call(player, player.media, 'timeupdate');
	          triggerEvent.call(player, player.media, 'durationchange'); // Reset timer

	          clearInterval(player.timers.buffering); // Setup buffering

	          player.timers.buffering = setInterval(function () {
	            // Get loaded % from YouTube
	            player.media.buffered = instance.getVideoLoadedFraction(); // Trigger progress only when we actually buffer something

	            if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) {
	              triggerEvent.call(player, player.media, 'progress');
	            } // Set last buffer point


	            player.media.lastBuffered = player.media.buffered; // Bail if we're at 100%

	            if (player.media.buffered === 1) {
	              clearInterval(player.timers.buffering); // Trigger event

	              triggerEvent.call(player, player.media, 'canplaythrough');
	            }
	          }, 200); // Rebuild UI

	          setTimeout(function () {
	            return ui.build.call(player);
	          }, 50);
	        },
	        onStateChange: function onStateChange(event) {
	          // Get the instance
	          var instance = event.target; // Reset timer

	          clearInterval(player.timers.playing);
	          var seeked = player.media.seeking && [1, 2].includes(event.data);

	          if (seeked) {
	            // Unset seeking and fire seeked event
	            player.media.seeking = false;
	            triggerEvent.call(player, player.media, 'seeked');
	          } // Handle events
	          // -1   Unstarted
	          // 0    Ended
	          // 1    Playing
	          // 2    Paused
	          // 3    Buffering
	          // 5    Video cued


	          switch (event.data) {
	            case -1:
	              // Update scrubber
	              triggerEvent.call(player, player.media, 'timeupdate'); // Get loaded % from YouTube

	              player.media.buffered = instance.getVideoLoadedFraction();
	              triggerEvent.call(player, player.media, 'progress');
	              break;

	            case 0:
	              assurePlaybackState$1.call(player, false); // YouTube doesn't support loop for a single video, so mimick it.

	              if (player.media.loop) {
	                // YouTube needs a call to `stopVideo` before playing again
	                instance.stopVideo();
	                instance.playVideo();
	              } else {
	                triggerEvent.call(player, player.media, 'ended');
	              }

	              break;

	            case 1:
	              // Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
	              if (!player.config.autoplay && player.media.paused && !player.embed.hasPlayed) {
	                player.media.pause();
	              } else {
	                assurePlaybackState$1.call(player, true);
	                triggerEvent.call(player, player.media, 'playing'); // Poll to get playback progress

	                player.timers.playing = setInterval(function () {
	                  triggerEvent.call(player, player.media, 'timeupdate');
	                }, 50); // Check duration again due to YouTube bug
	                // https://github.com/sampotts/plyr/issues/374
	                // https://code.google.com/p/gdata-issues/issues/detail?id=8690

	                if (player.media.duration !== instance.getDuration()) {
	                  player.media.duration = instance.getDuration();
	                  triggerEvent.call(player, player.media, 'durationchange');
	                }
	              }

	              break;

	            case 2:
	              // Restore audio (YouTube starts playing on seek if the video hasn't been played yet)
	              if (!player.muted) {
	                player.embed.unMute();
	              }

	              assurePlaybackState$1.call(player, false);
	              break;

	            default:
	              break;
	          }

	          triggerEvent.call(player, player.elements.container, 'statechange', false, {
	            code: event.data
	          });
	        }
	      }
	    });
	  }
function ready() {
	    var player = this; // Ignore already setup (race condition)

	    var currentId = player.media.getAttribute('id');

	    if (!is$1.empty(currentId) && currentId.startsWith('youtube-')) {
	      return;
	    } // Get the source URL or ID


	    var source = player.media.getAttribute('src'); // Get from <div> if needed

	    if (is$1.empty(source)) {
	      source = player.media.getAttribute(this.config.attributes.embed.id);
	    } // Replace the <iframe> with a <div> due to YouTube API issues


	    var videoId = parseId$1(source);
	    var id = generateId(player.provider); // Get poster, if already set

	    var poster = player.poster; // Replace media element

	    var container = createElement('div', {
	      id: id,
	      poster: poster
	    });
	    player.media = replaceElement(container, player.media); // Id to poster wrapper

	    var posterSrc = function posterSrc(format) {
	      return "https://i.ytimg.com/vi/".concat(videoId, "/").concat(format, "default.jpg");
	    }; // Check thumbnail images in order of quality, but reject fallback thumbnails (120px wide)


	    loadImage(posterSrc('maxres'), 121) // Higest quality and unpadded
	    .catch(function () {
	      return loadImage(posterSrc('sd'), 121);
	    }) // 480p padded 4:3
	    .catch(function () {
	      return loadImage(posterSrc('hq'));
	    }) // 360p padded 4:3. Always exists
	    .then(function (image) {
	      return ui.setPoster.call(player, image.src);
	    }).then(function (posterSrc) {
	      // If the image is padded, use background-size "cover" instead (like youtube does too with their posters)
	      if (!posterSrc.includes('maxres')) {
	        player.elements.poster.style.backgroundSize = 'cover';
	      }
	    }).catch(function () {});
	    var config = player.config.youtube; // Setup instance
	    // https://developers.google.com/youtube/iframe_api_reference

	    player.embed = new window.YT.Player(id, {
	      videoId: videoId,
	      host: getHost(config),
	      playerVars: extend({}, {
	        autoplay: player.config.autoplay ? 1 : 0,
	        // Autoplay
	        hl: player.config.hl,
	        // iframe interface language
	        controls: player.supported.ui ? 0 : 1,
	        // Only show controls if not fully supported
	        disablekb: 1,
	        // Disable keyboard as we handle it
	        playsinline: !player.config.fullscreen.iosNative ? 1 : 0,
	        // Allow iOS inline playback
	        // Captions are flaky on YouTube
	        cc_load_policy: player.captions.active ? 1 : 0,
	        cc_lang_pref: player.config.captions.language,
	        // Tracking for stats
	        widget_referrer: window ? window.location.href : null
	      }, config),
	      events: {
	        onError: function onError(event) {
	          // YouTube may fire onError twice, so only handle it once
	          if (!player.media.error) {
	            var code = event.data; // Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError

	            var message = {
	              2: '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.',
	              5: 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.',
	              100: 'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.',
	              101: 'The owner of the requested video does not allow it to be played in embedded players.',
	              150: 'The owner of the requested video does not allow it to be played in embedded players.'
	            }[code] || 'An unknown error occured';
	            player.media.error = {
	              code: code,
	              message: message
	            };
	            triggerEvent.call(player, player.media, 'error');
	          }
	        },
	        onPlaybackRateChange: function onPlaybackRateChange(event) {
	          // Get the instance
	          var instance = event.target; // Get current speed

	          player.media.playbackRate = instance.getPlaybackRate();
	          triggerEvent.call(player, player.media, 'ratechange');
	        },
	        onReady: function onReady(event) {
	          // Bail if onReady has already been called. See issue #1108
	          if (is$1.function(player.media.play)) {
	            return;
	          } // Get the instance


	          var instance = event.target; // Get the title

	          youtube.getTitle.call(player, videoId); // Create a faux HTML5 API using the YouTube API

	          player.media.play = function () {
	            assurePlaybackState$1.call(player, true);
	            instance.playVideo();
	          };

	          player.media.pause = function () {
	            assurePlaybackState$1.call(player, false);
	            instance.pauseVideo();
	          };

	          player.media.stop = function () {
	            instance.stopVideo();
	          };

	          player.media.duration = instance.getDuration();
	          player.media.paused = true; // Seeking

	          player.media.currentTime = 0;
	          Object.defineProperty(player.media, 'currentTime', {
	            get: function get() {
	              return Number(instance.getCurrentTime());
	            },
	            set: function set(time) {
	              // If paused and never played, mute audio preventively (YouTube starts playing on seek if the video hasn't been played yet).
	              if (player.paused && !player.embed.hasPlayed) {
	                player.embed.mute();
	              } // Set seeking state and trigger event


	              player.media.seeking = true;
	              triggerEvent.call(player, player.media, 'seeking'); // Seek after events sent

	              instance.seekTo(time);
	            }
	          }); // Playback speed

	          Object.defineProperty(player.media, 'playbackRate', {
	            get: function get() {
	              return instance.getPlaybackRate();
	            },
	            set: function set(input) {
	              instance.setPlaybackRate(input);
	            }
	          }); // Volume

	          var volume = player.config.volume;
	          Object.defineProperty(player.media, 'volume', {
	            get: function get() {
	              return volume;
	            },
	            set: function set(input) {
	              volume = input;
	              instance.setVolume(volume * 100);
	              triggerEvent.call(player, player.media, 'volumechange');
	            }
	          }); // Muted

	          var muted = player.config.muted;
	          Object.defineProperty(player.media, 'muted', {
	            get: function get() {
	              return muted;
	            },
	            set: function set(input) {
	              var toggle = is$1.boolean(input) ? input : muted;
	              muted = toggle;
	              instance[toggle ? 'mute' : 'unMute']();
	              triggerEvent.call(player, player.media, 'volumechange');
	            }
	          }); // Source

	          Object.defineProperty(player.media, 'currentSrc', {
	            get: function get() {
	              return instance.getVideoUrl();
	            }
	          }); // Ended

	          Object.defineProperty(player.media, 'ended', {
	            get: function get() {
	              return player.currentTime === player.duration;
	            }
	          }); // Get available speeds

	          player.options.speed = instance.getAvailablePlaybackRates(); // Set the tabindex to avoid focus entering iframe

	          if (player.supported.ui) {
	            player.media.setAttribute('tabindex', -1);
	          }

	          triggerEvent.call(player, player.media, 'timeupdate');
	          triggerEvent.call(player, player.media, 'durationchange'); // Reset timer

	          clearInterval(player.timers.buffering); // Setup buffering

	          player.timers.buffering = setInterval(function () {
	            // Get loaded % from YouTube
	            player.media.buffered = instance.getVideoLoadedFraction(); // Trigger progress only when we actually buffer something

	            if (player.media.lastBuffered === null || player.media.lastBuffered < player.media.buffered) {
	              triggerEvent.call(player, player.media, 'progress');
	            } // Set last buffer point


	            player.media.lastBuffered = player.media.buffered; // Bail if we're at 100%

	            if (player.media.buffered === 1) {
	              clearInterval(player.timers.buffering); // Trigger event

	              triggerEvent.call(player, player.media, 'canplaythrough');
	            }
	          }, 200); // Rebuild UI

	          setTimeout(function () {
	            return ui.build.call(player);
	          }, 50);
	        },
	        onStateChange: function onStateChange(event) {
	          // Get the instance
	          var instance = event.target; // Reset timer

	          clearInterval(player.timers.playing);
	          var seeked = player.media.seeking && [1, 2].includes(event.data);

	          if (seeked) {
	            // Unset seeking and fire seeked event
	            player.media.seeking = false;
	            triggerEvent.call(player, player.media, 'seeked');
	          } // Handle events
	          // -1   Unstarted
	          // 0    Ended
	          // 1    Playing
	          // 2    Paused
	          // 3    Buffering
	          // 5    Video cued


	          switch (event.data) {
	            case -1:
	              // Update scrubber
	              triggerEvent.call(player, player.media, 'timeupdate'); // Get loaded % from YouTube

	              player.media.buffered = instance.getVideoLoadedFraction();
	              triggerEvent.call(player, player.media, 'progress');
	              break;

	            case 0:
	              assurePlaybackState$1.call(player, false); // YouTube doesn't support loop for a single video, so mimick it.

	              if (player.media.loop) {
	                // YouTube needs a call to `stopVideo` before playing again
	                instance.stopVideo();
	                instance.playVideo();
	              } else {
	                triggerEvent.call(player, player.media, 'ended');
	              }

	              break;

	            case 1:
	              // Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
	              if (!player.config.autoplay && player.media.paused && !player.embed.hasPlayed) {
	                player.media.pause();
	              } else {
	                assurePlaybackState$1.call(player, true);
	                triggerEvent.call(player, player.media, 'playing'); // Poll to get playback progress

	                player.timers.playing = setInterval(function () {
	                  triggerEvent.call(player, player.media, 'timeupdate');
	                }, 50); // Check duration again due to YouTube bug
	                // https://github.com/sampotts/plyr/issues/374
	                // https://code.google.com/p/gdata-issues/issues/detail?id=8690

	                if (player.media.duration !== instance.getDuration()) {
	                  player.media.duration = instance.getDuration();
	                  triggerEvent.call(player, player.media, 'durationchange');
	                }
	              }

	              break;

	            case 2:
	              // Restore audio (YouTube starts playing on seek if the video hasn't been played yet)
	              if (!player.muted) {
	                player.embed.unMute();
	              }

	              assurePlaybackState$1.call(player, false);
	              break;

	            default:
	              break;
	          }

	          triggerEvent.call(player, player.elements.container, 'statechange', false, {
	            code: event.data
	          });
	        }
	      }
	    });
	  }
API ready