WordPress.org

Make WordPress Core

Changeset 41095


Ignore:
Timestamp:
07/19/17 20:50:48 (4 months ago)
Author:
westonruter
Message:

Widgets: Improve Text widget's reliability in syncing Visual tab's contents with Text tab and with hidden sync inputs.

Merges [41094] onto 4.8 branch.
Amends [40631].
Props azaozz, westonruter, rinkuyadav999 for testing.
See #35243.
Fixes #40974 for 4.8.1.

Location:
branches/4.8
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/4.8

  • branches/4.8/src/wp-admin/js/widgets/text-widgets.js

    r41089 r41095  
    8282                fieldInput.on( 'input change', function updateSyncField() { 
    8383                    var syncInput = control.syncContainer.find( 'input[type=hidden].' + fieldName ); 
    84                     if ( syncInput.val() !== $( this ).val() ) { 
    85                         syncInput.val( $( this ).val() ); 
     84                    if ( syncInput.val() !== fieldInput.val() ) { 
     85                        syncInput.val( fieldInput.val() ); 
    8686                        syncInput.trigger( 'change' ); 
    8787                    } 
     
    165165         */ 
    166166        initializeEditor: function initializeEditor() { 
    167             var control = this, changeDebounceDelay = 1000, id, textarea, restoreTextMode = false; 
     167            var control = this, changeDebounceDelay = 1000, id, textarea, triggerChangeIfDirty, restoreTextMode = false, needsTextareaChangeTrigger = false; 
    168168            textarea = control.fields.text; 
    169169            id = textarea.attr( 'id' ); 
     170 
     171            /** 
     172             * Trigger change if dirty. 
     173             * 
     174             * @returns {void} 
     175             */ 
     176            triggerChangeIfDirty = function() { 
     177                var updateWidgetBuffer = 300; // See wp.customize.Widgets.WidgetControl._setupUpdateUI() which uses 250ms for updateWidgetDebounced. 
     178                if ( control.editor.isDirty() ) { 
     179 
     180                    /* 
     181                     * Account for race condition in customizer where user clicks Save & Publish while 
     182                     * focus was just previously given to to the editor. Since updates to the editor 
     183                     * are debounced at 1 second and since widget input changes are only synced to 
     184                     * settings after 250ms, the customizer needs to be put into the processing 
     185                     * state during the time between the change event is triggered and updateWidget 
     186                     * logic starts. Note that the debounced update-widget request should be able 
     187                     * to be removed with the removal of the update-widget request entirely once 
     188                     * widgets are able to mutate their own instance props directly in JS without 
     189                     * having to make server round-trips to call the respective WP_Widget::update() 
     190                     * callbacks. See <https://core.trac.wordpress.org/ticket/33507>. 
     191                     */ 
     192                    if ( wp.customize && wp.customize.state ) { 
     193                        wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() + 1 ); 
     194                        _.delay( function() { 
     195                            wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() - 1 ); 
     196                        }, updateWidgetBuffer ); 
     197                    } 
     198 
     199                    if ( ! control.editor.isHidden() ) { 
     200                        control.editor.save(); 
     201                    } 
     202                } 
     203 
     204                // Trigger change on textarea when it is dirty for sake of widgets in the Customizer needing to sync form inputs to setting models. 
     205                if ( needsTextareaChangeTrigger ) { 
     206                    textarea.trigger( 'change' ); 
     207                    needsTextareaChangeTrigger = false; 
     208                } 
     209            }; 
     210 
     211            // Just-in-time force-update the hidden input fields. 
     212            control.syncContainer.closest( '.widget' ).find( '[name=savewidget]:first' ).on( 'click', function onClickSaveButton() { 
     213                triggerChangeIfDirty(); 
     214            }); 
    170215 
    171216            /** 
     
    175220             */ 
    176221            function buildEditor() { 
    177                 var editor, triggerChangeIfDirty, onInit, showPointerElement; 
     222                var editor, onInit, showPointerElement; 
    178223 
    179224                // Abort building if the textarea is gone, likely due to the widget having been deleted entirely. 
     
    231276                    // If a prior mce instance was replaced, and it was in text mode, toggle to text mode. 
    232277                    if ( restoreTextMode ) { 
    233                         switchEditors.go( id, 'toggle' ); 
     278                        switchEditors.go( id, 'html' ); 
    234279                    } 
    235280 
     
    270315 
    271316                control.editorFocused = false; 
    272                 triggerChangeIfDirty = function() { 
    273                     var updateWidgetBuffer = 300; // See wp.customize.Widgets.WidgetControl._setupUpdateUI() which uses 250ms for updateWidgetDebounced. 
    274                     if ( editor.isDirty() ) { 
    275  
    276                         /* 
    277                          * Account for race condition in customizer where user clicks Save & Publish while 
    278                          * focus was just previously given to to the editor. Since updates to the editor 
    279                          * are debounced at 1 second and since widget input changes are only synced to 
    280                          * settings after 250ms, the customizer needs to be put into the processing 
    281                          * state during the time between the change event is triggered and updateWidget 
    282                          * logic starts. Note that the debounced update-widget request should be able 
    283                          * to be removed with the removal of the update-widget request entirely once 
    284                          * widgets are able to mutate their own instance props directly in JS without 
    285                          * having to make server round-trips to call the respective WP_Widget::update() 
    286                          * callbacks. See <https://core.trac.wordpress.org/ticket/33507>. 
    287                          */ 
    288                         if ( wp.customize && wp.customize.state ) { 
    289                             wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() + 1 ); 
    290                             _.delay( function() { 
    291                                 wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() - 1 ); 
    292                             }, updateWidgetBuffer ); 
    293                         } 
    294  
    295                         editor.save(); 
    296                         textarea.trigger( 'change' ); 
    297                     } 
    298                 }; 
    299                 editor.on( 'focus', function() { 
     317 
     318                editor.on( 'focus', function onEditorFocus() { 
    300319                    control.editorFocused = true; 
    301320                }); 
     321                editor.on( 'paste', function onEditorPaste() { 
     322                    editor.setDirty( true ); // Because pasting doesn't currently set the dirty state. 
     323                    triggerChangeIfDirty(); 
     324                }); 
     325                editor.on( 'NodeChange', function onNodeChange() { 
     326                    needsTextareaChangeTrigger = true; 
     327                }); 
    302328                editor.on( 'NodeChange', _.debounce( triggerChangeIfDirty, changeDebounceDelay ) ); 
    303                 editor.on( 'blur', function() { 
     329                editor.on( 'blur hide', function onEditorBlur() { 
    304330                    control.editorFocused = false; 
    305331                    triggerChangeIfDirty(); 
Note: See TracChangeset for help on using the changeset viewer.