: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @output wp-includes/js/wp-custom-header.js
(function( window, settings ) {
var NativeHandler, YouTubeHandler;
window.wp = window.wp || {};
// Fail gracefully in unsupported browsers.
if ( ! ( 'addEventListener' in window ) ) {
* @param {Element} target HTML element to dispatch the event on.
* @param {string} name Event name.
function trigger( target, name ) {
if ( 'function' === typeof window.Event ) {
evt = document.createEvent( 'Event' );
evt.initEvent( name, true, true );
target.dispatchEvent( evt );
* Create a custom header instance.
function CustomHeader() {
nativeVideo: new NativeHandler(),
youtube: new YouTubeHandler()
CustomHeader.prototype = {
* Initialize the custom header.
* If the environment supports video, loops through registered handlers
* until one is found that can handle the video.
if ( this.supportsVideo() ) {
for ( var id in this.handlers ) {
var handler = this.handlers[ id ];
if ( 'test' in handler && handler.test( settings ) ) {
this.activeHandler = handler.initialize.call( handler, settings );
// Dispatch custom event when the video is loaded.
trigger( document, 'wp-custom-header-video-loaded' );
* Determines if the current environment supports video.
* Themes and plugins can override this method to change the criteria.
supportsVideo: function() {
// Don't load video on small screens. @todo Consider bandwidth and other factors.
if ( window.innerWidth < settings.minWidth || window.innerHeight < settings.minHeight ) {
* Base handler for custom handlers to extend.
BaseVideoHandler: BaseHandler
* Create a video handler instance.
function BaseHandler() {}
BaseHandler.prototype = {
* Initialize the video handler.
* @param {Object} settings Video settings.
initialize: function( settings ) {
button = document.createElement( 'button' );
this.settings = settings;
this.container = document.getElementById( 'wp-custom-header' );
button.setAttribute( 'type', 'button' );
button.setAttribute( 'id', 'wp-custom-header-video-button' );
button.setAttribute( 'class', 'wp-custom-header-video-button wp-custom-header-video-play' );
button.innerHTML = settings.l10n.play;
// Toggle video playback when the button is clicked.
button.addEventListener( 'click', function() {
if ( handler.isPaused() ) {
// Update the button class and text when the video state changes.
this.container.addEventListener( 'play', function() {
button.className = 'wp-custom-header-video-button wp-custom-header-video-play';
button.innerHTML = settings.l10n.pause;
if ( 'a11y' in window.wp ) {
window.wp.a11y.speak( settings.l10n.playSpeak);
this.container.addEventListener( 'pause', function() {
button.className = 'wp-custom-header-video-button wp-custom-header-video-pause';
button.innerHTML = settings.l10n.play;
if ( 'a11y' in window.wp ) {
window.wp.a11y.speak( settings.l10n.pauseSpeak);
* Ready method called after a handler is initialized.
* Whether the video is paused.
* Append a video node to the header container.
* @param {Element} node HTML element.
setVideo: function( node ) {
editShortcut = this.container.getElementsByClassName( 'customize-partial-edit-shortcut' );
if ( editShortcut.length ) {
editShortcutNode = this.container.removeChild( editShortcut[0] );
this.container.innerHTML = '';
this.container.appendChild( node );
if ( editShortcutNode ) {
this.container.appendChild( editShortcutNode );
* Show the video controls.
* Appends a play/pause button to header container.
showControls: function() {
if ( ! this.container.contains( this.button ) ) {
this.container.appendChild( this.button );
* Whether the handler can process a video.
* @param {Object} settings Video settings.
* Trigger an event on the header container.
* @param {string} name Event name.
trigger: function( name ) {
trigger( this.container, name );
* Create a custom handler.
* @param {Object} protoProps Properties to apply to the prototype.
* @return CustomHandler The subclass.
BaseHandler.extend = function( protoProps ) {
function CustomHandler() {
var result = BaseHandler.apply( this, arguments );
CustomHandler.prototype = Object.create( BaseHandler.prototype );
CustomHandler.prototype.constructor = CustomHandler;
for ( prop in protoProps ) {
CustomHandler.prototype[ prop ] = protoProps[ prop ];
NativeHandler = BaseHandler.extend(/** @lends wp.NativeHandler.prototype */{
* Whether the native handler supports a video.
* @param {Object} settings Video settings.
test: function( settings ) {
var video = document.createElement( 'video' );
return video.canPlayType( settings.mimeType );
* Set up a native video element.
video = document.createElement( 'video' );
video.id = 'wp-custom-header-video';
video.playsInline = true;
video.width = this.settings.width;
video.height = this.settings.height;
video.addEventListener( 'play', function() {
handler.trigger( 'play' );
video.addEventListener( 'pause', function() {
handler.trigger( 'pause' );
video.addEventListener( 'canplay', function() {
handler.setVideo( video );
video.src = this.settings.videoUrl;
* Whether the video is paused.
return this.video.paused;
* @class wp.YouTubeHandler
YouTubeHandler = BaseHandler.extend(/** @lends wp.YouTubeHandler.prototype */{
* Whether the handler supports a video.
* @param {Object} settings Video settings.
test: function( settings ) {
return 'video/x-youtube' === settings.mimeType;
* Set up a YouTube iframe.
* Loads the YouTube IFrame API if the 'YT' global doesn't exist.
YT.ready( handler.loadVideo.bind( handler ) );
var tag = document.createElement( 'script' );
tag.src = 'https://www.youtube.com/iframe_api';
tag.onload = function () {
YT.ready( handler.loadVideo.bind( handler ) );
document.getElementsByTagName( 'head' )[0].appendChild( tag );
video = document.createElement( 'div' ),
// @link http://stackoverflow.com/a/27728417
VIDEO_ID_REGEX = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/;
video.id = 'wp-custom-header-video';
handler.setVideo( video );
handler.player = new YT.Player( video, {
height: this.settings.height,
width: this.settings.width,
videoId: this.settings.videoUrl.match( VIDEO_ID_REGEX )[1],
onStateChange: function( e ) {
if ( YT.PlayerState.PLAYING === e.data ) {
handler.trigger( 'play' );
} else if ( YT.PlayerState.PAUSED === e.data ) {
handler.trigger( 'pause' );
} else if ( YT.PlayerState.ENDED === e.data ) {
* Whether the video is paused.
return YT.PlayerState.PAUSED === this.player.getPlayerState();
this.player.pauseVideo();
// Initialize the custom header when the DOM is ready.
window.wp.customHeader = new CustomHeader();
document.addEventListener( 'DOMContentLoaded', window.wp.customHeader.initialize.bind( window.wp.customHeader ), false );
// Selective refresh support in the Customizer.
if ( 'customize' in window.wp ) {
window.wp.customize.selectiveRefresh.bind( 'render-partials-response', function( response ) {
if ( 'custom_header_settings' in response ) {
settings = response.custom_header_settings;
window.wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
if ( 'custom_header' === placement.partial.id ) {
window.wp.customHeader.initialize();
})( window, window._wpCustomHeaderSettings || {} );