<!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>[39112] trunk/src: Customize: Prevent syncing unmodified settings from controls into preview to guard against triggering an infinite reload due to selective refresh fallback behavior.</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/39112">39112</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/39112","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>2016-11-03 05:06:17 +0000 (Thu, 03 Nov 2016)</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: Prevent syncing unmodified settings from controls into preview to guard against triggering an infinite reload due to selective refresh fallback behavior.

If a value is sanitized in PHP and differs from the JS value in the pane, a `change` event for the setting is triggered upon refresh. This should be avoided since the value just came from the server as being sanitized. This also fixes periodic issue where selective refresh happens immediately after a full refresh.

Fixes <a href="https://core.trac.wordpress.org/ticket/37032">#37032</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminjscustomizecontrolsjs">trunk/src/wp-admin/js/customize-controls.js</a></li>
<li><a href="#trunksrcwpincludesjscustomizepreviewjs">trunk/src/wp-includes/js/customize-preview.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<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       2016-11-03 04:57:00 UTC (rev 39111)
+++ trunk/src/wp-admin/js/customize-controls.js 2016-11-03 05:06:17 UTC (rev 39112)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4299,6 +4299,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        var previewer = this, synced = {}, constructs;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        synced.settings = api.get();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        synced['settings-modified-while-loading'] = previewer.settingsModifiedWhileLoading;
</ins><span class="cx" style="display: block; padding: 0 10px">                         if ( 'resolved' !== previewer.deferred.active.state() || previewer.loading ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                synced.scroll = previewer.scroll;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4421,7 +4422,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * Refresh the preview seamlessly.
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                refresh: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var previewer = this;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var previewer = this, onSettingChange;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Display loading indicator
</span><span class="cx" style="display: block; padding: 0 10px">                        previewer.send( 'loading-initiated' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -4435,6 +4436,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                container:  previewer.container
</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">+                        previewer.settingsModifiedWhileLoading = {};
+                       onSettingChange = function( setting ) {
+                               previewer.settingsModifiedWhileLoading[ setting.id ] = true;
+                       };
+                       api.bind( 'change', onSettingChange );
+                       previewer.loading.always( function() {
+                               api.unbind( 'change', onSettingChange );
+                       } );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         previewer.loading.done( function( readyData ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                var loadingFrame = this, previousPreview, onceSynced;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpincludesjscustomizepreviewjs"></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/js/customize-preview.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/customize-preview.js     2016-11-03 04:57:00 UTC (rev 39111)
+++ trunk/src/wp-includes/js/customize-preview.js       2016-11-03 05:06:17 UTC (rev 39112)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -654,6 +654,25 @@
</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">                api.preview.bind( 'sync', function( events ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       /*
+                        * Delete any settings that already exist locally which haven't been
+                        * modified in the controls while the preview was loading. This prevents
+                        * situations where the JS value being synced from the pane may differ
+                        * from the PHP-sanitized JS value in the preview which causes the
+                        * non-sanitized JS value to clobber the PHP-sanitized value. This
+                        * is particularly important for selective refresh partials that
+                        * have a fallback refresh behavior since infinite refreshing would
+                        * result.
+                        */
+                       if ( events.settings && events['settings-modified-while-loading'] ) {
+                               _.each( _.keys( events.settings ), function( syncedSettingId ) {
+                                       if ( api.has( syncedSettingId ) && ! events['settings-modified-while-loading'][ syncedSettingId ] ) {
+                                               delete events.settings[ syncedSettingId ];
+                                       }
+                               } );
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $.each( events, function( event, args ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                api.preview.trigger( event, args );
</span><span class="cx" style="display: block; padding: 0 10px">                        });
</span></span></pre>
</div>
</div>

</body>
</html>