[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 || {} );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |