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