Make WordPress Core

Changeset 41095


Ignore:
Timestamp:
07/19/2017 08:50:48 PM (7 years 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.