WordPress.org

Make WordPress Core

Ticket #34391: 34391_2.diff

File 34391_2.diff, 55.7 KB (added by delawski, 3 years ago)
  • src/wp-admin/css/common.css

    diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css
    index c699ad8..3d9014a 100644
    a b img { 
    33303330        display: none;
    33313331}
    33323332
    3333 .control-section .accordion-section-title {
     3333.control-section .accordion-section-title,
     3334.customize-pane-child .accordion-section-title {
    33343335        border-left: none;
    33353336        border-right: none;
    33363337        padding: 10px 10px 11px 14px;
    img { 
    33383339        background: #fff;
    33393340}
    33403341
    3341 .control-section .accordion-section-title:after {
     3342.control-section .accordion-section-title:after,
     3343.customize-pane-child .accordion-section-title:after {
    33423344        top: 11px;
    33433345}
    33443346
  • src/wp-admin/css/customize-controls.css

    diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css
    index a7648fd..d15b1e0 100644
    a b body { 
    7878        line-height: 24px;
    7979}
    8080
    81 #customize-controls .control-section .customize-section-title h3,
    82 #customize-controls .control-section h3.customize-section-title,
     81#customize-controls .customize-pane-child .customize-section-title h3,
     82#customize-controls .customize-pane-child h3.customize-section-title,
    8383#customize-controls .customize-info .panel-title {
    8484        font-size: 20px;
    8585        font-weight: 200;
    body { 
    219219        box-sizing: border-box;
    220220}
    221221
     222.wp-full-overlay #customize-controls .wp-full-overlay-sidebar-content {
     223        transform: translate3d(0, 0, 0); /* Promote to separate layer to avoid full-screen repaints */
     224}
     225
     226#customize-info,
     227#customize-theme-controls .customize-pane-parent,
     228#customize-theme-controls .customize-pane-child {
     229        overflow: visible;
     230        width: 100%;
     231        margin: 0;
     232        padding: 0;
     233        -webkit-box-sizing: border-box;
     234        -moz-box-sizing: border-box;
     235        box-sizing: border-box;
     236        transition: 0.36s transform cubic-bezier(0.645, 0.045, 0.355, 1); /* easeInOutCubic */
     237}
     238
     239#customize-info,
     240#customize-theme-controls .customize-pane-parent {
     241        position: relative;
     242        visibility: visible;
     243        height: auto;
     244        max-height: none;
     245        overflow: auto;
     246        transform: none;
     247}
     248
     249#customize-theme-controls .customize-pane-child {
     250        position: absolute;
     251        top: 0;
     252        left: 0;
     253        transform: translateX(100%);
     254        visibility: hidden;
     255        height: 0;
     256        max-height: none;
     257        overflow: hidden;
     258}
     259
     260#customize-theme-controls .customize-pane-child.open,
     261#customize-theme-controls .customize-pane-child.current-panel,
     262#customize-theme-controls .customize-themes-panel.customize-pane-child.current-panel {
     263        transform: none;
     264}
     265
     266#customize-theme-controls .customize-themes-panel.customize-pane-child,
     267.section-open #customize-theme-controls .customize-pane-parent,
     268.in-sub-panel #customize-theme-controls .customize-pane-parent,
     269.section-open #customize-info,
     270.in-sub-panel #customize-info,
     271.in-sub-panel.section-open #customize-theme-controls .customize-pane-child.current-panel,
     272.in-themes-panel #customize-theme-controls .customize-pane-parent,
     273.in-themes-panel #customize-info {
     274        visibility: hidden;
     275        height: 0;
     276        overflow: hidden;
     277        transform: translateX(-100%);
     278}
     279
     280.section-open #customize-theme-controls .customize-pane-parent.busy,
     281.in-sub-panel #customize-theme-controls .customize-pane-parent.busy,
     282.in-themes-panel #customize-theme-controls .customize-pane-parent.busy,
     283.section-open #customize-info.busy,
     284.in-sub-panel #customize-info.busy,
     285.in-themes-panel #customize-info.busy,
     286.busy.section-open.in-sub-panel #customize-theme-controls .customize-pane-child.current-panel,
     287#customize-theme-controls .customize-pane-child.open,
     288#customize-theme-controls .customize-pane-child.current-panel,
     289#customize-theme-controls .customize-pane-child.busy {
     290        visibility: visible;
     291        height: auto;
     292        overflow: auto;
     293}
     294
     295.in-themes-panel #customize-theme-controls .customize-pane-parent,
     296.in-themes-panel #customize-info {
     297        transform: translateX(100%);
     298}
     299
     300#customize-theme-controls .customize-pane-child.accordion-section-content,
     301#customize-theme-controls .customize-pane-child.accordion-sub-container {
     302        display: block;
     303        overflow-x: hidden;
     304}
     305
     306#customize-theme-controls .customize-pane-child.accordion-section-content {
     307        padding: 12px;
     308}
     309
    222310.customize-section-description-container {
    223311        margin-bottom: 15px;
    224312}
    h3.customize-section-title { 
    258346        color: #555;
    259347}
    260348
    261 #customize-theme-controls {
    262         position: relative;
    263         left: 0;
    264         -webkit-transition: .18s left ease-in-out;
    265         transition: .18s left ease-in-out;
    266 }
    267 
    268 .ios #customize-theme-controls {
    269         -webkit-transition: left 0s;
    270         transition: left 0s;
    271 }
    272 
    273 .section-open #customize-info,
    274 .section-open #customize-theme-controls {
    275         left: -100%;
    276 }
    277 
    278349.accordion-sub-container.control-panel-content {
    279350        display: none;
    280351        position: absolute;
    281         left: 100%;
    282352        top: 0;
    283353        width: 100%;
    284         -webkit-transition: left ease-in-out .18s;
    285         transition: left ease-in-out .18s;
    286 }
    287 
    288 .ios .accordion-sub-container.control-panel-content {
    289         -webkit-transition: left 0s;
    290         transition: left 0s;
    291354}
    292355
    293 .accordion-sub-container.control-panel-content.animating {
     356.accordion-sub-container.control-panel-content.busy {
    294357        display: block;
    295358}
    296359
    h3.customize-section-title { 
    333396        -webkit-box-shadow: none;
    334397        box-shadow: none;
    335398        cursor: pointer;
    336         -webkit-transition: left .18s ease-in-out, color .1s ease-in-out, background .1s ease-in-out;
    337         transition: left .18s ease-in-out, color .1s ease-in-out, background .1s ease-in-out;
     399        -webkit-transition: color .1s ease-in-out, background .1s ease-in-out;
     400        transition: color .1s ease-in-out, background .1s ease-in-out;
    338401}
    339402
    340403.customize-section-back {
    341404        height: 74px;
    342405}
    343406
    344 .ios .customize-panel-back,
    345 .ios .customize-section-back {
    346         -webkit-transition: left 0s;
    347         transition: left 0s;
    348 }
    349 
    350407.ios .customize-panel-back {
    351408        display: none;
    352409}
    h3.customize-section-title { 
    412469        padding-left: 62px;
    413470}
    414471
    415 #customize-info,
    416 #customize-theme-controls > ul > .accordion-section {
    417         position: relative;
    418         left: 0;
    419         -webkit-transition: left ease-in-out .18s;
    420         transition: left ease-in-out .18s;
    421 }
    422 
    423 .ios #customize-info,
    424 .ios #customize-theme-controls > ul > .accordion-section {
    425         -webkit-transition: left 0s;
    426         transition: left 0s;
    427 }
    428 
    429 .in-sub-panel #customize-info,
    430 .in-sub-panel #customize-theme-controls > ul > .accordion-section {
    431         left: -100%;
    432         width: 100%;
    433 }
    434 
    435 .in-sub-panel #customize-theme-controls .accordion-section.current-panel {
    436         width: 100%;
    437 }
    438 
    439 #customize-theme-controls .control-section.current-panel {
    440         padding: 0;
    441 }
    442 
    443 #customize-theme-controls .control-section > h3.accordion-section-title {
    444         position: relative;
    445         left: 0;
    446 }
    447 
    448 #customize-theme-controls .control-section.current-panel > h3.accordion-section-title {
    449         left: -354px;
    450         -webkit-transition: left ease-in-out .18s;
    451         transition: left ease-in-out .18s;
    452 }
    453 
    454 .ios #customize-theme-controls .control-section.current-panel > h3.accordion-section-title {
    455         -webkit-transition: left 0s;
    456         transition: left 0s;
    457 }
    458 
    459 .wp-full-overlay.section-open #customize-controls .wp-full-overlay-sidebar-content {
    460         visibility: hidden;
    461         overflow-y: hidden;
    462 }
    463 
    464 .wp-full-overlay.section-open .wp-full-overlay-sidebar-content .accordion-section.open {
    465         visibility: visible;
    466 }
    467 
    468 .wp-full-overlay.section-open .wp-full-overlay-sidebar-content .accordion-section.open .accordion-section-content {
    469         overflow-y: auto;
    470 }
    471 
    472472p.customize-section-description {
    473473        font-style: normal;
    474474        margin-top: 22px;
    p.customize-section-description { 
    976976        animation: customize-reload .75s;
    977977}
    978978
    979 .control-section-themes .accordion-section-title {
     979#customize-controls .control-section-themes .accordion-section-title {
    980980        cursor: default;
    981981}
    982982
    p.customize-section-description { 
    986986        background-color: #fff;
    987987}
    988988
    989 .control-section-themes .accordion-section-title {
     989#customize-controls .control-section-themes .accordion-section-title {
    990990        margin: 15px 0;
    991991}
    992992
    993 .customize-themes-panel .accordion-section-title {
     993#customize-controls .customize-themes-panel .accordion-section-title {
    994994        margin: 15px -8px;
    995995}
    996996
    997 .control-section-themes .accordion-section-title {
     997#customize-controls .control-section-themes .accordion-section-title,
     998#customize-controls .customize-themes-panel .accordion-section-title {
    998999        padding-right: 100px; /* Space for the button */
    9991000}
    10001001
    1001 .control-section-themes .accordion-section-title span.customize-action,
     1002#customize-controls .control-section-themes .accordion-section-title span.customize-action,
    10021003#customize-controls .customize-section-title span.customize-action {
    10031004        font-size: 13px;
    10041005        display: block;
    10051006        font-weight: 400;
    10061007}
    10071008
    1008 .control-section-themes .accordion-section-title .change-theme,
    1009 .control-section-themes .accordion-section-title .customize-theme {
     1009#customize-controls .control-section-themes .accordion-section-title .change-theme,
     1010#customize-controls .customize-themes-panel .accordion-section-title .customize-theme {
    10101011        position: absolute;
    10111012        right: 10px;
    10121013        top: 50%;
    p.customize-section-description { 
    10141015        font-weight: 400;
    10151016}
    10161017
    1017 .control-section-themes .accordion-section-title:before {
     1018#customize-controls .control-section-themes .accordion-section-title:before {
    10181019        display: none;
    10191020}
    10201021
    1021 .customize-themes-panel {
    1022         display: none;
     1022#customize-controls .customize-themes-panel {
    10231023        padding: 0 8px;
    10241024        background: #f1f1f1;
    10251025        -webkit-box-sizing: border-box;
    p.customize-section-description { 
    10271027        box-sizing: border-box;
    10281028}
    10291029
    1030 .customize-themes-panel .accordion-section-title:first-child {
     1030#customize-controls .customize-themes-panel .accordion-section-title:first-child {
    10311031        margin-top: 0;
    10321032}
    10331033
    p.customize-section-description { 
    10361036        font-weight: 600;
    10371037}
    10381038
    1039 .customize-themes-panel > h2 {
     1039#customize-controls .customize-themes-panel > h2 {
    10401040        padding: 15px 8px 0 8px;
    10411041}
    10421042
    1043 .control-section.open .customize-themes-panel {
    1044         display: block;
    1045 }
    1046 
    10471043#customize-theme-controls .customize-themes-panel .accordion-section-content {
    10481044        background: transparent;
    10491045        display: block;
    p.customize-section-description { 
    10891085        width: 100%;
    10901086}
    10911087
    1092 #accordion-section-themes .accordion-section-title:after {
     1088.control-section-themes .accordion-section-title:after,
     1089.customize-themes-panel .accordion-section-title:after {
    10931090        display: none;
    10941091}
    10951092
    1096 #customize-theme-controls .control-section-themes.current-panel > h3.accordion-section-title {
    1097         left: 0;
    1098 }
    1099 
    11001093.customize-themes-panel.control-panel-content {
    1101         position: absolute;
    1102         left: -100%;
    1103         top: 0;
    1104         width: 100%;
    11051094        border-top: 1px solid #ddd;
    11061095}
    11071096
    1108 .in-themes-panel #customize-info,
    1109 .in-themes-panel #customize-theme-controls > ul > .accordion-section {
    1110         left: 100%;
    1111 }
    1112 
    11131097/* Details View */
    11141098.wp-customizer .theme-overlay {
    11151099        display: none;
    p.customize-section-description { 
    11431127        text-align: right; /* Because there's only one action, match the pattern of media modals and right-align the action. */
    11441128}
    11451129
    1146 .modal-open .in-themes-panel #customize-controls .wp-full-overlay-sidebar-content {
    1147         overflow: visible; /* Prevent the top-level Customizer controls from becoming visible when elements on the right of the details modal are focused. */
     1130.in-themes-panel #customize-controls .wp-full-overlay-sidebar-content {
     1131        transform: none; /* Prevent creating new stacking contexts and containing blocks for themes modal. */
    11481132}
    11491133
    11501134.ie8 .wp-customizer .theme-overlay .theme-header,
  • src/wp-admin/css/customize-nav-menus.css

    diff --git a/src/wp-admin/css/customize-nav-menus.css b/src/wp-admin/css/customize-nav-menus.css
    index 66d2b08..7250da7 100644
    a b  
    377377.reordering .menu-item-depth-10 > .menu-item-bar { margin-right: 150px; }
    378378.reordering .menu-item-depth-11 > .menu-item-bar { margin-right: 165px; }
    379379
    380 .control-section-nav_menu .menu .menu-item-edit-active {
     380.accordion-section-content.menu .menu-item-edit-active {
    381381        margin-left: 0;
    382382}
    383383
    384 .control-section-nav_menu .menu .menu-item-edit-active .menu-item-bar {
     384.accordion-section-content.menu .menu-item-edit-active .menu-item-bar {
    385385        margin-right: 0;
    386386}
    387387
    388 .control-section-nav_menu .menu .sortable-placeholder {
     388.accordion-section-content.menu .sortable-placeholder {
    389389        margin-top: 0;
    390390        margin-bottom: 1px;
    391391        max-width: -webkit-calc(100% - 2px);
     
    399399        float: none;
    400400}
    401401
    402 .control-section-nav_menu .menu ul.menu-item-transport .menu-item-bar {
     402.accordion-section-content.menu ul.menu-item-transport .menu-item-bar {
    403403        margin-top: 0;
    404404}
    405405
  • src/wp-admin/customize.php

    diff --git a/src/wp-admin/customize.php b/src/wp-admin/customize.php
    index bd8e118..6c0b20d 100644
    a b  
    142142                        </div>
    143143
    144144                        <div id="customize-theme-controls">
    145                                 <ul><?php // Panels and sections are managed here via JavaScript ?></ul>
     145                                <ul class="customize-pane-parent"><?php // Panels and sections are managed here via JavaScript ?></ul>
    146146                        </div>
    147147                </div>
    148148                </div>
  • src/wp-admin/js/customize-controls.js

    diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js
    index 7078cfd..712c045 100644
    a b  
    11/* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer */
    22(function( exports, $ ){
    3         var Container, focus, api = wp.customize;
     3        var Container, focus, normalizedTransitionendEventName, api = wp.customize;
    44
    55        /**
    66         * A Customizer Setting.
     
    9696         * @param {Function} [params.completeCallback]
    9797         */
    9898        focus = function ( params ) {
    99                 var construct, completeCallback, focus, focusElement;
    100                 construct = this;
     99                var construct = this,
     100                        completeCallback, focus;
     101
    101102                params = params || {};
    102103                focus = function () {
    103                         var focusContainer;
    104                         if ( construct.extended( api.Panel ) && construct.expanded && construct.expanded() ) {
    105                                 focusContainer = construct.container.find( 'ul.control-panel-content' );
    106                         } else if ( construct.extended( api.Section ) && construct.expanded && construct.expanded() ) {
    107                                 focusContainer = construct.container.find( 'ul.accordion-section-content' );
    108                         } else {
    109                                 focusContainer = construct.container;
    110                         }
     104                        var focusContainer = construct.container,
     105                                focusElement;
    111106
    112                         focusElement = focusContainer.find( '.control-focus:first' );
     107                        if ( ( construct.extended( api.Panel ) || construct.extended( api.Section ) ) && construct.expanded && construct.expanded() ) {
     108                                focusContainer = ( construct.content.is( 'ul' ) ) ? construct.content : construct.content.find( 'ul:first' );
     109                        }
     110                        focusElement = focusContainer.find( '.control-focus, input, select, textarea, button' ).filter( ':visible' ).first();
    113111                        if ( 0 === focusElement.length ) {
    114112                                // Note that we can't use :focusable due to a jQuery UI issue. See: https://github.com/jquery/jquery-ui/pull/1583
    115                                 focusElement = focusContainer.find( 'input, select, textarea, button, object, a[href], [tabindex]' ).filter( ':visible' ).first();
     113                                focusElement = focusContainer.find( 'object, a[href], [tabindex]' ).filter( ':visible' ).first();
    116114                        }
    117115                        focusElement.focus();
    118116                };
     117
    119118                if ( params.completeCallback ) {
    120119                        completeCallback = params.completeCallback;
    121120                        params.completeCallback = function () {
     
    188187        };
    189188
    190189        /**
     190         * Return browser supported `transitionend` event name
     191         *
     192         * @since
     193         *
     194         * @returns {String}
     195         */
     196        normalizedTransitionendEventName = (function () {
     197                var el, transitions, prop;
     198                el = document.createElement( 'div' );
     199                transitions = {
     200                        'transition'      : 'transitionend',
     201                        'OTransition'     : 'oTransitionEnd',
     202                        'MozTransition'   : 'transitionend',
     203                        'WebkitTransition': 'webkitTransitionEnd'
     204                };
     205                prop = _.find( _.keys( transitions ), function( prop ) {
     206                        return ! _.isUndefined( el.style[ prop ] );
     207                } );
     208                if ( prop ) {
     209                        return transitions[ prop ];
     210                } else {
     211                        return null;
     212                }
     213        })();
     214
     215        /**
    191216         * Base class for Panel and Section.
    192217         *
    193218         * @since 4.1.0
     
    238263                        if ( 0 === container.container.length ) {
    239264                                container.container = $( container.getContainer() );
    240265                        }
     266                        container.content = container.getContent();
    241267
    242268                        container.deferred = {
    243269                                embedded: new $.Deferred()
     
    372398                                        construct.container.stop( true, true ).slideUp( duration, args.completeCallback );
    373399                                }
    374400                        }
    375 
    376                         // Recalculate the margin-top immediately, not waiting for debounced reflow, to prevent momentary (100ms) vertical jiggle.
    377                         if ( expandedOtherPanel ) {
    378                                 expandedOtherPanel._recalculateTopMargin();
    379                         }
    380401                },
    381402
    382403                /**
     
    426447                },
    427448
    428449                /**
     450                 * To override by subclass, handle CSS transitions/animations made on the container.
     451                 * @abstract
     452                 */
     453                animateChangeExpanded: function() {},
     454
     455                /**
    429456                 * Handle the toggle logic for expand/collapse.
    430457                 *
    431458                 * @param {Boolean}  expanded - The new state to apply.
     
    507534                        }
    508535
    509536                        return '<li></li>';
     537                },
     538
     539                /**
     540                 * Detach and return the content html, extracted from the container html, if it exists.
     541                 *
     542                 * @since
     543                 */
     544                getContent: function () {
     545                        var container = this,
     546                                list = container.container.find( '.accordion-section-content, .control-panel-content' ).first(),
     547                                contentId = 'sub-accordion-list-' + container.id;
     548
     549                        container.setOwnership( contentId );
     550
     551                        return list.detach().attr( {
     552                                'id': contentId,
     553                                'class': 'customize-pane-child ' + list.attr( 'class' )
     554                        } );
     555                },
     556
     557                /**
     558                 * Add new element to `aria-owned` property of the container.
     559                 *
     560                 * @since
     561                 */
     562                setOwnership: function ( elementId ) {
     563                        var container = this.container,
     564                                ownedElements = container.attr( 'aria-owns' );
     565
     566                        if ( _.isUndefined( ownedElements ) ) {
     567                                container.attr( {
     568                                        'aria-owns': elementId
     569                                } );
     570                        } else {
     571                                container.attr( {
     572                                        'aria-owns': ownedElements + ' ' + elementId
     573                                } );
     574                        }
    510575                }
    511576        });
    512577
     
    569634                 * @since 4.1.0
    570635                 */
    571636                embed: function () {
    572                         var section = this, inject;
     637                        var inject,
     638                                section = this,
     639                                container = $( '#customize-theme-controls' );
    573640
    574641                        // Watch for changes to the panel state
    575642                        inject = function ( panelId ) {
     
    579646                                        api.panel( panelId, function ( panel ) {
    580647                                                // The panel has been registered, wait for it to become ready/initialized
    581648                                                panel.deferred.embedded.done( function () {
    582                                                         parentContainer = panel.container.find( 'ul:first' );
     649                                                        parentContainer = panel.content;
    583650                                                        if ( ! section.container.parent().is( parentContainer ) ) {
    584651                                                                parentContainer.append( section.container );
    585652                                                        }
     653                                                        if ( ! section.content.parent().is( section.container ) ) {
     654                                                                container.append( section.content );
     655                                                        }
    586656                                                        section.deferred.embedded.resolve();
    587657                                                });
    588658                                        } );
    589659                                } else {
    590660                                        // There is no panel, so embed the section in the root of the customizer
    591                                         parentContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
     661                                        parentContainer = $( '.customize-pane-parent' ); // @todo This should be defined elsewhere, and to be configurable
    592662                                        if ( ! section.container.parent().is( parentContainer ) ) {
    593663                                                parentContainer.append( section.container );
    594664                                        }
     665                                        if ( ! section.content.parent().is( section.container ) ) {
     666                                                container.append( section.content );
     667                                        }
    595668                                        section.deferred.embedded.resolve();
    596669                                }
    597670                        };
    598671                        section.panel.bind( inject );
    599672                        inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one
    600 
    601                         section.deferred.embedded.done(function() {
    602                                 // Fix the top margin after reflow.
    603                                 api.bind( 'pane-contents-reflowed', _.debounce( function() {
    604                                         section._recalculateTopMargin();
    605                                 }, 100 ) );
    606                         });
    607673                },
    608674
    609675                /**
     
    612678                 * @since 4.1.0
    613679                 */
    614680                attachEvents: function () {
    615                         var section = this;
     681                        var section = this,
     682                                toggleHandler;
    616683
    617684                        // Expand/Collapse accordion sections on click.
    618                         section.container.find( '.accordion-section-title, .customize-section-back' ).on( 'click keydown', function( event ) {
     685                        toggleHandler = function ( event ) {
    619686                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
    620687                                        return;
    621688                                }
     
    626693                                } else {
    627694                                        section.expand();
    628695                                }
    629                         });
     696                        };
     697
     698                        section.container.find( '.accordion-section-title' ).on( 'click keydown', toggleHandler );
     699                        section.content.find( '.customize-section-back' ).on( 'click keydown', toggleHandler );
    630700                },
    631701
    632702                /**
     
    670740                onChangeExpanded: function ( expanded, args ) {
    671741                        var section = this,
    672742                                container = section.container.closest( '.wp-full-overlay-sidebar-content' ),
    673                                 content = section.container.find( '.accordion-section-content' ),
     743                                content = section.content,
    674744                                overlay = section.container.closest( '.wp-full-overlay' ),
    675                                 backBtn = section.container.find( '.customize-section-back' ),
     745                                backBtn = content.find( '.customize-section-back' ),
    676746                                sectionTitle = section.container.find( '.accordion-section-title' ).first(),
    677                                 headerActionsHeight = $( '#customize-header-actions' ).height(),
    678                                 resizeContentHeight, expand, position, scroll;
     747                                currentScrollTop = container.scrollTop(),
     748                                previousScrollTop, expand, stateChangeCallback;
    679749
    680                         if ( expanded && ! section.container.hasClass( 'open' ) ) {
     750                        if ( expanded && ! content.hasClass( 'open' ) ) {
    681751
    682752                                if ( args.unchanged ) {
    683753                                        expand = args.completeCallback;
    684754                                } else {
    685                                         container.scrollTop( 0 );
    686                                         resizeContentHeight = function() {
    687                                                 var matchMedia, offset;
    688                                                 matchMedia = window.matchMedia || window.msMatchMedia;
    689                                                 offset = 90; // 45px for customize header actions + 45px for footer actions.
    690 
    691                                                 // No footer on small screens.
    692                                                 if ( matchMedia && matchMedia( '(max-width: 640px)' ).matches ) {
    693                                                         offset = 45;
    694                                                 }
    695                                                 content.css( 'height', ( window.innerHeight - offset ) );
    696                                         };
    697                                         expand = function() {
    698                                                 section.container.addClass( 'open' );
    699                                                 overlay.addClass( 'section-open' );
    700                                                 position = content.offset().top;
    701                                                 scroll = container.scrollTop();
    702                                                 content.css( 'margin-top', ( headerActionsHeight - position - scroll ) );
    703                                                 resizeContentHeight();
    704                                                 sectionTitle.attr( 'tabindex', '-1' );
    705                                                 backBtn.attr( 'tabindex', '0' );
    706                                                 backBtn.focus();
    707                                                 if ( args.completeCallback ) {
    708                                                         args.completeCallback();
    709                                                 }
     755                                        expand = $.proxy( function() {
     756                                                stateChangeCallback = function() {
     757                                                        sectionTitle.attr( 'tabindex', '-1' );
     758                                                        backBtn.attr( 'tabindex', '0' );
    710759
    711                                                 // Fix the height after browser resize.
    712                                                 $( window ).on( 'resize.customizer-section', _.debounce( resizeContentHeight, 100 ) );
     760                                                        backBtn.focus();
     761                                                        content.css( 'top', '' );
     762                                                        container.scrollTop( 0 );
    713763
    714                                                 setTimeout( _.bind( section._recalculateTopMargin, section ), 0 );
    715                                         };
     764                                                        if ( args.completeCallback ) {
     765                                                                args.completeCallback();
     766                                                        }
     767                                                };
     768
     769                                                this.animateChangeExpanded( stateChangeCallback );
     770
     771                                                // Prevent ugly screen flicker for items that are 'below the fold'.
     772                                                if ( 0 < currentScrollTop ) {
     773                                                        content.data( 'previous-scrollTop', currentScrollTop );
     774                                                        content.css( 'top', currentScrollTop + 'px' );
     775                                                }
     776
     777                                                content.addClass( 'open' );
     778                                                overlay.addClass( 'section-open' );
     779                                        }, this );
    716780                                }
    717781
    718782                                if ( ! args.allowMultiple ) {
     
    735799                                        expand();
    736800                                }
    737801
    738                         } else if ( ! expanded && section.container.hasClass( 'open' ) ) {
    739                                 section.container.removeClass( 'open' );
     802                        } else if ( ! expanded && content.hasClass( 'open' ) ) {
     803                                stateChangeCallback = function() {
     804                                        backBtn.attr( 'tabindex', '-1' );
     805                                        sectionTitle.attr( 'tabindex', '0' );
     806
     807                                        sectionTitle.focus();
     808                                        content.css( 'top', '' );
     809
     810                                        if ( args.completeCallback ) {
     811                                                args.completeCallback();
     812                                        }
     813                                };
     814
     815                                this.animateChangeExpanded( stateChangeCallback );
     816
     817                                content.removeClass( 'open' );
    740818                                overlay.removeClass( 'section-open' );
    741                                 content.css( 'margin-top', '' );
    742                                 container.scrollTop( 0 );
    743                                 backBtn.attr( 'tabindex', '-1' );
    744                                 sectionTitle.attr( 'tabindex', '0' );
    745                                 sectionTitle.focus();
    746                                 if ( args.completeCallback ) {
    747                                         args.completeCallback();
    748                                 }
    749                                 $( window ).off( 'resize.customizer-section' );
     819
     820                                // Prevent ugly screen flicker for items that are 'below the fold'.
     821                                _.defer( function() {
     822                                        previousScrollTop = content.data( 'previous-scrollTop' ) || 0;
     823                                        if ( 0 < currentScrollTop + previousScrollTop ) {
     824                                                container.scrollTop( previousScrollTop );
     825                                                content.css( 'top', previousScrollTop - currentScrollTop + 'px' );
     826                                        }
     827                                } );
     828
    750829                        } else {
    751830                                if ( args.completeCallback ) {
    752831                                        args.completeCallback();
     
    755834                },
    756835
    757836                /**
    758                  * Recalculate the top margin.
     837                 * Animate pane state change if transitions are supported by the browser.
    759838                 *
    760                  * @since 4.4.0
    761                  * @private
     839                 * @param {function} completeCallback Function to be called after transition is completed.
    762840                 */
    763                 _recalculateTopMargin: function() {
    764                         var section = this, content, offset, headerActionsHeight;
    765                         content = section.container.find( '.accordion-section-content' );
    766                         if ( 0 === content.length ) {
     841                animateChangeExpanded: function( completeCallback ) {
     842                        var section = this,
     843                                content = section.content,
     844                                overlay = content.closest( '.wp-full-overlay' ),
     845                                affectedElements, transitionEndCallback;
     846
     847                        if ( ! normalizedTransitionendEventName ) {
     848                                if ( completeCallback ) {
     849                                        completeCallback();
     850                                }
    767851                                return;
    768852                        }
    769                         headerActionsHeight = $( '#customize-header-actions' ).height();
    770                         offset = ( content.offset().top - headerActionsHeight );
    771                         if ( 0 < offset ) {
    772                                 content.css( 'margin-top', ( parseInt( content.css( 'margin-top' ), 10 ) - offset ) );
     853
     854                        affectedElements = overlay.add( content );
     855                        if ( ! overlay.hasClass( 'in-sub-panel' ) ) {
     856                                affectedElements = affectedElements.add( '#customize-info, .customize-pane-parent' );
    773857                        }
     858
     859                        transitionEndCallback = function( e ) {
     860                                if ( 2 !== e.eventPhase || ! $( e.target ).is( content ) ) {
     861                                        return;
     862                                }
     863
     864                                content.off( normalizedTransitionendEventName, transitionEndCallback );
     865                                affectedElements.removeClass( 'busy' );
     866
     867                                if ( completeCallback ) {
     868                                        completeCallback();
     869                                }
     870                        };
     871
     872                        content.on( normalizedTransitionendEventName, transitionEndCallback );
     873                        affectedElements.addClass( 'busy' );
    774874                }
    775875        });
    776876
     
    804904                 */
    805905                ready: function () {
    806906                        var section = this;
    807                         section.overlay = section.container.find( '.theme-overlay' );
     907                        section.overlay = section.content.find( '.theme-overlay' );
    808908                        section.template = wp.template( 'customize-themes-details-view' );
    809909
    810910                        // Bind global keyboard events.
     
    851951                 * @since 4.2.0
    852952                 */
    853953                attachEvents: function () {
    854                         var section = this;
     954                        var section = this,
     955                                content = section.content,
     956                                toggleHandler;
    855957
    856958                        // Expand/Collapse section/panel.
    857                         section.container.find( '.change-theme, .customize-theme' ).on( 'click keydown', function( event ) {
     959                        toggleHandler = function ( event ) {
    858960                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
    859961                                        return;
    860962                                }
     
    865967                                } else {
    866968                                        section.expand();
    867969                                }
    868                         });
     970                        };
     971
     972                        section.container.find( '.change-theme' ).on( 'click keydown', toggleHandler );
     973                        content.find( '.customize-theme' ).on( 'click keydown', toggleHandler );
    869974
    870975                        // Theme navigation in details view.
    871                         section.container.on( 'click keydown', '.left', function( event ) {
     976                        content.on( 'click keydown', '.left', function( event ) {
    872977                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
    873978                                        return;
    874979                                }
     
    878983                                section.previousTheme();
    879984                        });
    880985
    881                         section.container.on( 'click keydown', '.right', function( event ) {
     986                        content.on( 'click keydown', '.right', function( event ) {
    882987                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
    883988                                        return;
    884989                                }
     
    888993                                section.nextTheme();
    889994                        });
    890995
    891                         section.container.on( 'click keydown', '.theme-backdrop, .close', function( event ) {
     996                        content.on( 'click keydown', '.theme-backdrop, .close', function( event ) {
    892997                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
    893998                                        return;
    894999                                }
     
    8991004                        });
    9001005
    9011006                        var renderScreenshots = _.throttle( _.bind( section.renderScreenshots, this ), 100 );
    902                         section.container.on( 'input', '#themes-filter', function( event ) {
     1007                        content.on( 'input', '#themes-filter', function( event ) {
    9031008                                var count,
    9041009                                        term = event.currentTarget.value.toLowerCase().trim().replace( '-', ' ' ),
    9051010                                        controls = section.controls();
     
    9111016                                renderScreenshots();
    9121017
    9131018                                // Update theme count.
    914                                 count = section.container.find( 'li.customize-control:visible' ).length;
    915                                 section.container.find( '.theme-count' ).text( count );
     1019                                count = content.find( 'li.customize-control:visible' ).length;
     1020                                content.find( '.theme-count' ).text( count );
    9161021                        });
    9171022
    9181023                        // Pre-load the first 3 theme screenshots.
     
    9481053                        }
    9491054
    9501055                        // Note: there is a second argument 'args' passed
    951                         var position, scroll,
    952                                 panel = this,
    953                                 section = panel.container.closest( '.accordion-section' ),
     1056                        var panel = this,
     1057                                section = panel.content,
    9541058                                overlay = section.closest( '.wp-full-overlay' ),
    9551059                                container = section.closest( '.wp-full-overlay-sidebar-content' ),
    956                                 siblings = container.find( '.open' ),
    9571060                                customizeBtn = section.find( '.customize-theme' ),
    958                                 changeBtn = section.find( '.change-theme' ),
    959                                 content = section.find( '.control-panel-content' );
    960 
    961                         if ( expanded ) {
     1061                                changeBtn = panel.container.find( '.change-theme' ),
     1062                                currentScrollTop = container.scrollTop(),
     1063                                previousScrollTop, stateChangeCallback;
    9621064
     1065                        if ( expanded && ! section.hasClass( 'current-panel' ) ) {
    9631066                                // Collapse any sibling sections/panels
    9641067                                api.section.each( function ( otherSection ) {
    9651068                                        if ( otherSection !== panel ) {
     
    9701073                                        otherPanel.collapse( { duration: 0 } );
    9711074                                });
    9721075
    973                                 content.show( 0, function() {
    974                                         position = content.offset().top;
    975                                         scroll = container.scrollTop();
    976                                         content.css( 'margin-top', ( $( '#customize-header-actions' ).height() - position - scroll ) );
    977                                         section.addClass( 'current-panel' );
    978                                         overlay.addClass( 'in-themes-panel' );
     1076                                stateChangeCallback = function() {
     1077                                        changeBtn.attr( 'tabindex', '-1' );
     1078                                        customizeBtn.attr( 'tabindex', '0' );
     1079
     1080                                        customizeBtn.focus();
     1081                                        section.css( 'top', '' );
    9791082                                        container.scrollTop( 0 );
    980                                         _.delay( panel.renderScreenshots, 10 ); // Wait for the controls
    981                                         panel.$customizeSidebar.on( 'scroll.customize-themes-section', _.throttle( panel.renderScreenshots, 300 ) );
     1083
    9821084                                        if ( args.completeCallback ) {
    9831085                                                args.completeCallback();
    9841086                                        }
    985                                 } );
    986                                 customizeBtn.focus();
    987                         } else {
    988                                 siblings.removeClass( 'open' );
    989                                 section.removeClass( 'current-panel' );
    990                                 overlay.removeClass( 'in-themes-panel' );
    991                                 panel.$customizeSidebar.off( 'scroll.customize-themes-section' );
    992                                 content.delay( 180 ).hide( 0, function() {
    993                                         content.css( 'margin-top', 'inherit' ); // Reset
     1087                                };
     1088
     1089                                this.animateChangeExpanded( stateChangeCallback );
     1090
     1091                                // Prevent ugly screen flicker for items that are 'below the fold'.
     1092                                if ( 0 < currentScrollTop ) {
     1093                                        section.data( 'previous-scrollTop', currentScrollTop );
     1094                                        section.css( 'top', currentScrollTop + 'px' );
     1095                                }
     1096
     1097                                overlay.addClass( 'in-themes-panel' );
     1098                                section.addClass( 'current-panel' );
     1099
     1100                        } else if ( ! expanded && section.hasClass( 'current-panel' ) ) {
     1101
     1102                                stateChangeCallback = function() {
     1103                                        changeBtn.attr( 'tabindex', '0' );
     1104                                        customizeBtn.attr( 'tabindex', '-1' );
     1105
     1106                                        changeBtn.focus();
     1107                                        section.css( 'top', '' );
     1108
    9941109                                        if ( args.completeCallback ) {
    9951110                                                args.completeCallback();
    9961111                                        }
     1112                                };
     1113
     1114                                this.animateChangeExpanded( stateChangeCallback );
     1115
     1116                                overlay.removeClass( 'in-themes-panel' );
     1117                                section.removeClass( 'current-panel' );
     1118
     1119                                // Prevent ugly screen flicker for items that are 'below the fold'.
     1120                                _.defer( function() {
     1121                                        previousScrollTop = section.data( 'previous-scrollTop' ) || 0;
     1122                                        if ( 0 < currentScrollTop + previousScrollTop ) {
     1123                                                container.scrollTop( previousScrollTop );
     1124                                                section.css( 'top', previousScrollTop - currentScrollTop + 'px' );
     1125                                        }
    9971126                                } );
    998                                 customizeBtn.attr( 'tabindex', '0' );
    999                                 changeBtn.focus();
    1000                                 container.scrollTop( 0 );
    10011127                        }
    10021128                },
    10031129
     1130
    10041131                /**
    1005                  * Recalculate the top margin.
     1132                 * Animate pane state change if transitions are supported by the browser.
    10061133                 *
    1007                  * @since 4.4.0
    1008                  * @private
     1134                 * @param {function} completeCallback Function to be called after transition is completed.
    10091135                 */
    1010                 _recalculateTopMargin: function() {
    1011                         api.Panel.prototype._recalculateTopMargin.call( this );
     1136                animateChangeExpanded: function( completeCallback ) {
     1137                        var content = this.content,
     1138                                overlay = content.closest( '.wp-full-overlay' ),
     1139                                affectedElements, transitionEndCallback;
     1140
     1141                        if ( ! normalizedTransitionendEventName ) {
     1142                                if ( completeCallback ) {
     1143                                        completeCallback();
     1144                                }
     1145                                return;
     1146                        }
     1147
     1148                        affectedElements = overlay.add( content );
     1149                        affectedElements = affectedElements.add( '#customize-info, .customize-pane-parent' );
     1150
     1151                        transitionEndCallback = function( e ) {
     1152                                if ( 2 !== e.eventPhase || ! $( e.target ).is( content ) ) {
     1153                                        return;
     1154                                }
     1155
     1156                                content.off( normalizedTransitionendEventName, transitionEndCallback );
     1157                                affectedElements.removeClass( 'busy' );
     1158
     1159                                if ( completeCallback ) {
     1160                                        completeCallback();
     1161                                }
     1162                        };
     1163
     1164                        content.on( normalizedTransitionendEventName, transitionEndCallback );
     1165                        affectedElements.addClass( 'busy' );
    10121166                },
    10131167
    10141168                /**
     
    12371391                 */
    12381392                embed: function () {
    12391393                        var panel = this,
    1240                                 parentContainer = $( '#customize-theme-controls > ul' ); // @todo This should be defined elsewhere, and to be configurable
     1394                                container = $( '#customize-theme-controls' ),
     1395                                parentContainer = $( '.customize-pane-parent' ); // @todo This should be defined elsewhere, and to be configurable
    12411396
    12421397                        if ( ! panel.container.parent().is( parentContainer ) ) {
    12431398                                parentContainer.append( panel.container );
     1399                        }
     1400                        if ( ! panel.content.parent().is( panel.container ) ) {
     1401                                container.append( panel.content );
    12441402                                panel.renderContent();
    12451403                        }
    12461404
    1247                         api.bind( 'pane-contents-reflowed', _.debounce( function() {
    1248                                 panel._recalculateTopMargin();
    1249                         }, 100 ) );
    1250 
    12511405                        panel.deferred.embedded.resolve();
    12521406                },
    12531407
     
    12701424                        });
    12711425
    12721426                        // Close panel.
    1273                         panel.container.find( '.customize-panel-back' ).on( 'click keydown', function( event ) {
     1427                        panel.content.find( '.customize-panel-back' ).on( 'click keydown', function( event ) {
    12741428                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
    12751429                                        return;
    12761430                                }
     
    12811435                                }
    12821436                        });
    12831437
    1284                         meta = panel.container.find( '.panel-meta:first' );
     1438                        meta = panel.content.find( '.panel-meta:first' );
    12851439
    12861440                        meta.find( '> .accordion-section-title .customize-help-toggle' ).on( 'click keydown', function( event ) {
    12871441                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     
    12891443                                }
    12901444                                event.preventDefault(); // Keep this AFTER the key filter above
    12911445
    1292                                 meta = panel.container.find( '.panel-meta' );
    12931446                                if ( meta.hasClass( 'cannot-expand' ) ) {
    12941447                                        return;
    12951448                                }
     
    13051458                                        $( this ).attr( 'aria-expanded', true );
    13061459                                }
    13071460                        });
    1308 
    13091461                },
    13101462
    13111463                /**
     
    13591511                        }
    13601512
    13611513                        // Note: there is a second argument 'args' passed
    1362                         var position, scroll,
    1363                                 panel = this,
    1364                                 accordionSection = panel.container.closest( '.accordion-section' ),
     1514                        var panel = this,
     1515                                accordionSection = panel.content,
    13651516                                overlay = accordionSection.closest( '.wp-full-overlay' ),
    13661517                                container = accordionSection.closest( '.wp-full-overlay-sidebar-content' ),
    1367                                 siblings = container.find( '.open' ),
    1368                                 topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ),
     1518                                topPanel = panel.container.find( '.accordion-section-title' ),
    13691519                                backBtn = accordionSection.find( '.customize-panel-back' ),
    1370                                 panelTitle = accordionSection.find( '.accordion-section-title' ).first(),
    1371                                 content = accordionSection.find( '.control-panel-content' ),
    1372                                 headerActionsHeight = $( '#customize-header-actions' ).height();
    1373 
    1374                         if ( expanded ) {
     1520                                currentScrollTop = container.scrollTop(),
     1521                                previousScrollTop, stateChangeCallback;
    13751522
     1523                        if ( expanded && ! accordionSection.hasClass( 'current-panel' ) ) {
    13761524                                // Collapse any sibling sections/panels
    13771525                                api.section.each( function ( section ) {
    13781526                                        if ( panel.id !== section.panel() ) {
     
    13851533                                        }
    13861534                                });
    13871535
    1388                                 content.show( 0, function() {
    1389                                         content.parent().show();
    1390                                         position = content.offset().top;
    1391                                         scroll = container.scrollTop();
    1392                                         content.css( 'margin-top', ( headerActionsHeight - position - scroll ) );
    1393                                         accordionSection.addClass( 'current-panel' );
    1394                                         overlay.addClass( 'in-sub-panel' );
     1536                                stateChangeCallback = function() {
     1537                                        topPanel.attr( 'tabindex', '-1' );
     1538                                        backBtn.attr( 'tabindex', '0' );
     1539
     1540                                        backBtn.focus();
     1541                                        accordionSection.css( 'top', '' );
    13951542                                        container.scrollTop( 0 );
     1543
    13961544                                        if ( args.completeCallback ) {
    13971545                                                args.completeCallback();
    13981546                                        }
    1399                                 } );
    1400                                 topPanel.attr( 'tabindex', '-1' );
    1401                                 backBtn.attr( 'tabindex', '0' );
    1402                                 backBtn.focus();
    1403                                 panel._recalculateTopMargin();
    1404                         } else {
    1405                                 siblings.removeClass( 'open' );
    1406                                 accordionSection.removeClass( 'current-panel' );
    1407                                 overlay.removeClass( 'in-sub-panel' );
    1408                                 content.delay( 180 ).hide( 0, function() {
    1409                                         content.css( 'margin-top', 'inherit' ); // Reset
     1547                                };
     1548
     1549                                this.animateChangeExpanded( stateChangeCallback );
     1550
     1551                                // Prevent ugly screen flicker for items that are 'below the fold'.
     1552                                if ( 0 < currentScrollTop ) {
     1553                                        accordionSection.data( 'previous-scrollTop', currentScrollTop );
     1554                                        accordionSection.css( 'top', currentScrollTop + 'px' );
     1555                                }
     1556
     1557                                overlay.addClass( 'in-sub-panel' );
     1558                                accordionSection.addClass( 'current-panel' );
     1559
     1560                        } else if ( ! expanded && accordionSection.hasClass( 'current-panel' ) ) {
     1561                                stateChangeCallback = function() {
     1562                                        topPanel.attr( 'tabindex', '0' );
     1563                                        backBtn.attr( 'tabindex', '-1' );
     1564
     1565                                        topPanel.focus();
     1566                                        accordionSection.css( 'top', '' );
     1567
    14101568                                        if ( args.completeCallback ) {
    14111569                                                args.completeCallback();
    14121570                                        }
     1571                                };
     1572
     1573                                this.animateChangeExpanded( stateChangeCallback );
     1574
     1575                                // Prevent ugly screen flicker for items that are 'below the fold'.
     1576                                _.defer( function() {
     1577                                        previousScrollTop = accordionSection.data( 'previous-scrollTop' ) || 0;
     1578                                        if ( 0 < currentScrollTop + previousScrollTop ) {
     1579                                                accordionSection.css( 'top', previousScrollTop - currentScrollTop + 'px' );
     1580                                                container.scrollTop( previousScrollTop );
     1581                                        }
    14131582                                } );
    1414                                 topPanel.attr( 'tabindex', '0' );
    1415                                 backBtn.attr( 'tabindex', '-1' );
    1416                                 panelTitle.focus();
    1417                                 container.scrollTop( 0 );
     1583
     1584                                overlay.removeClass( 'in-sub-panel' );
     1585                                accordionSection.removeClass( 'current-panel' );
    14181586                        }
    14191587                },
    14201588
    14211589                /**
    1422                  * Recalculate the top margin.
     1590                 * Animate pane state change if transitions are supported by the browser.
    14231591                 *
    1424                  * @since 4.4.0
    1425                  * @private
     1592                 * @param {function} completeCallback Function to be called after transition is completed.
    14261593                 */
    1427                 _recalculateTopMargin: function() {
    1428                         var panel = this, headerActionsHeight, content, accordionSection;
    1429                         headerActionsHeight = $( '#customize-header-actions' ).height();
    1430                         accordionSection = panel.container.closest( '.accordion-section' );
    1431                         content = accordionSection.find( '.control-panel-content' );
    1432                         content.css( 'margin-top', ( parseInt( content.css( 'margin-top' ), 10 ) - ( content.offset().top - headerActionsHeight ) ) );
     1594                animateChangeExpanded: function( completeCallback ) {
     1595                        var panel = this,
     1596                                content = panel.content,
     1597                                overlay = content.closest( '.wp-full-overlay' ),
     1598                                affectedElements, transitionEndCallback;
     1599
     1600                        if ( ! normalizedTransitionendEventName ) {
     1601                                if ( completeCallback ) {
     1602                                        completeCallback();
     1603                                }
     1604                                return;
     1605                        }
     1606
     1607                        affectedElements = overlay.add( content );
     1608                        affectedElements = affectedElements.add( '#customize-info, .customize-pane-parent' );
     1609
     1610                        transitionEndCallback = function( e ) {
     1611                                if ( 2 !== e.eventPhase || ! $( e.target ).is( content ) ) {
     1612                                        return;
     1613                                }
     1614
     1615                                content.off( normalizedTransitionendEventName, transitionEndCallback );
     1616                                affectedElements.removeClass( 'busy' );
     1617
     1618                                if ( completeCallback ) {
     1619                                        completeCallback();
     1620                                }
     1621                        };
     1622
     1623                        content.on( normalizedTransitionendEventName, transitionEndCallback );
     1624                        affectedElements.addClass( 'busy' );
    14331625                },
    14341626
    14351627                /**
     
    14501642                                template = wp.template( 'customize-panel-default-content' );
    14511643                        }
    14521644                        if ( template && panel.container ) {
    1453                                 panel.container.find( '.accordion-sub-container' ).html( template( panel.params ) );
     1645                                panel.content.html( template( panel.params ) );
    14541646                        }
    14551647                }
    14561648        });
     
    16261818                                api.section( sectionId, function ( section ) {
    16271819                                        // Wait for the section to be ready/initialized
    16281820                                        section.deferred.embedded.done( function () {
    1629                                                 parentContainer = section.container.find( 'ul:first' );
     1821                                                parentContainer = ( section.content.is( 'ul' ) ) ? section.content : section.content.find( 'ul:first' );
    16301822                                                if ( ! control.container.parent().is( parentContainer ) ) {
    16311823                                                        parentContainer.append( control.container );
    16321824                                                        control.renderContent();
     
    38964088                        });
    38974089                });
    38984090
     4091                /**
     4092                 * Sort panels, sections, controls by priorities. Hide empty sections and panels.
     4093                 *
     4094                 * @since 4.1.0
     4095                 */
     4096                api.reflowPaneContents = _.bind( function () {
     4097
     4098                        var appendContainer, activeElement, rootContainers, rootNodes = [], wasReflowed = false;
     4099
     4100                        if ( document.activeElement ) {
     4101                                activeElement = $( document.activeElement );
     4102                        }
     4103
     4104                        // Sort the sections within each panel
     4105                        api.panel.each( function ( panel ) {
     4106                                var sections = panel.sections(),
     4107                                        sectionContainers = _.pluck( sections, 'container' );
     4108                                rootNodes.push( panel );
     4109                                appendContainer = ( panel.content.is( 'ul' ) ) ? panel.content : panel.content.find( 'ul:first' );
     4110                                if ( ! api.utils.areElementListsEqual( sectionContainers, appendContainer.children( '[id]' ) ) ) {
     4111                                        _( sections ).each( function ( section ) {
     4112                                                appendContainer.append( section.container );
     4113                                        } );
     4114                                        wasReflowed = true;
     4115                                }
     4116                        } );
     4117
     4118                        // Sort the controls within each section
     4119                        api.section.each( function ( section ) {
     4120                                var controls = section.controls(),
     4121                                        controlContainers = _.pluck( controls, 'container' );
     4122                                if ( ! section.panel() ) {
     4123                                        rootNodes.push( section );
     4124                                }
     4125                                appendContainer = ( section.content.is( 'ul' ) ) ? section.content : section.content.find( 'ul:first' );
     4126                                if ( ! api.utils.areElementListsEqual( controlContainers, appendContainer.children( '[id]' ) ) ) {
     4127                                        _( controls ).each( function ( control ) {
     4128                                                appendContainer.append( control.container );
     4129                                        } );
     4130                                        wasReflowed = true;
     4131                                }
     4132                        } );
     4133
     4134                        // Sort the root panels and sections
     4135                        rootNodes.sort( api.utils.prioritySort );
     4136                        rootContainers = _.pluck( rootNodes, 'container' );
     4137                        appendContainer = $( '.customize-pane-parent' ); //$( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
     4138                        if ( ! api.utils.areElementListsEqual( rootContainers, appendContainer.children() ) ) {
     4139                                _( rootNodes ).each( function ( rootNode ) {
     4140                                        appendContainer.append( rootNode.container );
     4141                                } );
     4142                                wasReflowed = true;
     4143                        }
     4144
     4145                        // Now re-trigger the active Value callbacks to that the panels and sections can decide whether they can be rendered
     4146                        api.panel.each( function ( panel ) {
     4147                                var value = panel.active();
     4148                                panel.active.callbacks.fireWith( panel.active, [ value, value ] );
     4149                        } );
     4150                        api.section.each( function ( section ) {
     4151                                var value = section.active();
     4152                                section.active.callbacks.fireWith( section.active, [ value, value ] );
     4153                        } );
     4154
     4155                        // Restore focus if there was a reflow and there was an active (focused) element
     4156                        if ( wasReflowed && activeElement ) {
     4157                                activeElement.focus();
     4158                        }
     4159                        api.trigger( 'pane-contents-reflowed' );
     4160                }, api );
     4161
    38994162                api.bind( 'ready', api.reflowPaneContents );
    39004163                $( [ api.panel, api.section, api.control ] ).each( function ( i, values ) {
    39014164                        var debouncedReflowPaneContents = _.debounce( api.reflowPaneContents, 100 );
  • src/wp-admin/js/customize-nav-menus.js

    diff --git a/src/wp-admin/js/customize-nav-menus.js b/src/wp-admin/js/customize-nav-menus.js
    index e514a0a..fbfc63a 100644
    a b  
    655655                        api.Panel.prototype.attachEvents.call( this );
    656656
    657657                        var panel = this,
    658                                 panelMeta = panel.container.find( '.panel-meta' ),
     658                                panelMeta = panel.content.find( '.panel-meta' ),
    659659                                help = panelMeta.find( '.customize-help-toggle' ),
    660660                                content = panelMeta.find( '.customize-panel-description' ),
    661661                                options = $( '#screen-options-wrap' ),
     
    834834
    835835                        api.bind( 'pane-contents-reflowed', function() {
    836836                                // Skip menus that have been removed.
    837                                 if ( ! section.container.parent().length ) {
     837                                if ( ! section.content.parent().length ) {
    838838                                        return;
    839839                                }
    840                                 section.container.find( '.menu-item .menu-item-reorder-nav button' ).attr({ 'tabindex': '0', 'aria-hidden': 'false' });
    841                                 section.container.find( '.menu-item.move-up-disabled .menus-move-up' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
    842                                 section.container.find( '.menu-item.move-down-disabled .menus-move-down' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
    843                                 section.container.find( '.menu-item.move-left-disabled .menus-move-left' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
    844                                 section.container.find( '.menu-item.move-right-disabled .menus-move-right' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
     840                                section.content.find( '.menu-item .menu-item-reorder-nav button' ).attr({ 'tabindex': '0', 'aria-hidden': 'false' });
     841                                section.content.find( '.menu-item.move-up-disabled .menus-move-up' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
     842                                section.content.find( '.menu-item.move-down-disabled .menus-move-down' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
     843                                section.content.find( '.menu-item.move-left-disabled .menus-move-left' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
     844                                section.content.find( '.menu-item.move-right-disabled .menus-move-right' ).attr({ 'tabindex': '-1', 'aria-hidden': 'true' });
    845845                        } );
    846846                },
    847847
     
    952952                        var section = this;
    953953
    954954                        if ( expanded ) {
    955                                 wpNavMenu.menuList = section.container.find( '.accordion-section-content:first' );
     955                                wpNavMenu.menuList = section.content;
    956956                                wpNavMenu.targetList = wpNavMenu.menuList;
    957957
    958958                                // Add attributes needed by wpNavMenu
     
    10141014                onChangeExpanded: function( expanded ) {
    10151015                        var section = this,
    10161016                                button = section.container.find( '.add-menu-toggle' ),
    1017                                 content = section.container.find( '.new-menu-section-content' ),
     1017                                content = section.content,
    10181018                                customizer = section.container.closest( '.wp-full-overlay-sidebar-content' );
    10191019                        if ( expanded ) {
    10201020                                button.addClass( 'open' );
     
    10281028                                content.slideUp( 'fast' );
    10291029                                content.find( '.menu-name-field' ).removeClass( 'invalid' );
    10301030                        }
     1031                },
     1032
     1033                /**
     1034                 * Detach and return the content html, extracted from the container html, if it exists.
     1035                 *
     1036                 * @since
     1037                 */
     1038                getContent: function () {
     1039                        return this.container.find( 'ul:first' );
    10311040                }
    10321041        });
    10331042
     
    19841993                 */
    19851994                ready: function() {
    19861995                        var control = this,
     1996                                section = api.section( control.section() ),
    19871997                                menuId = control.params.menu_id,
    19881998                                menu = control.setting(),
    19891999                                name,
     
    20002010                         * being deactivated.
    20012011                         */
    20022012                        control.active.validate = function() {
    2003                                 var value, section = api.section( control.section() );
     2013                                var value;
    20042014                                if ( section ) {
    20052015                                        value = section.active();
    20062016                                } else {
     
    20092019                                return value;
    20102020                        };
    20112021
    2012                         control.$controlSection = control.container.closest( '.control-section' );
     2022                        control.$controlSection = section.container;
    20132023                        control.$sectionContent = control.container.closest( '.accordion-section-content' );
    20142024
    20152025                        this._setupModel();
     
    21922202                        section = api.section( control.section() );
    21932203                        removeSection = function() {
    21942204                                section.container.remove();
     2205                                section.content.remove();
    21952206                                api.section.remove( section.id );
    21962207                        };
    21972208
     
    22832294                                        return;
    22842295                                }
    22852296
    2286                                 var section = control.container.closest( '.accordion-section' ),
     2297                                var section = api.section( control.section() ),
    22872298                                        menuId = control.params.menu_id,
    2288                                         controlTitle = section.find( '.accordion-section-title' ),
    2289                                         sectionTitle = section.find( '.customize-section-title h3' ),
    2290                                         location = section.find( '.menu-in-location' ),
     2299                                        controlTitle = section.container.find( '.accordion-section-title' ),
     2300                                        sectionTitle = section.content.find( '.customize-section-title h3' ),
     2301                                        location = section.container.find( '.menu-in-location' ),
    22912302                                        action = sectionTitle.find( '.customize-action' ),
    22922303                                        name = displayNavMenuName( menu.name );
    22932304
     
    23112322                                } );
    23122323
    23132324                                // Update the nav menu name in all location checkboxes.
    2314                                 section.find( '.customize-control-checkbox input' ).each( function() {
     2325                                section.content.find( '.customize-control-checkbox input' ).each( function() {
    23152326                                        if ( $( this ).prop( 'checked' ) ) {
    23162327                                                $( '.current-menu-location-name-' + $( this ).data( 'location-id' ) ).text( name );
    23172328                                        }
     
    26252636
    26262637                        // Focus on the new menu section.
    26272638                        api.section( customizeId ).focus(); // @todo should we focus on the new menu's control and open the add-items panel? Thinking user flow...
    2628 
    2629                         // Fix an issue with extra space at top immediately after creating new menu.
    2630                         $( '#menu-to-edit' ).css( 'margin-top', 0 );
    26312639                }
    26322640        });
    26332641
  • src/wp-admin/js/customize-widgets.js

    diff --git a/src/wp-admin/js/customize-widgets.js b/src/wp-admin/js/customize-widgets.js
    index 7598023..51b0672 100644
    a b  
    15291529
    15301530                        panel.deferred.embedded.done(function() {
    15311531                                var panelMetaContainer, noRenderedAreasNotice, shouldShowNotice;
    1532                                 panelMetaContainer = panel.container.find( '.panel-meta' );
     1532                                panelMetaContainer = panel.content.find( '.panel-meta' );
    15331533                                noRenderedAreasNotice = $( '<div></div>', {
    15341534                                        'class': 'no-widget-areas-rendered-notice'
    15351535                                });
  • tests/qunit/wp-admin/js/customize-controls.js

    diff --git a/tests/qunit/wp-admin/js/customize-controls.js b/tests/qunit/wp-admin/js/customize-controls.js
    index d806e46..10c7787 100644
    a b jQuery( window ).load( function (){ 
    211211                section = wp.customize.section( id );
    212212                ok( ! section.params.content );
    213213                ok( !! section.container );
     214                ok( !! section.content );
    214215                ok( section.container.is( '.control-section.control-section-default' ) );
    215216                ok( 1 === section.container.find( '> .accordion-section-title' ).length );
    216                 ok( 1 === section.container.find( '> .accordion-section-content' ).length );
     217                ok( section.content.is( '.accordion-section-content' ) );
     218                equal( section.container.attr( 'aria-owns' ), section.content.attr( 'id' ) );
    217219        } );
    218220
    219221        module( 'Customizer Custom Type (titleless) Section with Template in Fixture' );
    jQuery( window ).load( function (){ 
    225227                section = wp.customize.section( id );
    226228                ok( ! section.params.content );
    227229                ok( !! section.container );
     230                ok( !! section.content );
    228231                ok( section.container.is( '.control-section.control-section-titleless' ) );
    229232                ok( 0 === section.container.find( '> .accordion-section-title' ).length );
    230                 ok( 1 === section.container.find( '> .accordion-section-content' ).length );
     233                ok( section.content.is( '.accordion-section-content' ) );
     234                equal( section.container.attr( 'aria-owns' ), section.content.attr( 'id' ) );
    231235        } );
    232236        module( 'Customizer Custom Type Section Lacking Specific Template' );
    233237        test( 'Fixture section has expected content', function () {
    jQuery( window ).load( function (){ 
    235239                section = wp.customize.section( id );
    236240                ok( ! section.params.content );
    237241                ok( !! section.container );
     242                ok( !! section.content );
    238243                ok( section.container.is( '.control-section.control-section-' + section.params.type ) );
    239244                ok( 1 === section.container.find( '> .accordion-section-title' ).length );
    240                 ok( 1 === section.container.find( '> .accordion-section-content' ).length );
     245                ok( section.content.is( '.accordion-section-content' ) );
     246                equal( section.container.attr( 'aria-owns' ), section.content.attr( 'id' ) );
    241247        } );
    242248        module( 'Customizer Section lacking any params' );
    243249        test( 'Fixture section has default params supplied', function () {
    jQuery( window ).load( function (){ 
    270276                var panel = wp.customize.panel( 'fixture-panel' );
    271277                ok( !! panel.params.content );
    272278                ok( !! panel.container );
     279                ok( !! panel.content );
    273280        } );
    274281        test( 'Fixture panel has section among its sections()', function () {
    275282                var panel = wp.customize.panel( 'fixture-panel' );
    jQuery( window ).load( function (){ 
    304311                panel = wp.customize.panel( id );
    305312                ok( ! panel.params.content );
    306313                ok( !! panel.container );
     314                ok( !! panel.content );
    307315                ok( panel.container.is( '.control-panel.control-panel-default' ) );
    308316                ok( 1 === panel.container.find( '> .accordion-section-title' ).length );
    309                 ok( 1 === panel.container.find( '> .control-panel-content' ).length );
     317                ok( panel.content.is( '.control-panel-content' ) );
     318                equal( panel.container.attr( 'aria-owns' ), panel.content.attr( 'id' ) );
    310319        } );
    311320
    312321        module( 'Customizer Custom Type Panel (titleless) with Template in Fixture' );
    jQuery( window ).load( function (){ 
    318327                panel = wp.customize.panel( id );
    319328                ok( ! panel.params.content );
    320329                ok( !! panel.container );
     330                ok( !! panel.content );
    321331                ok( panel.container.is( '.control-panel.control-panel-titleless' ) );
    322332                ok( 0 === panel.container.find( '> .accordion-section-title' ).length );
    323                 ok( 1 === panel.container.find( '> .control-panel-content' ).length );
     333                ok( panel.content.is( '.control-panel-content' ) );
     334                equal( panel.container.attr( 'aria-owns' ), panel.content.attr( 'id' ) );
    324335        } );
    325336
    326337        module( 'Customizer Custom Type Panel Lacking Specific Template' );
    jQuery( window ).load( function (){ 
    329340                panel = wp.customize.panel( id );
    330341                ok( ! panel.params.content );
    331342                ok( !! panel.container );
     343                ok( !! panel.content );
    332344                ok( panel.container.is( '.control-panel.control-panel-' + panel.params.type ) );
    333345                ok( 1 === panel.container.find( '> .accordion-section-title' ).length );
    334                 ok( 1 === panel.container.find( '> .control-panel-content' ).length );
     346                ok( panel.content.is( '.control-panel-content' ) );
     347                equal( panel.container.attr( 'aria-owns' ), panel.content.attr( 'id' ) );
    335348        } );
    336349        module( 'Customizer Panel lacking any params' );
    337350        test( 'Fixture panel has default params supplied', function () {
  • tests/qunit/wp-admin/js/customize-widgets.js

    diff --git a/tests/qunit/wp-admin/js/customize-widgets.js b/tests/qunit/wp-admin/js/customize-widgets.js
    index 225d155..bed93d1 100644
    a b jQuery( window ).load( function() { 
    5858                ok( panel.extended( api.Widgets.WidgetsPanel ) );
    5959
    6060                panel.deferred.embedded.done( function() {
    61                         ok( 1 === panel.container.find( '.no-widget-areas-rendered-notice' ).length );
    62                         ok( panel.container.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
     61                        ok( 1 === panel.content.find( '.no-widget-areas-rendered-notice' ).length );
     62                        ok( panel.content.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
    6363                        api.section( 'sidebar-widgets-sidebar-1' ).active( true );
    6464                        api.control( 'sidebars_widgets[sidebar-1]' ).active( true );
    6565                        api.trigger( 'pane-contents-reflowed' );
    66                         ok( ! panel.container.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
     66                        ok( ! panel.content.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
    6767                } );
    6868
    6969                expect( 4 );