]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/wp-custom-header.js
WordPress 4.7.2-scripts
[autoinstalls/wordpress.git] / wp-includes / js / wp-custom-header.js
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() {
38                 this.handlers = {
39                         nativeVideo: new NativeHandler(),
40                         youtube: new YouTubeHandler()
41                 };
42         }
43
44         CustomHeader.prototype = {
45                 /**
46                  * Initalize the custom header.
47                  *
48                  * If the environment supports video, loops through registered handlers
49                  * until one is found that can handle the video.
50                  */
51                 initialize: function() {
52                         if ( this.supportsVideo() ) {
53                                 for ( var id in this.handlers ) {
54                                         var handler = this.handlers[ id ];
55
56                                         if ( 'test' in handler && handler.test( settings ) ) {
57                                                 this.activeHandler = handler.initialize.call( handler, settings );
58
59                                                 // Dispatch custom event when the video is loaded.
60                                                 trigger( document, 'wp-custom-header-video-loaded' );
61                                                 break;
62                                         }
63                                 }
64                         }
65                 },
66
67                 /**
68                  * Determines if the current environment supports video.
69                  *
70                  * Themes and plugins can override this method to change the criteria.
71                  *
72                  * @return {boolean}
73                  */
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 ) {
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         function BaseHandler() {}
97
98         BaseHandler.prototype = {
99                 /**
100                  * Initialize the video handler.
101                  *
102                  * @param {object} settings Video settings.
103                  */
104                 initialize: function( settings ) {
105                         var handler = this,
106                                 button = document.createElement( 'button' );
107
108                         this.settings = settings;
109                         this.container = document.getElementById( 'wp-custom-header' );
110                         this.button = button;
111
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;
116
117                         // Toggle video playback when the button is clicked.
118                         button.addEventListener( 'click', function() {
119                                 if ( handler.isPaused() ) {
120                                         handler.play();
121                                 } else {
122                                         handler.pause();
123                                 }
124                         });
125
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);
132                                 }
133                         });
134
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);
140                                 }
141                         });
142
143                         this.ready();
144                 },
145
146                 /**
147                  * Ready method called after a handler is initialized.
148                  *
149                  * @abstract
150                  */
151                 ready: function() {},
152
153                 /**
154                  * Whether the video is paused.
155                  *
156                  * @abstract
157                  * @return {boolean}
158                  */
159                 isPaused: function() {},
160
161                 /**
162                  * Pause the video.
163                  *
164                  * @abstract
165                  */
166                 pause: function() {},
167
168                 /**
169                  * Play the video.
170                  *
171                  * @abstract
172                  */
173                 play: function() {},
174
175                 /**
176                  * Append a video node to the header container.
177                  *
178                  * @param {Element} node HTML element.
179                  */
180                 setVideo: function( node ) {
181                         var editShortcutNode,
182                                 editShortcut = this.container.getElementsByClassName( 'customize-partial-edit-shortcut' );
183
184                         if ( editShortcut.length ) {
185                                 editShortcutNode = this.container.removeChild( editShortcut[0] );
186                         }
187
188                         this.container.innerHTML = '';
189                         this.container.appendChild( node );
190
191                         if ( editShortcutNode ) {
192                                 this.container.appendChild( editShortcutNode );
193                         }
194                 },
195
196                 /**
197                  * Show the video controls.
198                  *
199                  * Appends a play/pause button to header container.
200                  */
201                 showControls: function() {
202                         if ( ! this.container.contains( this.button ) ) {
203                                 this.container.appendChild( this.button );
204                         }
205                 },
206
207                 /**
208                  * Whether the handler can process a video.
209                  *
210                  * @abstract
211                  * @param {object} settings Video settings.
212                  * @return {boolean}
213                  */
214                 test: function() {
215                         return false;
216                 },
217
218                 /**
219                  * Trigger an event on the header container.
220                  *
221                  * @param {string} name Event name.
222                  */
223                 trigger: function( name ) {
224                         trigger( this.container, name );
225                 }
226         };
227
228         /**
229          * Create a custom handler.
230          *
231          * @param {object} protoProps Properties to apply to the prototype.
232          * @return CustomHandler The subclass.
233          */
234         BaseHandler.extend = function( protoProps ) {
235                 var prop;
236
237                 function CustomHandler() {
238                         var result = BaseHandler.apply( this, arguments );
239                         return result;
240                 }
241
242                 CustomHandler.prototype = Object.create( BaseHandler.prototype );
243                 CustomHandler.prototype.constructor = CustomHandler;
244
245                 for ( prop in protoProps ) {
246                         CustomHandler.prototype[ prop ] = protoProps[ prop ];
247                 }
248
249                 return CustomHandler;
250         };
251
252         /**
253          * Native video handler.
254          *
255          * @class NativeHandler
256          */
257         NativeHandler = BaseHandler.extend({
258                 /**
259                  * Whether the native handler supports a video.
260                  *
261                  * @param {object} settings Video settings.
262                  * @return {boolean}
263                  */
264                 test: function( settings ) {
265                         var video = document.createElement( 'video' );
266                         return video.canPlayType( settings.mimeType );
267                 },
268
269                 /**
270                  * Set up a native video element.
271                  */
272                 ready: function() {
273                         var handler = this,
274                                 video = document.createElement( 'video' );
275
276                         video.id = 'wp-custom-header-video';
277                         video.autoplay = 'autoplay';
278                         video.loop = 'loop';
279                         video.muted = 'muted';
280                         video.width = this.settings.width;
281                         video.height = this.settings.height;
282
283                         video.addEventListener( 'play', function() {
284                                 handler.trigger( 'play' );
285                         });
286
287                         video.addEventListener( 'pause', function() {
288                                 handler.trigger( 'pause' );
289                         });
290
291                         video.addEventListener( 'canplay', function() {
292                                 handler.showControls();
293                         });
294
295                         this.video = video;
296                         handler.setVideo( video );
297                         video.src = this.settings.videoUrl;
298                 },
299
300                 /**
301                  * Whether the video is paused.
302                  *
303                  * @return {boolean}
304                  */
305                 isPaused: function() {
306                         return this.video.paused;
307                 },
308
309                 /**
310                  * Pause the video.
311                  */
312                 pause: function() {
313                         this.video.pause();
314                 },
315
316                 /**
317                  * Play the video.
318                  */
319                 play: function() {
320                         this.video.play();
321                 }
322         });
323
324         /**
325          * YouTube video handler.
326          *
327          * @class YouTubeHandler
328          */
329         YouTubeHandler = BaseHandler.extend({
330                 /**
331                  * Whether the handler supports a video.
332                  *
333                  * @param {object} settings Video settings.
334                  * @return {boolean}
335                  */
336                 test: function( settings ) {
337                         return 'video/x-youtube' === settings.mimeType;
338                 },
339
340                 /**
341                  * Set up a YouTube iframe.
342                  *
343                  * Loads the YouTube IFrame API if the 'YT' global doesn't exist.
344                  */
345                 ready: function() {
346                         var handler = this;
347
348                         if ( 'YT' in window ) {
349                                 YT.ready( handler.loadVideo.bind( handler ) );
350                         } else {
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 ) );
355                                 };
356
357                                 document.getElementsByTagName( 'head' )[0].appendChild( tag );
358                         }
359                 },
360
361                 /**
362                  * Load a YouTube video.
363                  */
364                 loadVideo: function() {
365                         var handler = this,
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)?=))([^#\&\?]*).*/;
369
370                         video.id = 'wp-custom-header-video';
371                         handler.setVideo( video );
372
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],
377                                 events: {
378                                         onReady: function( e ) {
379                                                 e.target.mute();
380                                                 handler.showControls();
381                                         },
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();
389                                                 }
390                                         }
391                                 },
392                                 playerVars: {
393                                         autoplay: 1,
394                                         controls: 0,
395                                         disablekb: 1,
396                                         fs: 0,
397                                         iv_load_policy: 3,
398                                         loop: 1,
399                                         modestbranding: 1,
400                                         playsinline: 1,
401                                         rel: 0,
402                                         showinfo: 0
403                                 }
404                         });
405                 },
406
407                 /**
408                  * Whether the video is paused.
409                  *
410                  * @return {boolean}
411                  */
412                 isPaused: function() {
413                         return YT.PlayerState.PAUSED === this.player.getPlayerState();
414                 },
415
416                 /**
417                  * Pause the video.
418                  */
419                 pause: function() {
420                         this.player.pauseVideo();
421                 },
422
423                 /**
424                  * Play the video.
425                  */
426                 play: function() {
427                         this.player.playVideo();
428                 }
429         });
430
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 );
434
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;
440                         }
441                 });
442
443                 window.wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
444                         if ( 'custom_header' === placement.partial.id ) {
445                                 window.wp.customHeader.initialize();
446                         }
447                 });
448         }
449
450 })( window, window._wpCustomHeaderSettings || {} );