diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 56c061f04a..b43c1df109 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -4299,6 +4299,7 @@ var previewer = this, synced = {}, constructs; synced.settings = api.get(); + synced['settings-modified-while-loading'] = previewer.settingsModifiedWhileLoading; if ( 'resolved' !== previewer.deferred.active.state() || previewer.loading ) { synced.scroll = previewer.scroll; } @@ -4421,7 +4422,7 @@ * Refresh the preview seamlessly. */ refresh: function() { - var previewer = this; + var previewer = this, onSettingChange; // Display loading indicator previewer.send( 'loading-initiated' ); @@ -4435,6 +4436,15 @@ container: previewer.container }); + previewer.settingsModifiedWhileLoading = {}; + onSettingChange = function( setting ) { + previewer.settingsModifiedWhileLoading[ setting.id ] = true; + }; + api.bind( 'change', onSettingChange ); + previewer.loading.always( function() { + api.unbind( 'change', onSettingChange ); + } ); + previewer.loading.done( function( readyData ) { var loadingFrame = this, previousPreview, onceSynced; diff --git a/src/wp-includes/js/customize-preview.js b/src/wp-includes/js/customize-preview.js index 484ccf4acc..be24e414bf 100644 --- a/src/wp-includes/js/customize-preview.js +++ b/src/wp-includes/js/customize-preview.js @@ -654,6 +654,25 @@ }); api.preview.bind( 'sync', function( events ) { + + /* + * 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 ]; + } + } ); + } + $.each( events, function( event, args ) { api.preview.trigger( event, args ); });