whole_func_string: 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
  	          });
  	        }
  	      }
  	    });
  	  }
func_code_string: 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
  	          });
  	        }
  	      }
  	    });
  	  }
func_documentation_string: API ready