<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[41750] trunk: Customize: Allow controls to be created with pre-instantiated `Setting` object(s), or even with plain `Value` object(s).</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/41750">41750</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/41750","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>westonruter</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2017-10-04 20:01:12 +0000 (Wed, 04 Oct 2017)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Customize: Allow controls to be created with pre-instantiated `Setting` object(s), or even with plain `Value` object(s).

* Allow passing settings in keyed object (e.g. `settings: { default: 'id' }  ), or as an array (e.g. `settings: [ 'id' ]`) with first being default; again, `Setting`/`Value` objects may be supplied instead of IDs.
* Allow a single setting to be supplied with just a single `setting` param, either a string or a `Setting`/`Value` object.
* Update `changeset_status` and `scheduled_changeset_date` to be added dynamically with JS and simply passing of `api.state()` instances as `setting`.
* Introduce a `data-customize-setting-key-link` attribute which, unlike `data-customize-setting-link`, allows passing the setting key (e.g. `default`) as opposed to the setting ID.
* Allow `WP_Customize_Control::get_link()` to return `data-customize-setting-key-link` when setting is not registered.
* Eliminate `default_value` from `WP_Customize_Date_Time_Control` since now comes from supplied `Value`.
* Export status choices as `wp.customize.settings.changeset.statusChoices`.
* Export date and time formats as `wp.customize.settings.dateFormat` and `wp.customize.settings.timeFormat` respectively.

Props westonruter, sayedwp.
See <a href="https://core.trac.wordpress.org/ticket/39896">#39896</a>, <a href="https://core.trac.wordpress.org/ticket/30738">#30738</a>, <a href="https://core.trac.wordpress.org/ticket/30741">#30741</a>, <a href="https://core.trac.wordpress.org/ticket/42083">#42083</a>.
Fixes <a href="https://core.trac.wordpress.org/ticket/37964">#37964</a>, <a href="https://core.trac.wordpress.org/ticket/36167">#36167</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincsscustomizecontrolscss">trunk/src/wp-admin/css/customize-controls.css</a></li>
<li><a href="#trunksrcwpadminjscustomizecontrolsjs">trunk/src/wp-admin/js/customize-controls.js</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizecontrolphp">trunk/src/wp-includes/class-wp-customize-control.php</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizemanagerphp">trunk/src/wp-includes/class-wp-customize-manager.php</a></li>
<li><a href="#trunksrcwpincludescustomizeclasswpcustomizedatetimecontrolphp">trunk/src/wp-includes/customize/class-wp-customize-date-time-control.php</a></li>
<li><a href="#trunksrcwpincludesscriptloaderphp">trunk/src/wp-includes/script-loader.php</a></li>
<li><a href="#trunktestsphpunittestscustomizemanagerphp">trunk/tests/phpunit/tests/customize/manager.php</a></li>
<li><a href="#trunktestsqunitwpadminjscustomizecontrolsjs">trunk/tests/qunit/wp-admin/js/customize-controls.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmincsscustomizecontrolscss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/css/customize-controls.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/css/customize-controls.css     2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/src/wp-admin/css/customize-controls.css       2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -138,7 +138,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        margin-bottom: 15px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#customize-control-changeset_status label,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#customize-control-changeset_status .customize-inside-control-row,
</ins><span class="cx" style="display: block; padding: 0 10px"> #customize-control-changeset_preview_link input {
</span><span class="cx" style="display: block; padding: 0 10px">        background-color: #ffffff;
</span><span class="cx" style="display: block; padding: 0 10px">        border-bottom: 1px solid #ddd;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -170,13 +170,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        border-color: #dc3232;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#customize-control-changeset_status label {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#customize-control-changeset_status .customize-inside-control-row {
</ins><span class="cx" style="display: block; padding: 0 10px">         padding-top: 10px;
</span><span class="cx" style="display: block; padding: 0 10px">        padding-bottom: 10px;
</span><span class="cx" style="display: block; padding: 0 10px">        font-weight: 500;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-#customize-control-changeset_status label:first-of-type {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+#customize-control-changeset_status .customize-inside-control-row:first-of-type {
</ins><span class="cx" style="display: block; padding: 0 10px">         border-top: 1px solid #ddd;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2809,7 +2809,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                height: 31px;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        #customize-control-changeset_status label {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ #customize-control-changeset_status .customize-inside-control-row {
</ins><span class="cx" style="display: block; padding: 0 10px">                 padding-top: 15px;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpadminjscustomizecontrolsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/customize-controls.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/customize-controls.js       2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/src/wp-admin/js/customize-controls.js 2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3094,8 +3094,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @param {string} [options.priority=10]    - Order of priority to show the control within the section.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param {string} [options.active=true]    - Whether the control is active.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param {string} options.section          - The ID of the section the control belongs to.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param {string} options.settings.default - The ID of the setting the control relates to.
-        * @param {string} options.settings.data
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param {mixed}  [options.setting]        - The ID of the main setting or an instance of this setting.
+        * @param {mixed}  options.settings         - An object with keys (e.g. default) that maps to setting IDs or Setting/Value objects, or an array of setting IDs or Setting/Value objects.    
+        * @param {mixed}  options.settings.default - The ID of the setting the control relates to.
+        * @param {string} options.settings.data    - @todo Is this used?
</ins><span class="cx" style="display: block; padding: 0 10px">          * @param {string} options.label            - Label.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param {string} options.description      - Description.
</span><span class="cx" style="display: block; padding: 0 10px">         * @param {number} [options.instanceNumber] - Order in which this instance was created in relation to other instances.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3110,8 +3112,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                initialize: function( id, options ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var control = this,
-                               nodes, radios, settings;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var control = this, deferredSettingIds = [], settings, gatherSettings;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        control.params = _.extend( {}, control.defaults );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3123,6 +3124,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                control.params.instanceNumber = api.Control.instanceCounter;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // Look up the type if one was not supplied.
+                       if ( ! control.params.type ) {
+                               _.find( api.controlConstructor, function( Constructor, type ) {
+                                       if ( Constructor === control.constructor ) {
+                                               control.params.type = type;
+                                               return true;
+                                       }
+                                       return false;
+                               } );
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         _.extend( control.params, options.params || options );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! control.params.content ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                control.params.content = $( '<li></li>', {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3153,31 +3165,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        control.elements = [];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        nodes  = control.container.find('[data-customize-setting-link]');
-                       radios = {};
-
-                       nodes.each( function() {
-                               var node = $( this ),
-                                       name;
-
-                               if ( node.is( ':radio' ) ) {
-                                       name = node.prop( 'name' );
-                                       if ( radios[ name ] ) {
-                                               return;
-                                       }
-
-                                       radios[ name ] = true;
-                                       node = nodes.filter( '[name="' + name + '"]' );
-                               }
-
-                               api( node.data( 'customizeSettingLink' ), function( setting ) {
-                                       var element = new api.Element( node );
-                                       control.elements.push( element );
-                                       element.sync( setting );
-                                       element.set( setting() );
-                               });
-                       });
-
</del><span class="cx" style="display: block; padding: 0 10px">                         control.active.bind( function ( active ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                var args = control.activeArgumentsQueue.shift();
</span><span class="cx" style="display: block; padding: 0 10px">                                args = $.extend( {}, control.defaultActiveArguments, args );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3190,58 +3177,106 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        api.utils.bubbleChildValueChanges( control, [ 'section', 'priority', 'active' ] );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        /*
-                        * After all settings related to the control are available,
-                        * make them available on the control and embed the control into the page.
-                        */
-                       settings = $.map( control.params.settings, function( value ) {
-                               return value;
-                       });
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 control.settings = {};
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( 0 === settings.length ) {
-                               control.setting = null;
-                               control.settings = {};
-                               control.embed();
-                       } else {
-                               api.apply( api, settings.concat( function() {
-                                       var key;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 settings = {};
+                       if ( control.params.setting ) {
+                               settings['default'] = control.params.setting;
+                       }
+                       _.extend( settings, control.params.settings );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        control.settings = {};
-                                       for ( key in control.params.settings ) {
-                                               control.settings[ key ] = api( control.params.settings[ key ] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 // Note: Settings can be an array or an object.
+                       _.each( settings, function( setting, key ) {
+                               if ( _.isObject( setting ) ) { // @todo Or check if instance of api.Setting?
+                                       control.settings[ key ] = setting;
+                               } else {
+                                       deferredSettingIds.push( setting );
+                               }
+                       } );
+
+                       gatherSettings = function() {
+
+                               // Fill-in all resolved settings.
+                               _.each( settings, function ( settingId, key ) {
+                                       if ( ! control.settings[ key ] && _.isString( settingId ) ) {
+                                               control.settings[ key ] = api( settingId );
</ins><span class="cx" style="display: block; padding: 0 10px">                                         }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                } );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        control.setting = control.settings['default'] || null;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         // Make sure settings passed as array gets associated with default.
+                               if ( control.settings[0] && ! control.settings['default'] ) {
+                                       control.settings['default'] = control.settings[0];
+                               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        // Add setting notifications to the control notification.
-                                       _.each( control.settings, function( setting ) {
-                                               setting.notifications.bind( 'add', function( settingNotification ) {
-                                                       var params = _.extend(
-                                                               {},
-                                                               settingNotification,
-                                                               {
-                                                                       setting: setting.id
-                                                               }
-                                                       );
-                                                       control.notifications.add( new api.Notification( setting.id + ':' + settingNotification.code, params ) );
-                                               } );
-                                               setting.notifications.bind( 'remove', function( settingNotification ) {
-                                                       control.notifications.remove( setting.id + ':' + settingNotification.code );
-                                               } );
-                                       } );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         // Identify the main setting.
+                               control.setting = control.settings['default'] || null;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        control.embed();
-                               }) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         control.embed();
+                       };
+
+                       if ( 0 === deferredSettingIds.length ) {
+                               gatherSettings();
+                       } else {
+                               api.apply( api, deferredSettingIds.concat( gatherSettings ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // After the control is embedded on the page, invoke the "ready" method.
</span><span class="cx" style="display: block; padding: 0 10px">                        control.deferred.embedded.done( function () {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                control.linkElements();
</ins><span class="cx" style="display: block; padding: 0 10px">                                 control.setupNotifications();
</span><span class="cx" style="display: block; padding: 0 10px">                                control.ready();
</span><span class="cx" style="display: block; padding: 0 10px">                        });
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * Link elements between settings and inputs.
+                *
+                * @since 4.7.0
+                * @access public
+                *
+                * @returns {void}
+                */
+               linkElements: function () {
+                       var control = this, nodes, radios, element;
+
+                       nodes = control.container.find( '[data-customize-setting-link], [data-customize-setting-key-link]' );
+                       radios = {};
+
+                       nodes.each( function () {
+                               var node = $( this ), name, setting;
+
+                               if ( node.data( 'customizeSettingLinked' ) ) {
+                                       return;
+                               }
+                               node.data( 'customizeSettingLinked', true ); // Prevent re-linking element.
+
+                               if ( node.is( ':radio' ) ) {
+                                       name = node.prop( 'name' );
+                                       if ( radios[name] ) {
+                                               return;
+                                       }
+
+                                       radios[name] = true;
+                                       node = nodes.filter( '[name="' + name + '"]' );
+                               }
+
+                               // Let link by default refer to setting ID. If it doesn't exist, fallback to looking up by setting key.
+                               if ( node.data( 'customizeSettingLink' ) ) {
+                                       setting = api( node.data( 'customizeSettingLink' ) );
+                               } else if ( node.data( 'customizeSettingKeyLink' ) ) {
+                                       setting = control.settings[ node.data( 'customizeSettingKeyLink' ) ];
+                               }
+
+                               if ( setting ) {
+                                       element = new api.Element( node );
+                                       control.elements.push( element );
+                                       element.sync( setting );
+                                       element.set( setting() );
+                               }
+                       } );
+               },
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * Embed the control into the page.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                embed: function () {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3342,6 +3377,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">                setupNotifications: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                        var control = this, renderNotificationsIfVisible, onSectionAssigned;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // Add setting notifications to the control notification.
+                       _.each( control.settings, function( setting ) {
+                               if ( ! setting.notifications ) {
+                                       return;
+                               }
+                               setting.notifications.bind( 'add', function( settingNotification ) {
+                                       var params = _.extend(
+                                               {},
+                                               settingNotification,
+                                               {
+                                                       setting: setting.id
+                                               }
+                                       );
+                                       control.notifications.add( new api.Notification( setting.id + ':' + settingNotification.code, params ) );
+                               } );
+                               setting.notifications.bind( 'remove', function( settingNotification ) {
+                                       control.notifications.remove( setting.id + ':' + settingNotification.code );
+                               } );
+                       } );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         control.notifications.container = control.getNotificationsContainerElement();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        renderNotificationsIfVisible = function() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4986,16 +5041,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        _.bindAll( control, 'populateSetting', 'updateDaysForMonth', 'updateMinutesForHour', 'populateDateInputs' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // @todo This needs https://core.trac.wordpress.org/ticket/37964
</del><span class="cx" style="display: block; padding: 0 10px">                         if ( ! control.setting ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                control.setting = new api.Value();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         throw new Error( 'Missing setting' );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // @todo Should this be? Default should be on client. The default value should be in the setting itself.
-                       if ( ! control.setting.get() && control.params.defaultValue ) {
-                               control.setting.set( control.params.defaultValue );
-                       }
-
</del><span class="cx" style="display: block; padding: 0 10px">                         control.container.find( '.date-input' ).each( function() {
</span><span class="cx" style="display: block; padding: 0 10px">                                var input = $( this ), component, element;
</span><span class="cx" style="display: block; padding: 0 10px">                                component = input.data( 'component' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6360,8 +6409,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        publishSettingsBtn = $( '#publish-settings' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        footerActions = $( '#customize-footer-actions' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Set up publish settings section and its controls.
</ins><span class="cx" style="display: block; padding: 0 10px">                 api.section( 'publish_settings', function( section ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var updateButtonsState, trashControl, updateSectionActive, isSectionActive;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var updateButtonsState, trashControl, updateSectionActive, isSectionActive, statusControl, dateControl, toggleDateControl, publishWhenTime, pollInterval, updateTimeArrivedPoller, timeArrivedPollingInterval = 1000;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        trashControl = new api.Control( 'trash_changeset', {
</span><span class="cx" style="display: block; padding: 0 10px">                                type: 'button',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6433,6 +6483,84 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                publishSettingsBtn.attr( 'aria-expanded', String( isExpanded ) );
</span><span class="cx" style="display: block; padding: 0 10px">                                publishSettingsBtn.toggleClass( 'active', isExpanded );
</span><span class="cx" style="display: block; padding: 0 10px">                        } );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       statusControl = new api.Control( 'changeset_status', {
+                               priority: 10,
+                               type: 'radio',
+                               section: 'publish_settings',
+                               setting: api.state( 'selectedChangesetStatus' ),
+                               templateId: 'customize-selected-changeset-status-control',
+                               label: api.l10n.action,
+                               choices: api.settings.changeset.statusChoices
+                       } );
+                       api.control.add( statusControl );
+
+                       dateControl = new api.DateTimeControl( 'changeset_scheduled_date', {
+                               priority: 20,
+                               section: 'publish_settings',
+                               setting: api.state( 'selectedChangesetDate' ),
+                               minYear: ( new Date() ).getFullYear(),
+                               allowPastDate: false,
+                               includeTime: true,
+                               twelveHourFormat: /a/i.test( api.settings.timeFormat ),
+                               description: api.l10n.scheduleDescription
+                       } );
+                       dateControl.notifications.alt = true;
+                       api.control.add( dateControl );
+
+                       publishWhenTime = function() {
+                               api.state( 'selectedChangesetStatus' ).set( 'publish' );
+                               api.previewer.save();
+                       };
+
+                       // Start countdown for when the dateTime arrives, or clear interval when it is .
+                       updateTimeArrivedPoller = function() {
+                               var shouldPoll = (
+                                       'future' === api.state( 'changesetStatus' ).get() &&
+                                       'future' === api.state( 'selectedChangesetStatus' ).get() &&
+                                       api.state( 'changesetDate' ).get() &&
+                                       api.state( 'selectedChangesetDate' ).get() === api.state( 'changesetDate' ).get() &&
+                                       api.utils.getRemainingTime( api.state( 'changesetDate' ).get() ) >= 0
+                               );
+
+                               if ( shouldPoll && ! pollInterval ) {
+                                       pollInterval = setInterval( function() {
+                                               var remainingTime = api.utils.getRemainingTime( api.state( 'changesetDate' ).get() );
+                                               api.state( 'remainingTimeToPublish' ).set( remainingTime );
+                                               if ( remainingTime <= 0 ) {
+                                                       clearInterval( pollInterval );
+                                                       pollInterval = 0;
+                                                       publishWhenTime();
+                                               }
+                                       }, timeArrivedPollingInterval );
+                               } else if ( ! shouldPoll && pollInterval ) {
+                                       clearInterval( pollInterval );
+                                       pollInterval = 0;
+                               }
+                       };
+
+                       api.state( 'changesetDate' ).bind( updateTimeArrivedPoller );
+                       api.state( 'selectedChangesetDate' ).bind( updateTimeArrivedPoller );
+                       api.state( 'changesetStatus' ).bind( updateTimeArrivedPoller );
+                       api.state( 'selectedChangesetStatus' ).bind( updateTimeArrivedPoller );
+                       updateTimeArrivedPoller();
+
+                       // Ensure dateControl only appears when selected status is future.
+                       dateControl.active.validate = function() {
+                               return 'future' === api.state( 'selectedChangesetStatus' ).get();
+                       };
+                       toggleDateControl = function( value ) {
+                               dateControl.active.set( 'future' === value );
+                       };
+                       toggleDateControl( api.state( 'selectedChangesetStatus' ).get() );
+                       api.state( 'selectedChangesetStatus' ).bind( toggleDateControl );
+
+                       // Show notification on date control when status is future but it isn't a future date.
+                       api.state( 'saving' ).bind( function( isSaving ) {
+                               if ( isSaving && 'future' === api.state( 'selectedChangesetStatus' ).get() ) {
+                                       dateControl.toggleFutureDateNotification( ! dateControl.isFutureDate() );
+                               }
+                       } );
</ins><span class="cx" style="display: block; padding: 0 10px">                 } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // Prevent the form from saving when enter is pressed on an input or select element.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7062,6 +7190,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        // Set default states.
</span><span class="cx" style="display: block; padding: 0 10px">                        changesetStatus( api.settings.changeset.status );
</span><span class="cx" style="display: block; padding: 0 10px">                        changesetDate( api.settings.changeset.publishDate );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        selectedChangesetDate( api.settings.changeset.publishDate );
</ins><span class="cx" style="display: block; padding: 0 10px">                         selectedChangesetStatus( '' === api.settings.changeset.status || 'auto-draft' === api.settings.changeset.status ? 'publish' : api.settings.changeset.status );
</span><span class="cx" style="display: block; padding: 0 10px">                        selectedChangesetStatus.link( changesetStatus ); // Ensure that direct updates to status on server via wp.customizer.previewer.save() will update selection.
</span><span class="cx" style="display: block; padding: 0 10px">                        saved( true );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7985,80 +8114,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        });
</span><span class="cx" style="display: block; padding: 0 10px">                })();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Publish settings section and controls.
-               api.control( 'changeset_status', 'changeset_scheduled_date', function( statusControl, dateControl ) {
-                       $.when( statusControl.deferred.embedded, dateControl.deferred.embedded ).done( function() {
-                               var radioNodes, statusElement, toggleDateControl, publishWhenTime, pollInterval, updateTimeArrivedPoller, timeArrivedPollingInterval = 1000;
-
-                               radioNodes = statusControl.container.find( 'input[type=radio][name]' );
-                               statusElement = new api.Element( radioNodes );
-                               statusControl.elements.push( statusElement );
-
-                               statusElement.sync( api.state( 'selectedChangesetStatus' ) );
-                               statusElement.set( api.state( 'selectedChangesetStatus' ).get() );
-
-                               dateControl.notifications.alt = true;
-                               dateControl.deferred.embedded.done( function() {
-                                       api.state( 'selectedChangesetDate' ).sync( dateControl.setting );
-                                   api.state( 'selectedChangesetDate' ).set( dateControl.setting() );
-                               } );
-
-                               publishWhenTime = function() {
-                                       api.state( 'selectedChangesetStatus' ).set( 'publish' );
-                                       api.previewer.save();
-                               };
-
-                               // Start countdown for when the dateTime arrives, or clear interval when it is .
-                               updateTimeArrivedPoller = function() {
-                                       var shouldPoll = (
-                                               'future' === api.state( 'changesetStatus' ).get() &&
-                                               'future' === api.state( 'selectedChangesetStatus' ).get() &&
-                                               api.state( 'changesetDate' ).get() &&
-                                               api.state( 'selectedChangesetDate' ).get() === api.state( 'changesetDate' ).get() &&
-                                               api.utils.getRemainingTime( api.state( 'changesetDate' ).get() ) >= 0
-                                       );
-
-                                       if ( shouldPoll && ! pollInterval ) {
-                                               pollInterval = setInterval( function() {
-                                                       var remainingTime = api.utils.getRemainingTime( api.state( 'changesetDate' ).get() );
-                                                       api.state( 'remainingTimeToPublish' ).set( remainingTime );
-                                                       if ( remainingTime <= 0 ) {
-                                                               clearInterval( pollInterval );
-                                                               pollInterval = 0;
-                                                               publishWhenTime();
-                                                       }
-                                               }, timeArrivedPollingInterval );
-                                       } else if ( ! shouldPoll && pollInterval ) {
-                                               clearInterval( pollInterval );
-                                               pollInterval = 0;
-                                       }
-                               };
-
-                               api.state( 'changesetDate' ).bind( updateTimeArrivedPoller );
-                               api.state( 'selectedChangesetDate' ).bind( updateTimeArrivedPoller );
-                               api.state( 'changesetStatus' ).bind( updateTimeArrivedPoller );
-                               api.state( 'selectedChangesetStatus' ).bind( updateTimeArrivedPoller );
-                               updateTimeArrivedPoller();
-
-                               // Ensure dateControl only appears when selected status is future.
-                               dateControl.active.validate = function() {
-                                       return 'future' === statusElement.get();
-                               };
-                               toggleDateControl = function( value ) {
-                                       dateControl.active.set( 'future' === value );
-                               };
-                               toggleDateControl( statusElement.get() );
-                               statusElement.bind( toggleDateControl );
-
-                               // Show notification on date control when status is future but it isn't a future date.
-                               api.state( 'saving' ).bind( function( isSaving ) {
-                                       if ( isSaving && 'future' === api.state( 'selectedChangesetStatus' ).get() ) {
-                                               dateControl.toggleFutureDateNotification( ! dateControl.isFutureDate() );
-                                       }
-                               } );
-                       } );
-               } );
-
</del><span class="cx" style="display: block; padding: 0 10px">                 // Toggle visibility of Header Video notice when active state change.
</span><span class="cx" style="display: block; padding: 0 10px">                api.control( 'header_video', function( headerVideoControl ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        headerVideoControl.deferred.embedded.done( function() {
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizecontrolphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-control.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-control.php      2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/src/wp-includes/class-wp-customize-control.php        2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -430,15 +430,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * Get the data link attribute for a setting.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 3.4.0
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.9.0 Return a `data-customize-setting-key-link` attribute if a setting is not registered for the supplied setting key.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param string $setting_key
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @return string Data link parameter, if $setting_key is a valid setting, empty string otherwise.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @return string Data link parameter, a `data-customize-setting-link` attribute if the `$setting_key` refers to a pre-registered setting,
+        *                and a `data-customize-setting-key-link` attribute if the setting is not yet registered.
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_link( $setting_key = 'default' ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! isset( $this->settings[ $setting_key ] ) )
-                       return '';
-
-               return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( isset( $this->settings[ $setting_key ] ) && $this->settings[ $setting_key ] instanceof WP_Customize_Setting ) {
+                       return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"';
+               } else {
+                       return 'data-customize-setting-key-link="' . esc_attr( $setting_key ) . '"';
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizemanagerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-manager.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-manager.php      2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/src/wp-includes/class-wp-customize-manager.php        2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3642,6 +3642,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <script type="text/html" id="tmpl-customize-trash-changeset-control">
</span><span class="cx" style="display: block; padding: 0 10px">                        <button type="button" class="button-link button-link-delete"><?php _e( 'Discard changes' ); ?></button>
</span><span class="cx" style="display: block; padding: 0 10px">                </script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <script type="text/html" id="tmpl-customize-selected-changeset-status-control">
+                       <# var inputId = _.uniqueId( 'customize-selected-changeset-status-control-input-' ); #>
+                       <# var descriptionId = _.uniqueId( 'customize-selected-changeset-status-control-description-' ); #>
+                       <# if ( data.label ) { #>
+                               <label for="{{ inputId }}" class="customize-control-title">{{ data.label }}</label>
+                       <# } #>
+                       <# if ( data.description ) { #>
+                               <span id="{{ descriptionId }}" class="description customize-control-description">{{{ data.description }}}</span>
+                       <# } #>
+                       <# _.each( data.choices, function( choice ) { #>
+                               <# var choiceId = inputId + '-' + choice.status; #>
+                               <span class="customize-inside-control-row">
+                                       <input id="{{ choiceId }}" type="radio" value="{{ choice.status }}" name="{{ inputId }}" data-customize-setting-key-link="default">
+                                       <label for="{{ choiceId }}">{{ choice.label }}</label>
+                               </span>
+                       <# } ); #>
+               </script>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <?php
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4024,12 +4041,39 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $current_user_can_publish = current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts );
+
+               // @todo Include all of the status labels here from script-loader.php, and then allow it to be filtered.
+               $status_choices = array();
+               if ( $current_user_can_publish ) {
+                       $status_choices[] = array(
+                               'status' => 'publish',
+                               'label' => __( 'Publish' ),
+                       );
+               }
+               $status_choices[] = array(
+                       'status' => 'draft',
+                       'label' => __( 'Save Draft' ),
+               );
+               if ( $current_user_can_publish ) {
+                       $status_choices[] = array(
+                               'status' => 'future',
+                               'label' => __( 'Schedule' ),
+                       );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Prepare Customizer settings to pass to JavaScript.
</span><span class="cx" style="display: block; padding: 0 10px">                $changeset_post = null;
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $changeset_post_id ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $changeset_post = get_post( $changeset_post_id );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( $this->changeset_post_id() && 'future' === get_post_status( $this->changeset_post_id() ) ) {
+                       $initial_date = get_the_time( 'Y-m-d H:i:s', $this->changeset_post_id() );
+               } else {
+                       $initial_date = current_time( 'mysql', false );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $settings = array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'changeset' => array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'uuid' => $this->changeset_uuid(),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4038,10 +4082,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'hasAutosaveRevision' => ! empty( $autosave_revision_post ),
</span><span class="cx" style="display: block; padding: 0 10px">                                'latestAutoDraftUuid' => $autosave_autodraft_post ? $autosave_autodraft_post->post_name : null,
</span><span class="cx" style="display: block; padding: 0 10px">                                'status' => $changeset_post ? $changeset_post->post_status : '',
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'currentUserCanPublish' => current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ),
-                               'publishDate' => $changeset_post ? $changeset_post->post_date : '', // @todo Only if future status? Rename to just date?
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'currentUserCanPublish' => $current_user_can_publish,
+                               'publishDate' => $initial_date,
+                               'statusChoices' => $status_choices,
</ins><span class="cx" style="display: block; padding: 0 10px">                         ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'initialServerDate' => current_time( 'mysql', false ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'dateFormat' => get_option( 'date_format' ),
+                       'timeFormat' => get_option( 'time_format' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         'initialServerTimestamp' => floor( microtime( true ) * 1000 ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'initialClientTimestamp' => -1, // To be set with JS below.
</span><span class="cx" style="display: block; padding: 0 10px">                        'timeouts' => array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4205,6 +4252,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /* Publish Settings */
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Note the controls for this section are added via JS.
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->add_section( 'publish_settings', array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'title' => __( 'Publish Settings' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'priority' => 0,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4213,47 +4261,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'active_callback' => array( $this, 'is_theme_active' ),
</span><span class="cx" style="display: block; padding: 0 10px">                ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /* Publish Settings Controls */
-               $status_choices = array(
-                       'publish' => __( 'Publish' ),
-                       'draft' => __( 'Save Draft' ),
-                       'future' => __( 'Schedule' ),
-               );
-
-               if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->publish_posts ) ) {
-                       unset( $status_choices['publish'] );
-               }
-
-               $this->add_control( 'changeset_status', array(
-                       'section' => 'publish_settings',
-                       'priority' => 10,
-                       'settings' => array(),
-                       'type' => 'radio',
-                       'label' => __( 'Action' ),
-                       'choices' => $status_choices,
-                       'capability' => 'customize',
-               ) );
-
-               if ( $this->changeset_post_id() && 'future' === get_post_status( $this->changeset_post_id() ) ) {
-                       $initial_date = get_the_time( 'Y-m-d H:i:s', $this->changeset_post_id() );
-               } else {
-                       $initial_date = current_time( 'mysql', false );
-               }
-
-               $this->add_control( new WP_Customize_Date_Time_Control( $this, 'changeset_scheduled_date', array(
-                       'section' => 'publish_settings',
-                       'priority' => 20,
-                       'settings' => array(),
-                       'type' => 'date_time',
-                       'min_year' => date( 'Y' ),
-                       'allow_past_date' => false,
-                       'include_time' => true,
-                       'twelve_hour_format' => false !== stripos( get_option( 'time_format' ), 'a' ),
-                       'description' => __( 'Schedule your customization changes to publish ("go live") at a future date.' ),
-                       'capability' => 'customize',
-                       'default_value' => $initial_date,
-               ) ) );
-
</del><span class="cx" style="display: block; padding: 0 10px">                 /* Themes (controls are loaded via ajax) */
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->add_panel( new WP_Customize_Themes_Panel( $this, 'themes', array(
</span></span></pre></div>
<a id="trunksrcwpincludescustomizeclasswpcustomizedatetimecontrolphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/customize/class-wp-customize-date-time-control.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/customize/class-wp-customize-date-time-control.php  2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/src/wp-includes/customize/class-wp-customize-date-time-control.php    2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -66,14 +66,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $twelve_hour_format = true;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Default date/time to be displayed in the control.
-        *
-        * @since 4.9.0
-        * @var string
-        */
-       public $default_value;
-
-       /**
</del><span class="cx" style="display: block; padding: 0 10px">          * Don't render the control's content - it's rendered with a JS template.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.9.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -94,7 +86,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $data['allowPastDate'] = (bool) $this->allow_past_date;
</span><span class="cx" style="display: block; padding: 0 10px">                $data['twelveHourFormat'] = (bool) $this->twelve_hour_format;
</span><span class="cx" style="display: block; padding: 0 10px">                $data['includeTime'] = (bool) $this->include_time;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $data['defaultValue'] = $this->default_value;
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return $data;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span></span></pre></div>
<a id="trunksrcwpincludesscriptloaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/script-loader.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/script-loader.php   2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/src/wp-includes/script-loader.php     2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -566,6 +566,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'saved'              => __( 'Saved' ),
</span><span class="cx" style="display: block; padding: 0 10px">                'cancel'             => __( 'Cancel' ),
</span><span class="cx" style="display: block; padding: 0 10px">                'close'              => __( 'Close' ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'action'             => __( 'Action' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                 'cheatin'            => __( 'Cheatin&#8217; uh?' ),
</span><span class="cx" style="display: block; padding: 0 10px">                'notAllowed'         => __( 'Sorry, you are not allowed to customize this site.' ),
</span><span class="cx" style="display: block; padding: 0 10px">                'previewIframeTitle' => __( 'Site Preview' ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -594,6 +595,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        _n_noop( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        array( 'singular', 'plural' )
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'scheduleDescription' => __( 'Schedule your customization changes to publish ("go live") at a future date.' ),
</ins><span class="cx" style="display: block; padding: 0 10px">         ) );
</span><span class="cx" style="display: block; padding: 0 10px">        $scripts->add( 'customize-selective-refresh', "/wp-includes/js/customize-selective-refresh$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunktestsphpunittestscustomizemanagerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/customize/manager.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/customize/manager.php   2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/tests/phpunit/tests/customize/manager.php     2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2581,7 +2581,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $data = json_decode( $json, true );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertNotEmpty( $data );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts', 'initialClientTimestamp', 'initialServerDate', 'initialServerTimestamp', 'l10n' ), array_keys( $data ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts', 'dateFormat', 'timeFormat', 'initialClientTimestamp', 'initialServerDate', 'initialServerTimestamp', 'l10n' ), array_keys( $data ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->assertEquals( $autofocus, $data['autofocus'] );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertArrayHasKey( 'save', $data['nonce'] );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertArrayHasKey( 'preview', $data['nonce'] );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2596,6 +2596,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'uuid',
</span><span class="cx" style="display: block; padding: 0 10px">                                'currentUserCanPublish',
</span><span class="cx" style="display: block; padding: 0 10px">                                'publishDate',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'statusChoices',
</ins><span class="cx" style="display: block; padding: 0 10px">                         ),
</span><span class="cx" style="display: block; padding: 0 10px">                        array_keys( $data['changeset'] )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span></span></pre></div>
<a id="trunktestsqunitwpadminjscustomizecontrolsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/wp-admin/js/customize-controls.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/wp-admin/js/customize-controls.js       2017-10-04 19:58:31 UTC (rev 41749)
+++ trunk/tests/qunit/wp-admin/js/customize-controls.js 2017-10-04 20:01:12 UTC (rev 41750)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -710,9 +710,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        params: {
</span><span class="cx" style="display: block; padding: 0 10px">                                section: section.id,
</span><span class="cx" style="display: block; padding: 0 10px">                                type: 'date_time',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                setting: new wp.customize.Value( datetime ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                 includeTime: true,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                content: '<li id="customize-control-' + controlId + '" class="customize-control"></li>',
-                               defaultValue: datetime
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         content: '<li id="customize-control-' + controlId + '" class="customize-control"></li>'
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>