balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

wp-custom-header.js (10465B)


      1 /**
      2  * @output wp-includes/js/wp-custom-header.js
      3  */
      4 
      5 /* global YT */
      6 (function( window, settings ) {
      7 
      8 	var NativeHandler, YouTubeHandler;
      9 
     10 	/** @namespace wp */
     11 	window.wp = window.wp || {};
     12 
     13 	// Fail gracefully in unsupported browsers.
     14 	if ( ! ( 'addEventListener' in window ) ) {
     15 		return;
     16 	}
     17 
     18 	/**
     19 	 * Trigger an event.
     20 	 *
     21 	 * @param {Element} target HTML element to dispatch the event on.
     22 	 * @param {string} name Event name.
     23 	 */
     24 	function trigger( target, name ) {
     25 		var evt;
     26 
     27 		if ( 'function' === typeof window.Event ) {
     28 			evt = new Event( name );
     29 		} else {
     30 			evt = document.createEvent( 'Event' );
     31 			evt.initEvent( name, true, true );
     32 		}
     33 
     34 		target.dispatchEvent( evt );
     35 	}
     36 
     37 	/**
     38 	 * Create a custom header instance.
     39 	 *
     40 	 * @memberOf wp
     41 	 *
     42 	 * @class
     43 	 */
     44 	function CustomHeader() {
     45 		this.handlers = {
     46 			nativeVideo: new NativeHandler(),
     47 			youtube: new YouTubeHandler()
     48 		};
     49 	}
     50 
     51 	CustomHeader.prototype = {
     52 		/**
     53 		 * Initialize the custom header.
     54 		 *
     55 		 * If the environment supports video, loops through registered handlers
     56 		 * until one is found that can handle the video.
     57 		 */
     58 		initialize: function() {
     59 			if ( this.supportsVideo() ) {
     60 				for ( var id in this.handlers ) {
     61 					var handler = this.handlers[ id ];
     62 
     63 					if ( 'test' in handler && handler.test( settings ) ) {
     64 						this.activeHandler = handler.initialize.call( handler, settings );
     65 
     66 						// Dispatch custom event when the video is loaded.
     67 						trigger( document, 'wp-custom-header-video-loaded' );
     68 						break;
     69 					}
     70 				}
     71 			}
     72 		},
     73 
     74 		/**
     75 		 * Determines if the current environment supports video.
     76 		 *
     77 		 * Themes and plugins can override this method to change the criteria.
     78 		 *
     79 		 * @return {boolean}
     80 		 */
     81 		supportsVideo: function() {
     82 			// Don't load video on small screens. @todo Consider bandwidth and other factors.
     83 			if ( window.innerWidth < settings.minWidth || window.innerHeight < settings.minHeight ) {
     84 				return false;
     85 			}
     86 
     87 			return true;
     88 		},
     89 
     90 		/**
     91 		 * Base handler for custom handlers to extend.
     92 		 *
     93 		 * @type {BaseHandler}
     94 		 */
     95 		BaseVideoHandler: BaseHandler
     96 	};
     97 
     98 	/**
     99 	 * Create a video handler instance.
    100 	 *
    101 	 * @memberOf wp
    102 	 *
    103 	 * @class
    104 	 */
    105 	function BaseHandler() {}
    106 
    107 	BaseHandler.prototype = {
    108 		/**
    109 		 * Initialize the video handler.
    110 		 *
    111 		 * @param {Object} settings Video settings.
    112 		 */
    113 		initialize: function( settings ) {
    114 			var handler = this,
    115 				button = document.createElement( 'button' );
    116 
    117 			this.settings = settings;
    118 			this.container = document.getElementById( 'wp-custom-header' );
    119 			this.button = button;
    120 
    121 			button.setAttribute( 'type', 'button' );
    122 			button.setAttribute( 'id', 'wp-custom-header-video-button' );
    123 			button.setAttribute( 'class', 'wp-custom-header-video-button wp-custom-header-video-play' );
    124 			button.innerHTML = settings.l10n.play;
    125 
    126 			// Toggle video playback when the button is clicked.
    127 			button.addEventListener( 'click', function() {
    128 				if ( handler.isPaused() ) {
    129 					handler.play();
    130 				} else {
    131 					handler.pause();
    132 				}
    133 			});
    134 
    135 			// Update the button class and text when the video state changes.
    136 			this.container.addEventListener( 'play', function() {
    137 				button.className = 'wp-custom-header-video-button wp-custom-header-video-play';
    138 				button.innerHTML = settings.l10n.pause;
    139 				if ( 'a11y' in window.wp ) {
    140 					window.wp.a11y.speak( settings.l10n.playSpeak);
    141 				}
    142 			});
    143 
    144 			this.container.addEventListener( 'pause', function() {
    145 				button.className = 'wp-custom-header-video-button wp-custom-header-video-pause';
    146 				button.innerHTML = settings.l10n.play;
    147 				if ( 'a11y' in window.wp ) {
    148 					window.wp.a11y.speak( settings.l10n.pauseSpeak);
    149 				}
    150 			});
    151 
    152 			this.ready();
    153 		},
    154 
    155 		/**
    156 		 * Ready method called after a handler is initialized.
    157 		 *
    158 		 * @abstract
    159 		 */
    160 		ready: function() {},
    161 
    162 		/**
    163 		 * Whether the video is paused.
    164 		 *
    165 		 * @abstract
    166 		 * @return {boolean}
    167 		 */
    168 		isPaused: function() {},
    169 
    170 		/**
    171 		 * Pause the video.
    172 		 *
    173 		 * @abstract
    174 		 */
    175 		pause: function() {},
    176 
    177 		/**
    178 		 * Play the video.
    179 		 *
    180 		 * @abstract
    181 		 */
    182 		play: function() {},
    183 
    184 		/**
    185 		 * Append a video node to the header container.
    186 		 *
    187 		 * @param {Element} node HTML element.
    188 		 */
    189 		setVideo: function( node ) {
    190 			var editShortcutNode,
    191 				editShortcut = this.container.getElementsByClassName( 'customize-partial-edit-shortcut' );
    192 
    193 			if ( editShortcut.length ) {
    194 				editShortcutNode = this.container.removeChild( editShortcut[0] );
    195 			}
    196 
    197 			this.container.innerHTML = '';
    198 			this.container.appendChild( node );
    199 
    200 			if ( editShortcutNode ) {
    201 				this.container.appendChild( editShortcutNode );
    202 			}
    203 		},
    204 
    205 		/**
    206 		 * Show the video controls.
    207 		 *
    208 		 * Appends a play/pause button to header container.
    209 		 */
    210 		showControls: function() {
    211 			if ( ! this.container.contains( this.button ) ) {
    212 				this.container.appendChild( this.button );
    213 			}
    214 		},
    215 
    216 		/**
    217 		 * Whether the handler can process a video.
    218 		 *
    219 		 * @abstract
    220 		 * @param {Object} settings Video settings.
    221 		 * @return {boolean}
    222 		 */
    223 		test: function() {
    224 			return false;
    225 		},
    226 
    227 		/**
    228 		 * Trigger an event on the header container.
    229 		 *
    230 		 * @param {string} name Event name.
    231 		 */
    232 		trigger: function( name ) {
    233 			trigger( this.container, name );
    234 		}
    235 	};
    236 
    237 	/**
    238 	 * Create a custom handler.
    239 	 *
    240 	 * @memberOf wp
    241 	 *
    242 	 * @param {Object} protoProps Properties to apply to the prototype.
    243 	 * @return CustomHandler The subclass.
    244 	 */
    245 	BaseHandler.extend = function( protoProps ) {
    246 		var prop;
    247 
    248 		function CustomHandler() {
    249 			var result = BaseHandler.apply( this, arguments );
    250 			return result;
    251 		}
    252 
    253 		CustomHandler.prototype = Object.create( BaseHandler.prototype );
    254 		CustomHandler.prototype.constructor = CustomHandler;
    255 
    256 		for ( prop in protoProps ) {
    257 			CustomHandler.prototype[ prop ] = protoProps[ prop ];
    258 		}
    259 
    260 		return CustomHandler;
    261 	};
    262 
    263 	/**
    264 	 * Native video handler.
    265 	 *
    266 	 * @memberOf wp
    267 	 *
    268 	 * @class
    269 	 */
    270 	NativeHandler = BaseHandler.extend(/** @lends wp.NativeHandler.prototype */{
    271 		/**
    272 		 * Whether the native handler supports a video.
    273 		 *
    274 		 * @param {Object} settings Video settings.
    275 		 * @return {boolean}
    276 		 */
    277 		test: function( settings ) {
    278 			var video = document.createElement( 'video' );
    279 			return video.canPlayType( settings.mimeType );
    280 		},
    281 
    282 		/**
    283 		 * Set up a native video element.
    284 		 */
    285 		ready: function() {
    286 			var handler = this,
    287 				video = document.createElement( 'video' );
    288 
    289 			video.id = 'wp-custom-header-video';
    290 			video.autoplay = true;
    291 			video.loop = true;
    292 			video.muted = true;
    293 			video.playsInline = true;
    294 			video.width = this.settings.width;
    295 			video.height = this.settings.height;
    296 
    297 			video.addEventListener( 'play', function() {
    298 				handler.trigger( 'play' );
    299 			});
    300 
    301 			video.addEventListener( 'pause', function() {
    302 				handler.trigger( 'pause' );
    303 			});
    304 
    305 			video.addEventListener( 'canplay', function() {
    306 				handler.showControls();
    307 			});
    308 
    309 			this.video = video;
    310 			handler.setVideo( video );
    311 			video.src = this.settings.videoUrl;
    312 		},
    313 
    314 		/**
    315 		 * Whether the video is paused.
    316 		 *
    317 		 * @return {boolean}
    318 		 */
    319 		isPaused: function() {
    320 			return this.video.paused;
    321 		},
    322 
    323 		/**
    324 		 * Pause the video.
    325 		 */
    326 		pause: function() {
    327 			this.video.pause();
    328 		},
    329 
    330 		/**
    331 		 * Play the video.
    332 		 */
    333 		play: function() {
    334 			this.video.play();
    335 		}
    336 	});
    337 
    338 	/**
    339 	 * YouTube video handler.
    340 	 *
    341 	 * @memberOf wp
    342 	 *
    343 	 * @class wp.YouTubeHandler
    344 	 */
    345 	YouTubeHandler = BaseHandler.extend(/** @lends wp.YouTubeHandler.prototype */{
    346 		/**
    347 		 * Whether the handler supports a video.
    348 		 *
    349 		 * @param {Object} settings Video settings.
    350 		 * @return {boolean}
    351 		 */
    352 		test: function( settings ) {
    353 			return 'video/x-youtube' === settings.mimeType;
    354 		},
    355 
    356 		/**
    357 		 * Set up a YouTube iframe.
    358 		 *
    359 		 * Loads the YouTube IFrame API if the 'YT' global doesn't exist.
    360 		 */
    361 		ready: function() {
    362 			var handler = this;
    363 
    364 			if ( 'YT' in window ) {
    365 				YT.ready( handler.loadVideo.bind( handler ) );
    366 			} else {
    367 				var tag = document.createElement( 'script' );
    368 				tag.src = 'https://www.youtube.com/iframe_api';
    369 				tag.onload = function () {
    370 					YT.ready( handler.loadVideo.bind( handler ) );
    371 				};
    372 
    373 				document.getElementsByTagName( 'head' )[0].appendChild( tag );
    374 			}
    375 		},
    376 
    377 		/**
    378 		 * Load a YouTube video.
    379 		 */
    380 		loadVideo: function() {
    381 			var handler = this,
    382 				video = document.createElement( 'div' ),
    383 				// @link http://stackoverflow.com/a/27728417
    384 				VIDEO_ID_REGEX = /^.*(?:(?:youtu\.be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/;
    385 
    386 			video.id = 'wp-custom-header-video';
    387 			handler.setVideo( video );
    388 
    389 			handler.player = new YT.Player( video, {
    390 				height: this.settings.height,
    391 				width: this.settings.width,
    392 				videoId: this.settings.videoUrl.match( VIDEO_ID_REGEX )[1],
    393 				events: {
    394 					onReady: function( e ) {
    395 						e.target.mute();
    396 						handler.showControls();
    397 					},
    398 					onStateChange: function( e ) {
    399 						if ( YT.PlayerState.PLAYING === e.data ) {
    400 							handler.trigger( 'play' );
    401 						} else if ( YT.PlayerState.PAUSED === e.data ) {
    402 							handler.trigger( 'pause' );
    403 						} else if ( YT.PlayerState.ENDED === e.data ) {
    404 							e.target.playVideo();
    405 						}
    406 					}
    407 				},
    408 				playerVars: {
    409 					autoplay: 1,
    410 					controls: 0,
    411 					disablekb: 1,
    412 					fs: 0,
    413 					iv_load_policy: 3,
    414 					loop: 1,
    415 					modestbranding: 1,
    416 					playsinline: 1,
    417 					rel: 0,
    418 					showinfo: 0
    419 				}
    420 			});
    421 		},
    422 
    423 		/**
    424 		 * Whether the video is paused.
    425 		 *
    426 		 * @return {boolean}
    427 		 */
    428 		isPaused: function() {
    429 			return YT.PlayerState.PAUSED === this.player.getPlayerState();
    430 		},
    431 
    432 		/**
    433 		 * Pause the video.
    434 		 */
    435 		pause: function() {
    436 			this.player.pauseVideo();
    437 		},
    438 
    439 		/**
    440 		 * Play the video.
    441 		 */
    442 		play: function() {
    443 			this.player.playVideo();
    444 		}
    445 	});
    446 
    447 	// Initialize the custom header when the DOM is ready.
    448 	window.wp.customHeader = new CustomHeader();
    449 	document.addEventListener( 'DOMContentLoaded', window.wp.customHeader.initialize.bind( window.wp.customHeader ), false );
    450 
    451 	// Selective refresh support in the Customizer.
    452 	if ( 'customize' in window.wp ) {
    453 		window.wp.customize.selectiveRefresh.bind( 'render-partials-response', function( response ) {
    454 			if ( 'custom_header_settings' in response ) {
    455 				settings = response.custom_header_settings;
    456 			}
    457 		});
    458 
    459 		window.wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
    460 			if ( 'custom_header' === placement.partial.id ) {
    461 				window.wp.customHeader.initialize();
    462 			}
    463 		});
    464 	}
    465 
    466 })( window, window._wpCustomHeaderSettings || {} );