2 (function( window, settings ) {
4 var NativeHandler, YouTubeHandler;
6 window.wp = window.wp || {};
8 // Fail gracefully in unsupported browsers.
9 if ( ! ( 'addEventListener' in window ) ) {
16 * @param {Element} target HTML element to dispatch the event on.
17 * @param {string} name Event name.
19 function trigger( target, name ) {
22 if ( 'function' === typeof window.Event ) {
23 evt = new Event( name );
25 evt = document.createEvent( 'Event' );
26 evt.initEvent( name, true, true );
29 target.dispatchEvent( evt );
33 * Create a custom header instance.
37 function CustomHeader() {
39 nativeVideo: new NativeHandler(),
40 youtube: new YouTubeHandler()
44 CustomHeader.prototype = {
46 * Initalize the custom header.
48 * If the environment supports video, loops through registered handlers
49 * until one is found that can handle the video.
51 initialize: function() {
52 if ( this.supportsVideo() ) {
53 for ( var id in this.handlers ) {
54 var handler = this.handlers[ id ];
56 if ( 'test' in handler && handler.test( settings ) ) {
57 this.activeHandler = handler.initialize.call( handler, settings );
59 // Dispatch custom event when the video is loaded.
60 trigger( document, 'wp-custom-header-video-loaded' );
68 * Determines if the current environment supports video.
70 * Themes and plugins can override this method to change the criteria.
74 supportsVideo: function() {
75 // Don't load video on small screens. @todo: consider bandwidth and other factors.
76 if ( window.innerWidth < settings.minWidth || window.innerHeight < settings.minHeight ) {
84 * Base handler for custom handlers to extend.
88 BaseVideoHandler: BaseHandler
92 * Create a video handler instance.
96 function BaseHandler() {}
98 BaseHandler.prototype = {
100 * Initialize the video handler.
102 * @param {object} settings Video settings.
104 initialize: function( settings ) {
106 button = document.createElement( 'button' );
108 this.settings = settings;
109 this.container = document.getElementById( 'wp-custom-header' );
110 this.button = button;
112 button.setAttribute( 'type', 'button' );
113 button.setAttribute( 'id', 'wp-custom-header-video-button' );
114 button.setAttribute( 'class', 'wp-custom-header-video-button wp-custom-header-video-play' );
115 button.innerHTML = settings.l10n.play;
117 // Toggle video playback when the button is clicked.
118 button.addEventListener( 'click', function() {
119 if ( handler.isPaused() ) {
126 // Update the button class and text when the video state changes.
127 this.container.addEventListener( 'play', function() {
128 button.className = 'wp-custom-header-video-button wp-custom-header-video-play';
129 button.innerHTML = settings.l10n.pause;
130 if ( 'a11y' in window.wp ) {
131 window.wp.a11y.speak( settings.l10n.playSpeak);
135 this.container.addEventListener( 'pause', function() {
136 button.className = 'wp-custom-header-video-button wp-custom-header-video-pause';
137 button.innerHTML = settings.l10n.play;
138 if ( 'a11y' in window.wp ) {
139 window.wp.a11y.speak( settings.l10n.pauseSpeak);
147 * Ready method called after a handler is initialized.
151 ready: function() {},
154 * Whether the video is paused.
159 isPaused: function() {},
166 pause: function() {},
176 * Append a video node to the header container.
178 * @param {Element} node HTML element.
180 setVideo: function( node ) {
181 var editShortcutNode,
182 editShortcut = this.container.getElementsByClassName( 'customize-partial-edit-shortcut' );
184 if ( editShortcut.length ) {
185 editShortcutNode = this.container.removeChild( editShortcut[0] );
188 this.container.innerHTML = '';
189 this.container.appendChild( node );
191 if ( editShortcutNode ) {
192 this.container.appendChild( editShortcutNode );
197 * Show the video controls.
199 * Appends a play/pause button to header container.
201 showControls: function() {
202 if ( ! this.container.contains( this.button ) ) {
203 this.container.appendChild( this.button );
208 * Whether the handler can process a video.
211 * @param {object} settings Video settings.
219 * Trigger an event on the header container.
221 * @param {string} name Event name.
223 trigger: function( name ) {
224 trigger( this.container, name );
229 * Create a custom handler.
231 * @param {object} protoProps Properties to apply to the prototype.
232 * @return CustomHandler The subclass.
234 BaseHandler.extend = function( protoProps ) {
237 function CustomHandler() {
238 var result = BaseHandler.apply( this, arguments );
242 CustomHandler.prototype = Object.create( BaseHandler.prototype );
243 CustomHandler.prototype.constructor = CustomHandler;
245 for ( prop in protoProps ) {
246 CustomHandler.prototype[ prop ] = protoProps[ prop ];
249 return CustomHandler;
253 * Native video handler.
255 * @class NativeHandler
257 NativeHandler = BaseHandler.extend({
259 * Whether the native handler supports a video.
261 * @param {object} settings Video settings.
264 test: function( settings ) {
265 var video = document.createElement( 'video' );
266 return video.canPlayType( settings.mimeType );
270 * Set up a native video element.
274 video = document.createElement( 'video' );
276 video.id = 'wp-custom-header-video';
277 video.autoplay = 'autoplay';
279 video.muted = 'muted';
280 video.width = this.settings.width;
281 video.height = this.settings.height;
283 video.addEventListener( 'play', function() {
284 handler.trigger( 'play' );
287 video.addEventListener( 'pause', function() {
288 handler.trigger( 'pause' );
291 video.addEventListener( 'canplay', function() {
292 handler.showControls();
296 handler.setVideo( video );
297 video.src = this.settings.videoUrl;
301 * Whether the video is paused.
305 isPaused: function() {
306 return this.video.paused;
325 * YouTube video handler.
327 * @class YouTubeHandler
329 YouTubeHandler = BaseHandler.extend({
331 * Whether the handler supports a video.
333 * @param {object} settings Video settings.
336 test: function( settings ) {
337 return 'video/x-youtube' === settings.mimeType;
341 * Set up a YouTube iframe.
343 * Loads the YouTube IFrame API if the 'YT' global doesn't exist.
348 if ( 'YT' in window ) {
349 YT.ready( handler.loadVideo.bind( handler ) );
351 var tag = document.createElement( 'script' );
352 tag.src = 'https://www.youtube.com/iframe_api';
353 tag.onload = function () {
354 YT.ready( handler.loadVideo.bind( handler ) );
357 document.getElementsByTagName( 'head' )[0].appendChild( tag );
362 * Load a YouTube video.
364 loadVideo: function() {
366 video = document.createElement( 'div' ),
367 // @link http://stackoverflow.com/a/27728417
368 VIDEO_ID_REGEX = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/;
370 video.id = 'wp-custom-header-video';
371 handler.setVideo( video );
373 handler.player = new YT.Player( video, {
374 height: this.settings.height,
375 width: this.settings.width,
376 videoId: this.settings.videoUrl.match( VIDEO_ID_REGEX )[1],
378 onReady: function( e ) {
380 handler.showControls();
382 onStateChange: function( e ) {
383 if ( YT.PlayerState.PLAYING === e.data ) {
384 handler.trigger( 'play' );
385 } else if ( YT.PlayerState.PAUSED === e.data ) {
386 handler.trigger( 'pause' );
387 } else if ( YT.PlayerState.ENDED === e.data ) {
388 e.target.playVideo();
408 * Whether the video is paused.
412 isPaused: function() {
413 return YT.PlayerState.PAUSED === this.player.getPlayerState();
420 this.player.pauseVideo();
427 this.player.playVideo();
431 // Initialize the custom header when the DOM is ready.
432 window.wp.customHeader = new CustomHeader();
433 document.addEventListener( 'DOMContentLoaded', window.wp.customHeader.initialize.bind( window.wp.customHeader ), false );
435 // Selective refresh support in the Customizer.
436 if ( 'customize' in window.wp ) {
437 window.wp.customize.selectiveRefresh.bind( 'render-partials-response', function( response ) {
438 if ( 'custom_header_settings' in response ) {
439 settings = response.custom_header_settings;
443 window.wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
444 if ( 'custom_header' === placement.partial.id ) {
445 window.wp.customHeader.initialize();
450 })( window, window._wpCustomHeaderSettings || {} );