WordPress.org

Make WordPress Core

Ticket #38678: 38678.12.diff

File 38678.12.diff, 16.0 KB (added by joemcgill, 4 years ago)
  • src/wp-includes/class-wp-customize-manager.php

    diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
    index 3aa7ebc..f6d77a6 100644
    final class WP_Customize_Manager { 
    35903590                $this->add_setting( 'external_header_video', array(
    35913591                        'theme_supports'    => array( 'custom-header', 'video' ),
    35923592                        'transport'         => 'postMessage',
    3593                         'sanitize_callback' => 'esc_url',
     3593                        'sanitize_callback' => 'esc_url_raw',
    35943594                        'validate_callback' => array( $this, '_validate_external_header_video' ),
    35953595                ) );
    35963596
  • src/wp-includes/js/wp-custom-header.js

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

    diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php
    index fadfb28..e405b13 100644
    function wp_default_scripts( &$scripts ) { 
    481481        $scripts->add( 'customize-nav-menus', "/wp-admin/js/customize-nav-menus$suffix.js", array( 'jquery', 'wp-backbone', 'customize-controls', 'accordion', 'nav-menu' ), false, 1 );
    482482        $scripts->add( 'customize-preview-nav-menus', "/wp-includes/js/customize-preview-nav-menus$suffix.js", array( 'jquery', 'wp-util', 'customize-preview', 'customize-selective-refresh' ), false, 1 );
    483483
    484         $scripts->add( 'wp-custom-header', "/wp-includes/js/wp-custom-header$suffix.js", array(), false, 1 );
     484        $scripts->add( 'wp-custom-header', "/wp-includes/js/wp-custom-header$suffix.js", array( 'wp-a11y' ), false, 1 );
    485485
    486486        $scripts->add( 'accordion', "/wp-admin/js/accordion$suffix.js", array( 'jquery' ), false, 1 );
    487487
  • src/wp-includes/theme.php

    diff --git src/wp-includes/theme.php src/wp-includes/theme.php
    index d892ea7..a3af51c 100644
    function get_header_video_settings() { 
    13811381                'height'    => absint( $header->height ),
    13821382                'minWidth'  => 900,
    13831383                'minHeight' => 500,
     1384                'l10n'      => array(
     1385                        'pause'      => __( 'Pause' ),
     1386                        'play'       => __( 'Play' ),
     1387                        'pauseSpeak' => __( 'Video is paused.'),
     1388                        'playSpeak'  => __( 'Video is playing.'),
     1389                ),
    13841390        );
    13851391
    13861392        if ( preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video_url ) ) {