WordPress.org

Make WordPress Core

Changeset 37867


Ignore:
Timestamp:
06/26/2016 11:54:20 AM (3 years ago)
Author:
westonruter
Message:

Customize: Always define functions reflowPaneContents, findControlsForSettings, and _handleSettingValidities on wp.customize.

Moves definitions of functions outside of a jQuery.ready() callback, as these functions needn't be deferred to DOM ready. This change also ensures that the functions are available if customize-controls is enqueued outside of the Customizer context, as the ready callback short-circuits if _wpCustomizeSettings is not defined. The findControlsForSettings and _handleSettingValidities functions were misplaced in r37700.

See #34893.
See #36944.
See #29071.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/js/customize-controls.js

    r37701 r37867  
    34203420    };
    34213421
     3422    /**
     3423     * Handle setting_validities in an error response for the customize-save request.
     3424     *
     3425     * Add notifications to the settings and focus on the first control that has an invalid setting.
     3426     *
     3427     * @since 4.6.0
     3428     * @private
     3429     *
     3430     * @param {object}  args
     3431     * @param {object}  args.settingValidities
     3432     * @param {boolean} [args.focusInvalidControl=false]
     3433     * @returns {void}
     3434     */
     3435    api._handleSettingValidities = function handleSettingValidities( args ) {
     3436        var invalidSettingControls, invalidSettings = [], wasFocused = false;
     3437
     3438        // Find the controls that correspond to each invalid setting.
     3439        _.each( args.settingValidities, function( validity, settingId ) {
     3440            var setting = api( settingId );
     3441            if ( setting ) {
     3442
     3443                // Add notifications for invalidities.
     3444                if ( _.isObject( validity ) ) {
     3445                    _.each( validity, function( params, code ) {
     3446                        var notification = new api.Notification( code, params ), existingNotification, needsReplacement = false;
     3447
     3448                        // Remove existing notification if already exists for code but differs in parameters.
     3449                        existingNotification = setting.notifications( notification.code );
     3450                        if ( existingNotification ) {
     3451                            needsReplacement = ( notification.type !== existingNotification.type ) || ! _.isEqual( notification.data, existingNotification.data );
     3452                        }
     3453                        if ( needsReplacement ) {
     3454                            setting.notifications.remove( code );
     3455                        }
     3456
     3457                        if ( ! setting.notifications.has( notification.code ) ) {
     3458                            setting.notifications.add( code, notification );
     3459                        }
     3460                        invalidSettings.push( setting.id );
     3461                    } );
     3462                }
     3463
     3464                // Remove notification errors that are no longer valid.
     3465                setting.notifications.each( function( notification ) {
     3466                    if ( 'error' === notification.type && ( true === validity || ! validity[ notification.code ] ) ) {
     3467                        setting.notifications.remove( notification.code );
     3468                    }
     3469                } );
     3470            }
     3471        } );
     3472
     3473        if ( args.focusInvalidControl ) {
     3474            invalidSettingControls = api.findControlsForSettings( invalidSettings );
     3475
     3476            // Focus on the first control that is inside of an expanded section (one that is visible).
     3477            _( _.values( invalidSettingControls ) ).find( function( controls ) {
     3478                return _( controls ).find( function( control ) {
     3479                    var isExpanded = control.section() && api.section.has( control.section() ) && api.section( control.section() ).expanded();
     3480                    if ( isExpanded && control.expanded ) {
     3481                        isExpanded = control.expanded();
     3482                    }
     3483                    if ( isExpanded ) {
     3484                        control.focus();
     3485                        wasFocused = true;
     3486                    }
     3487                    return wasFocused;
     3488                } );
     3489            } );
     3490
     3491            // Focus on the first invalid control.
     3492            if ( ! wasFocused && ! _.isEmpty( invalidSettingControls ) ) {
     3493                _.values( invalidSettingControls )[0][0].focus();
     3494            }
     3495        }
     3496    };
     3497
     3498    /**
     3499     * Find all controls associated with the given settings.
     3500     *
     3501     * @since 4.6.0
     3502     * @param {string[]} settingIds Setting IDs.
     3503     * @returns {object<string, wp.customize.Control>} Mapping setting ids to arrays of controls.
     3504     */
     3505    api.findControlsForSettings = function findControlsForSettings( settingIds ) {
     3506        var controls = {}, settingControls;
     3507        _.each( _.unique( settingIds ), function( settingId ) {
     3508            var setting = api( settingId );
     3509            if ( setting ) {
     3510                settingControls = setting.findControls();
     3511                if ( settingControls && settingControls.length > 0 ) {
     3512                    controls[ settingId ] = settingControls;
     3513                }
     3514            }
     3515        } );
     3516        return controls;
     3517    };
     3518
     3519    /**
     3520     * Sort panels, sections, controls by priorities. Hide empty sections and panels.
     3521     *
     3522     * @since 4.1.0
     3523     */
     3524    api.reflowPaneContents = _.bind( function () {
     3525
     3526        var appendContainer, activeElement, rootContainers, rootNodes = [], wasReflowed = false;
     3527
     3528        if ( document.activeElement ) {
     3529            activeElement = $( document.activeElement );
     3530        }
     3531
     3532        // Sort the sections within each panel
     3533        api.panel.each( function ( panel ) {
     3534            var sections = panel.sections(),
     3535                sectionContainers = _.pluck( sections, 'container' );
     3536            rootNodes.push( panel );
     3537            appendContainer = panel.container.find( 'ul:first' );
     3538            if ( ! api.utils.areElementListsEqual( sectionContainers, appendContainer.children( '[id]' ) ) ) {
     3539                _( sections ).each( function ( section ) {
     3540                    appendContainer.append( section.container );
     3541                } );
     3542                wasReflowed = true;
     3543            }
     3544        } );
     3545
     3546        // Sort the controls within each section
     3547        api.section.each( function ( section ) {
     3548            var controls = section.controls(),
     3549                controlContainers = _.pluck( controls, 'container' );
     3550            if ( ! section.panel() ) {
     3551                rootNodes.push( section );
     3552            }
     3553            appendContainer = section.container.find( 'ul:first' );
     3554            if ( ! api.utils.areElementListsEqual( controlContainers, appendContainer.children( '[id]' ) ) ) {
     3555                _( controls ).each( function ( control ) {
     3556                    appendContainer.append( control.container );
     3557                } );
     3558                wasReflowed = true;
     3559            }
     3560        } );
     3561
     3562        // Sort the root panels and sections
     3563        rootNodes.sort( api.utils.prioritySort );
     3564        rootContainers = _.pluck( rootNodes, 'container' );
     3565        appendContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
     3566        if ( ! api.utils.areElementListsEqual( rootContainers, appendContainer.children() ) ) {
     3567            _( rootNodes ).each( function ( rootNode ) {
     3568                appendContainer.append( rootNode.container );
     3569            } );
     3570            wasReflowed = true;
     3571        }
     3572
     3573        // Now re-trigger the active Value callbacks to that the panels and sections can decide whether they can be rendered
     3574        api.panel.each( function ( panel ) {
     3575            var value = panel.active();
     3576            panel.active.callbacks.fireWith( panel.active, [ value, value ] );
     3577        } );
     3578        api.section.each( function ( section ) {
     3579            var value = section.active();
     3580            section.active.callbacks.fireWith( section.active, [ value, value ] );
     3581        } );
     3582
     3583        // Restore focus if there was a reflow and there was an active (focused) element
     3584        if ( wasReflowed && activeElement ) {
     3585            activeElement.focus();
     3586        }
     3587        api.trigger( 'pane-contents-reflowed' );
     3588    }, api );
     3589
    34223590    $( function() {
    34233591        api.settings = window._wpCustomizeSettings;
     
    37103878        });
    37113879
    3712         /**
    3713          * Handle setting_validities in an error response for the customize-save request.
    3714          *
    3715          * Add notifications to the settings and focus on the first control that has an invalid setting.
    3716          *
    3717          * @since 4.6.0
    3718          * @private
    3719          *
    3720          * @param {object}  args
    3721          * @param {object}  args.settingValidities
    3722          * @param {boolean} [args.focusInvalidControl=false]
    3723          * @returns {void}
    3724          */
    3725         api._handleSettingValidities = function handleSettingValidities( args ) {
    3726             var invalidSettingControls, invalidSettings = [], wasFocused = false;
    3727 
    3728             // Find the controls that correspond to each invalid setting.
    3729             _.each( args.settingValidities, function( validity, settingId ) {
    3730                 var setting = api( settingId );
    3731                 if ( setting ) {
    3732 
    3733                     // Add notifications for invalidities.
    3734                     if ( _.isObject( validity ) ) {
    3735                         _.each( validity, function( params, code ) {
    3736                             var notification = new api.Notification( code, params ), existingNotification, needsReplacement = false;
    3737 
    3738                             // Remove existing notification if already exists for code but differs in parameters.
    3739                             existingNotification = setting.notifications( notification.code );
    3740                             if ( existingNotification ) {
    3741                                 needsReplacement = ( notification.type !== existingNotification.type ) || ! _.isEqual( notification.data, existingNotification.data );
    3742                             }
    3743                             if ( needsReplacement ) {
    3744                                 setting.notifications.remove( code );
    3745                             }
    3746 
    3747                             if ( ! setting.notifications.has( notification.code ) ) {
    3748                                 setting.notifications.add( code, notification );
    3749                             }
    3750                             invalidSettings.push( setting.id );
    3751                         } );
    3752                     }
    3753 
    3754                     // Remove notification errors that are no longer valid.
    3755                     setting.notifications.each( function( notification ) {
    3756                         if ( 'error' === notification.type && ( true === validity || ! validity[ notification.code ] ) ) {
    3757                             setting.notifications.remove( notification.code );
    3758                         }
    3759                     } );
    3760                 }
    3761             } );
    3762 
    3763             if ( args.focusInvalidControl ) {
    3764                 invalidSettingControls = api.findControlsForSettings( invalidSettings );
    3765 
    3766                 // Focus on the first control that is inside of an expanded section (one that is visible).
    3767                 _( _.values( invalidSettingControls ) ).find( function( controls ) {
    3768                     return _( controls ).find( function( control ) {
    3769                         var isExpanded = control.section() && api.section.has( control.section() ) && api.section( control.section() ).expanded();
    3770                         if ( isExpanded && control.expanded ) {
    3771                             isExpanded = control.expanded();
    3772                         }
    3773                         if ( isExpanded ) {
    3774                             control.focus();
    3775                             wasFocused = true;
    3776                         }
    3777                         return wasFocused;
    3778                     } );
    3779                 } );
    3780 
    3781                 // Focus on the first invalid control.
    3782                 if ( ! wasFocused && ! _.isEmpty( invalidSettingControls ) ) {
    3783                     _.values( invalidSettingControls )[0][0].focus();
    3784                 }
    3785             }
    3786         };
    3787 
    3788         /**
    3789          * Find all controls associated with the given settings.
    3790          *
    3791          * @since 4.6.0
    3792          * @param {string[]} settingIds Setting IDs.
    3793          * @returns {object<string, wp.customize.Control>} Mapping setting ids to arrays of controls.
    3794          */
    3795         api.findControlsForSettings = function findControlsForSettings( settingIds ) {
    3796             var controls = {}, settingControls;
    3797             _.each( _.unique( settingIds ), function( settingId ) {
    3798                 var setting = api( settingId );
    3799                 if ( setting ) {
    3800                     settingControls = setting.findControls();
    3801                     if ( settingControls && settingControls.length > 0 ) {
    3802                         controls[ settingId ] = settingControls;
    3803                     }
    3804                 }
    3805             } );
    3806             return controls;
    3807         };
    3808 
    3809         /**
    3810          * Sort panels, sections, controls by priorities. Hide empty sections and panels.
    3811          *
    3812          * @since 4.1.0
    3813          */
    3814         api.reflowPaneContents = _.bind( function () {
    3815 
    3816             var appendContainer, activeElement, rootContainers, rootNodes = [], wasReflowed = false;
    3817 
    3818             if ( document.activeElement ) {
    3819                 activeElement = $( document.activeElement );
    3820             }
    3821 
    3822             // Sort the sections within each panel
    3823             api.panel.each( function ( panel ) {
    3824                 var sections = panel.sections(),
    3825                     sectionContainers = _.pluck( sections, 'container' );
    3826                 rootNodes.push( panel );
    3827                 appendContainer = panel.container.find( 'ul:first' );
    3828                 if ( ! api.utils.areElementListsEqual( sectionContainers, appendContainer.children( '[id]' ) ) ) {
    3829                     _( sections ).each( function ( section ) {
    3830                         appendContainer.append( section.container );
    3831                     } );
    3832                     wasReflowed = true;
    3833                 }
    3834             } );
    3835 
    3836             // Sort the controls within each section
    3837             api.section.each( function ( section ) {
    3838                 var controls = section.controls(),
    3839                     controlContainers = _.pluck( controls, 'container' );
    3840                 if ( ! section.panel() ) {
    3841                     rootNodes.push( section );
    3842                 }
    3843                 appendContainer = section.container.find( 'ul:first' );
    3844                 if ( ! api.utils.areElementListsEqual( controlContainers, appendContainer.children( '[id]' ) ) ) {
    3845                     _( controls ).each( function ( control ) {
    3846                         appendContainer.append( control.container );
    3847                     } );
    3848                     wasReflowed = true;
    3849                 }
    3850             } );
    3851 
    3852             // Sort the root panels and sections
    3853             rootNodes.sort( api.utils.prioritySort );
    3854             rootContainers = _.pluck( rootNodes, 'container' );
    3855             appendContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
    3856             if ( ! api.utils.areElementListsEqual( rootContainers, appendContainer.children() ) ) {
    3857                 _( rootNodes ).each( function ( rootNode ) {
    3858                     appendContainer.append( rootNode.container );
    3859                 } );
    3860                 wasReflowed = true;
    3861             }
    3862 
    3863             // Now re-trigger the active Value callbacks to that the panels and sections can decide whether they can be rendered
    3864             api.panel.each( function ( panel ) {
    3865                 var value = panel.active();
    3866                 panel.active.callbacks.fireWith( panel.active, [ value, value ] );
    3867             } );
    3868             api.section.each( function ( section ) {
    3869                 var value = section.active();
    3870                 section.active.callbacks.fireWith( section.active, [ value, value ] );
    3871             } );
    3872 
    3873             // Restore focus if there was a reflow and there was an active (focused) element
    3874             if ( wasReflowed && activeElement ) {
    3875                 activeElement.focus();
    3876             }
    3877             api.trigger( 'pane-contents-reflowed' );
    3878         }, api );
    38793880        api.bind( 'ready', api.reflowPaneContents );
    3880         api.reflowPaneContents = _.debounce( api.reflowPaneContents, 100 );
    38813881        $( [ api.panel, api.section, api.control ] ).each( function ( i, values ) {
    3882             values.bind( 'add', api.reflowPaneContents );
    3883             values.bind( 'change', api.reflowPaneContents );
    3884             values.bind( 'remove', api.reflowPaneContents );
     3882            var debouncedReflowPaneContents = _.debounce( api.reflowPaneContents, 100 );
     3883            values.bind( 'add', debouncedReflowPaneContents );
     3884            values.bind( 'change', debouncedReflowPaneContents );
     3885            values.bind( 'remove', debouncedReflowPaneContents );
    38853886        } );
    38863887
     
    39293930
    39303931            activated.bind( function( to ) {
    3931                 if ( to )
     3932                if ( to ) {
    39323933                    api.trigger( 'activated' );
     3934                }
    39333935            });
    39343936
Note: See TracChangeset for help on using the changeset viewer.