<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[30102] trunk: Improve/introduce Customizer JavaScript models for Controls, Sections, and Panels.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/30102">30102</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/30102","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>ocean90</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2014-10-29 22:50:21 +0000 (Wed, 29 Oct 2014)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Improve/introduce Customizer JavaScript models for Controls, Sections, and Panels.

* Introduce models for panels and sections.
* Introduce API to expand and focus a control, section or panel.
* Allow deep-linking to panels, sections, and controls inside of the Customizer.
* Clean up `accordion.js`, removing all Customizer-specific logic.
* Add initial unit tests for `wp.customize.Class` in `customize-base.js`.

https://make.wordpress.org/core/2014/10/27/toward-a-complete-javascript-api-for-the-customizer/ provides an overview of how to use the JavaScript API.

props westonruter, celloexpressions, ryankienstra.
see <a href="https://core.trac.wordpress.org/ticket/28032">#28032</a>, <a href="https://core.trac.wordpress.org/ticket/28579">#28579</a>, <a href="https://core.trac.wordpress.org/ticket/28580">#28580</a>, <a href="https://core.trac.wordpress.org/ticket/28650">#28650</a>, <a href="https://core.trac.wordpress.org/ticket/28709">#28709</a>, <a href="https://core.trac.wordpress.org/ticket/29758">#29758</a>.
fixes <a href="https://core.trac.wordpress.org/ticket/29529">#29529</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincustomizephp">trunk/src/wp-admin/customize.php</a></li>
<li><a href="#trunksrcwpadminjsaccordionjs">trunk/src/wp-admin/js/accordion.js</a></li>
<li><a href="#trunksrcwpadminjscustomizecontrolsjs">trunk/src/wp-admin/js/customize-controls.js</a></li>
<li><a href="#trunksrcwpadminjscustomizewidgetsjs">trunk/src/wp-admin/js/customize-widgets.js</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizecontrolphp">trunk/src/wp-includes/class-wp-customize-control.php</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizemanagerphp">trunk/src/wp-includes/class-wp-customize-manager.php</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizepanelphp">trunk/src/wp-includes/class-wp-customize-panel.php</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizesectionphp">trunk/src/wp-includes/class-wp-customize-section.php</a></li>
<li><a href="#trunksrcwpincludesjscustomizebasejs">trunk/src/wp-includes/js/customize-base.js</a></li>
<li><a href="#trunksrcwpincludesjscustomizepreviewjs">trunk/src/wp-includes/js/customize-preview.js</a></li>
<li><a href="#trunktestsqunitindexhtml">trunk/tests/qunit/index.html</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsqunitwpadminjscustomizebasejs">trunk/tests/qunit/wp-admin/js/customize-base.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmincustomizephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/customize.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/customize.php  2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-admin/customize.php    2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -53,8 +53,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_script( 'customize-controls' );
</span><span class="cx" style="display: block; padding: 0 10px"> wp_enqueue_style( 'customize-controls' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-wp_enqueue_script( 'accordion' );
-
</del><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Enqueue Customizer control scripts.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -130,7 +128,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                <div id="widgets-right"><!-- For Widget Customizer, many widgets try to look for instances under div#widgets-right, so we have to add that ID to a container div in the Customizer for compat -->
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <div class="wp-full-overlay-sidebar-content accordion-container" tabindex="-1">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <div class="wp-full-overlay-sidebar-content" tabindex="-1">
</ins><span class="cx" style="display: block; padding: 0 10px">                         <div id="customize-info" class="accordion-section <?php if ( $cannot_expand ) echo ' cannot-expand'; ?>">
</span><span class="cx" style="display: block; padding: 0 10px">                                <div class="accordion-section-title" aria-label="<?php esc_attr_e( 'Customizer Options' ); ?>" tabindex="0">
</span><span class="cx" style="display: block; padding: 0 10px">                                        <span class="preview-notice"><?php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -160,13 +158,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <div id="customize-theme-controls"><ul>
-                               <?php
-                               foreach ( $wp_customize->containers() as $container ) {
-                                       $container->maybe_render();
-                               }
-                               ?>
-                       </ul></div>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <div id="customize-theme-controls">
+                               <ul><?php // Panels and sections are managed here via JavaScript ?></ul>
+                       </div>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">                </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -252,10 +246,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><span class="cx" style="display: block; padding: 0 10px">                'settings' => array(),
</span><span class="cx" style="display: block; padding: 0 10px">                'controls' => array(),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'panels'   => array(),
+               'sections' => array(),
</ins><span class="cx" style="display: block; padding: 0 10px">                 'nonce'    => array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'save'    => wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() ),
</span><span class="cx" style="display: block; padding: 0 10px">                        'preview' => wp_create_nonce( 'preview-customize_' . $wp_customize->get_stylesheet() )
</span><span class="cx" style="display: block; padding: 0 10px">                ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'autofocus' => array(),
</ins><span class="cx" style="display: block; padding: 0 10px">         );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // Prepare Customize Setting objects to pass to Javascript.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -266,12 +263,34 @@
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        // Prepare Customize Control objects to pass to Javascript.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Prepare Customize Control objects to pass to JavaScript.
</ins><span class="cx" style="display: block; padding: 0 10px">         foreach ( $wp_customize->controls() as $id => $control ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $control->to_json();
-               $settings['controls'][ $id ] = $control->json;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $settings['controls'][ $id ] = $control->json();
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        // Prepare Customize Section objects to pass to JavaScript.
+       foreach ( $wp_customize->sections() as $id => $section ) {
+               $settings['sections'][ $id ] = $section->json();
+       }
+
+       // Prepare Customize Panel objects to pass to JavaScript.
+       foreach ( $wp_customize->panels() as $id => $panel ) {
+               $settings['panels'][ $id ] = $panel->json();
+               foreach ( $panel->sections as $section_id => $section ) {
+                       $settings['sections'][ $section_id ] = $section->json();
+               }
+       }
+
+       // Pass to frontend the Customizer construct being deeplinked
+       if ( isset( $_GET['autofocus'] ) && is_array( $_GET['autofocus'] ) ) {
+               $autofocus = wp_unslash( $_GET['autofocus'] );
+               foreach ( $autofocus as $type => $id ) {
+                       if ( isset( $settings[ $type . 's' ][ $id ] ) ) {
+                               $settings['autofocus'][ $type ] = $id;
+                       }
+               }
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         ?>
</span><span class="cx" style="display: block; padding: 0 10px">        <script type="text/javascript">
</span><span class="cx" style="display: block; padding: 0 10px">                var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
</span></span></pre></div>
<a id="trunksrcwpadminjsaccordionjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/accordion.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/accordion.js        2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-admin/js/accordion.js  2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -25,9 +25,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="cx" style="display: block; padding: 0 10px">  * Note that any appropriate tags may be used, as long as the above classes are present.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * In addition to the standard accordion behavior, this file includes JS for the
- * Customizer's "Panel" functionality.
- *
</del><span class="cx" style="display: block; padding: 0 10px">  * @since 3.6.0.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -46,20 +43,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        accordionSwitch( $( this ) );
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Go back to the top-level Customizer accordion.
-               $( '#customize-header-actions' ).on( 'click keydown', '.control-panel-back', function( e ) {
-                       if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key
-                               return;
-                       }
-
-                       e.preventDefault(); // Keep this AFTER the key filter above
-
-                       panelSwitch( $( '.current-panel' ) );
-               });
</del><span class="cx" style="display: block; padding: 0 10px">         });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        var sectionContent = $( '.accordion-section-content' );
-
</del><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * Close the current accordion section and open a new one.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -69,75 +54,22 @@
</span><span class="cx" style="display: block; padding: 0 10px">        function accordionSwitch ( el ) {
</span><span class="cx" style="display: block; padding: 0 10px">                var section = el.closest( '.accordion-section' ),
</span><span class="cx" style="display: block; padding: 0 10px">                        siblings = section.closest( '.accordion-container' ).find( '.open' ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        content = section.find( sectionContent );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 content = section.find( '.accordion-section-content' );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // This section has no content and cannot be expanded.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( section.hasClass( 'cannot-expand' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Slide into a sub-panel instead of accordioning (Customizer-specific).
-               if ( section.hasClass( 'control-panel' ) ) {
-                       panelSwitch( section );
-                       return;
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 if ( section.hasClass( 'open' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        section.toggleClass( 'open' );
</span><span class="cx" style="display: block; padding: 0 10px">                        content.toggle( true ).slideToggle( 150 );
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><span class="cx" style="display: block; padding: 0 10px">                        siblings.removeClass( 'open' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        siblings.find( sectionContent ).show().slideUp( 150 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 siblings.find( '.accordion-section-content' ).show().slideUp( 150 );
</ins><span class="cx" style="display: block; padding: 0 10px">                         content.toggle( false ).slideToggle( 150 );
</span><span class="cx" style="display: block; padding: 0 10px">                        section.toggleClass( 'open' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        /**
-        * Slide into an accordion sub-panel.
-        *
-        * For the Customizer-specific panel functionality
-        *
-        * @param {Object} panel Title element or back button of the accordion panel to toggle.
-        * @since 4.0.0
-        */
-       function panelSwitch( panel ) {
-               var position, scroll,
-                       section = panel.closest( '.accordion-section' ),
-                       overlay = section.closest( '.wp-full-overlay' ),
-                       container = section.closest( '.accordion-container' ),
-                       siblings = container.find( '.open' ),
-                       topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ),
-                       backBtn = overlay.find( '.control-panel-back' ),
-                       panelTitle = section.find( '.accordion-section-title' ).first(),
-                       content = section.find( '.control-panel-content' );
-
-               if ( section.hasClass( 'current-panel' ) ) {
-                       section.toggleClass( 'current-panel' );
-                       overlay.toggleClass( 'in-sub-panel' );
-                       content.delay( 180 ).hide( 0, function() {
-                               content.css( 'margin-top', 'inherit' ); // Reset
-                       } );
-                       topPanel.attr( 'tabindex', '0' );
-                       backBtn.attr( 'tabindex', '-1' );
-                       panelTitle.focus();
-                       container.scrollTop( 0 );
-               } else {
-                       // Close all open sections in any accordion level.
-                       siblings.removeClass( 'open' );
-                       siblings.find( sectionContent ).show().slideUp( 0 );
-                       content.show( 0, function() {
-                               position = content.offset().top;
-                               scroll = container.scrollTop();
-                               content.css( 'margin-top', ( 45 - position - scroll ) );
-                               section.toggleClass( 'current-panel' );
-                               overlay.toggleClass( 'in-sub-panel' );
-                               container.scrollTop( 0 );
-                       } );
-                       topPanel.attr( 'tabindex', '-1' );
-                       backBtn.attr( 'tabindex', '0' );
-                       backBtn.focus();
-               }
-       }
-
</del><span class="cx" style="display: block; padding: 0 10px"> })(jQuery);
</span></span></pre></div>
<a id="trunksrcwpadminjscustomizecontrolsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/customize-controls.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/customize-controls.js       2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-admin/js/customize-controls.js 2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,7 +1,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /* globals _wpCustomizeHeader, _wpMediaViewsL10n */
</span><span class="cx" style="display: block; padding: 0 10px"> (function( exports, $ ){
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        var api = wp.customize;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ var bubbleChildValueChanges, Container, focus, isKeydownButNotEnterEvent, areElementListsEqual, api = wp.customize;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        // @todo Move private helper functions to wp.customize.utils so they can be unit tested
+
</ins><span class="cx" style="display: block; padding: 0 10px">         /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @constructor
</span><span class="cx" style="display: block; padding: 0 10px">         * @augments wp.customize.Value
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -31,60 +33,640 @@
</span><span class="cx" style="display: block; padding: 0 10px">        });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Watch all changes to Value properties, and bubble changes to parent Values instance
+        *
+        * @param {wp.customize.Class} instance
+        * @param {Array} properties  The names of the Value instances to watch.
+        */
+       bubbleChildValueChanges = function ( instance, properties ) {
+               $.each( properties, function ( i, key ) {
+                       instance[ key ].bind( function ( to, from ) {
+                               if ( instance.parent && to !== from ) {
+                                       instance.parent.trigger( 'change', instance );
+                               }
+                       } );
+               } );
+       };
+
+       /**
+        * Expand a panel, section, or control and focus on the first focusable element.
+        *
+        * @param {Object} [params]
+        */
+       focus = function ( params ) {
+               var construct, completeCallback, focus;
+               construct = this;
+               params = params || {};
+               focus = function () {
+                       construct.container.find( ':focusable:first' ).focus();
+                       construct.container[0].scrollIntoView( true );
+               };
+               if ( params.completeCallback ) {
+                       completeCallback = params.completeCallback;
+                       params.completeCallback = function () {
+                               focus();
+                               completeCallback();
+                       };
+               } else {
+                       params.completeCallback = focus;
+               }
+               if ( construct.expand ) {
+                       construct.expand( params );
+               } else {
+                       params.completeCallback();
+               }
+       };
+
+       /**
+        * Return whether the supplied Event object is for a keydown event but not the Enter key.
+        *
+        * @param {jQuery.Event} event
+        * @returns {boolean}
+        */
+       isKeydownButNotEnterEvent = function ( event ) {
+               return ( 'keydown' === event.type && 13 !== event.which );
+       };
+
+       /**
+        * Return whether the two lists of elements are the same and are in the same order.
+        *
+        * @param {Array|jQuery} listA
+        * @param {Array|jQuery} listB
+        * @returns {boolean}
+        */
+       areElementListsEqual = function ( listA, listB ) {
+               var equal = (
+                       listA.length === listB.length && // if lists are different lengths, then naturally they are not equal
+                       -1 === _.map( // are there any false values in the list returned by map?
+                               _.zip( listA, listB ), // pair up each element between the two lists
+                               function ( pair ) {
+                                       return $( pair[0] ).is( pair[1] ); // compare to see if each pair are equal
+                               }
+                       ).indexOf( false ) // check for presence of false in map's return value
+               );
+               return equal;
+       };
+
+       /**
+        * Base class for Panel and Section
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @constructor
</span><span class="cx" style="display: block; padding: 0 10px">         * @augments wp.customize.Class
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        api.Control = api.Class.extend({
-               initialize: function( id, options ) {
-                       var control = this,
-                               nodes, radios, settings;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ Container = api.Class.extend({
+               defaultActiveArguments: { duration: 'fast' },
+               defaultExpandedArguments: { duration: 'fast' },
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.params = {};
-                       $.extend( this, options || {} );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         initialize: function ( id, options ) {
+                       var container = this;
+                       container.id = id;
+                       container.params = {};
+                       $.extend( container, options || {} );
+                       container.container = $( container.params.content );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.id = id;
-                       this.selector = '#customize-control-' + id.replace( /\]/g, '' ).replace( /\[/g, '-' );
-                       this.container = $( this.selector );
-                       this.active = new api.Value( this.params.active );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 container.deferred = {
+                               ready: new $.Deferred()
+                       };
+                       container.priority = new api.Value();
+                       container.active = new api.Value();
+                       container.activeArgumentsQueue = [];
+                       container.expanded = new api.Value();
+                       container.expandedArgumentsQueue = [];
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        settings = $.map( this.params.settings, function( value ) {
-                               return value;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 container.active.bind( function ( active ) {
+                               var args = container.activeArgumentsQueue.shift();
+                               args = $.extend( {}, container.defaultActiveArguments, args );
+                               active = ( active && container.isContextuallyActive() );
+                               container.onChangeActive( active, args );
+                               // @todo trigger 'activated' and 'deactivated' events based on the expanded param?
</ins><span class="cx" style="display: block; padding: 0 10px">                         });
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        container.expanded.bind( function ( expanded ) {
+                               var args = container.expandedArgumentsQueue.shift();
+                               args = $.extend( {}, container.defaultExpandedArguments, args );
+                               container.onChangeExpanded( expanded, args );
+                               // @todo trigger 'expanded' and 'collapsed' events based on the expanded param?
+                       });
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        api.apply( api, settings.concat( function() {
-                               var key;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 container.attachEvents();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                control.settings = {};
-                               for ( key in control.params.settings ) {
-                                       control.settings[ key ] = api( control.params.settings[ key ] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 bubbleChildValueChanges( container, [ 'priority', 'active' ] );
+
+                       container.priority.set( isNaN( container.params.priority ) ? 100 : container.params.priority );
+                       container.active.set( container.params.active );
+                       container.expanded.set( false ); // @todo True if deeplinking?
+               },
+
+               /**
+                * @abstract
+                */
+               ready: function() {},
+
+               /**
+                * Get the child models associated with this parent, sorting them by their priority Value.
+                *
+                * @param {String} parentType
+                * @param {String} childType
+                * @returns {Array}
+                */
+               _children: function ( parentType, childType ) {
+                       var parent = this,
+                               children = [];
+                       api[ childType ].each( function ( child ) {
+                               if ( child[ parentType ].get() === parent.id ) {
+                                       children.push( child );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        } );
+                       children.sort( function ( a, b ) {
+                               return a.priority() - b.priority();
+                       } );
+                       return children;
+               },
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                control.setting = control.settings['default'] || null;
-                               control.renderContent( function() {
-                                       // Don't call ready() until the content has rendered.
-                                       control.ready();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * To override by subclass, to return whether the container has active children.
+                * @abstract
+                */
+               isContextuallyActive: function () {
+                       throw new Error( 'Must override with subclass.' );
+               },
+
+               /**
+                * Handle changes to the active state.
+                * This does not change the active state, it merely handles the behavior
+                * for when it does change.
+                *
+                * To override by subclass, update the container's UI to reflect the provided active state.
+                *
+                * @param {Boolean} active
+                * @param {Object} args  merged on top of this.defaultActiveArguments
+                */
+               onChangeActive: function ( active, args ) {
+                       var duration = ( 'resolved' === api.previewer.deferred.active.state() ? args.duration : 0 );
+                       if ( active ) {
+                               this.container.stop( true, true ).slideDown( duration, args.completeCallback );
+                       } else {
+                               this.container.stop( true, true ).slideUp( duration, args.completeCallback );
+                       }
+               },
+
+               /**
+                * @params {Boolean} active
+                * @param {Object} [params]
+                * @returns {Boolean} false if state already applied
+                */
+               _toggleActive: function ( active, params ) {
+                       var self = this;
+                       params = params || {};
+                       if ( ( active && this.active.get() ) || ( ! active && ! this.active.get() ) ) {
+                               params.unchanged = true;
+                               self.onChangeActive( self.active.get(), params );
+                               return false;
+                       } else {
+                               params.unchanged = false;
+                               this.activeArgumentsQueue.push( params );
+                               this.active.set( active );
+                               return true;
+                       }
+               },
+
+               /**
+                * @param {Object} [params]
+                * @returns {Boolean} false if already active
+                */
+               activate: function ( params ) {
+                       return this._toggleActive( true, params );
+               },
+
+               /**
+                * @param {Object} [params]
+                * @returns {Boolean} false if already inactive
+                */
+               deactivate: function ( params ) {
+                       return this._toggleActive( false, params );
+               },
+
+               /**
+                * To override by subclass, update the container's UI to reflect the provided active state.
+                * @abstract
+                */
+               onChangeExpanded: function () {
+                       throw new Error( 'Must override with subclass.' );
+               },
+
+               /**
+                * @param {Boolean} expanded
+                * @param {Object} [params]
+                * @returns {Boolean} false if state already applied
+                */
+               _toggleExpanded: function ( expanded, params ) {
+                       var self = this;
+                       params = params || {};
+                       if ( ( expanded && this.expanded.get() ) || ( ! expanded && ! this.expanded.get() ) ) {
+                               params.unchanged = true;
+                               self.onChangeExpanded( self.expanded.get(), params );
+                               return false;
+                       } else {
+                               params.unchanged = false;
+                               this.expandedArgumentsQueue.push( params );
+                               this.expanded.set( expanded );
+                               return true;
+                       }
+               },
+
+               /**
+                * @param {Object} [params]
+                * @returns {Boolean} false if already expanded
+                */
+               expand: function ( params ) {
+                       return this._toggleExpanded( true, params );
+               },
+
+               /**
+                * @param {Object} [params]
+                * @returns {Boolean} false if already collapsed
+                */
+               collapse: function ( params ) {
+                       return this._toggleExpanded( false, params );
+               },
+
+               /**
+                * Bring the container into view and then expand this and bring it into view
+                * @param {Object} [params]
+                */
+               focus: focus
+       });
+
+       /**
+        * @constructor
+        * @augments wp.customize.Class
+        */
+       api.Section = Container.extend({
+
+               /**
+                * @param {String} id
+                * @param {Array} options
+                */
+               initialize: function ( id, options ) {
+                       var section = this;
+                       Container.prototype.initialize.call( section, id, options );
+
+                       section.id = id;
+                       section.panel = new api.Value();
+                       section.panel.bind( function ( id ) {
+                               $( section.container ).toggleClass( 'control-subsection', !! id );
+                       });
+                       section.panel.set( section.params.panel || '' );
+                       bubbleChildValueChanges( section, [ 'panel' ] );
+
+                       section.embed();
+                       section.deferred.ready.done( function () {
+                               section.ready();
+                       });
+               },
+
+               /**
+                * Embed the container in the DOM when any parent panel is ready.
+                */
+               embed: function () {
+                       var section = this, inject;
+
+                       // Watch for changes to the panel state
+                       inject = function ( panelId ) {
+                               var parentContainer;
+                               if ( panelId ) {
+                                       // The panel has been supplied, so wait until the panel object is registered
+                                       api.panel( panelId, function ( panel ) {
+                                               // The panel has been registered, wait for it to become ready/initialized
+                                               panel.deferred.ready.done( function () {
+                                                       parentContainer = panel.container.find( 'ul:first' );
+                                                       if ( ! section.container.parent().is( parentContainer ) ) {
+                                                               parentContainer.append( section.container );
+                                                       }
+                                                       section.deferred.ready.resolve(); // @todo Better to use `embedded` instead of `ready`
+                                               });
+                                       } );
+                               } else {
+                                       // There is no panel, so embed the section in the root of the customizer
+                                       parentContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
+                                       if ( ! section.container.parent().is( parentContainer ) ) {
+                                               parentContainer.append( section.container );
+                                       }
+                                       section.deferred.ready.resolve();
+                               }
+                       };
+                       section.panel.bind( inject );
+                       inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one
+               },
+
+               /**
+                * Add behaviors for the accordion section
+                */
+               attachEvents: function () {
+                       var section = this;
+
+                       // Expand/Collapse accordion sections on click.
+                       section.container.find( '.accordion-section-title' ).on( 'click keydown', function( event ) {
+                               if ( isKeydownButNotEnterEvent( event ) ) {
+                                       return;
+                               }
+                               event.preventDefault(); // Keep this AFTER the key filter above
+
+                               if ( section.expanded() ) {
+                                       section.collapse();
+                               } else {
+                                       section.expand();
+                               }
+                       });
+               },
+
+               /**
+                * Return whether this section has any active controls.
+                *
+                * @returns {boolean}
+                */
+               isContextuallyActive: function () {
+                       var section = this,
+                               controls = section.controls(),
+                               activeCount = 0;
+                       _( controls ).each( function ( control ) {
+                               if ( control.active() ) {
+                                       activeCount += 1;
+                               }
+                       } );
+                       return ( activeCount !== 0 );
+               },
+
+               /**
+                * Get the controls that are associated with this section, sorted by their priority Value.
+                *
+                * @returns {Array}
+                */
+               controls: function () {
+                       return this._children( 'section', 'control' );
+               },
+
+               /**
+                * Update UI to reflect expanded state
+                *
+                * @param {Boolean} expanded
+                * @param {Object} args
+                */
+               onChangeExpanded: function ( expanded, args ) {
+                       var section = this,
+                               content = section.container.find( '.accordion-section-content' ),
+                               expand;
+
+                       if ( expanded ) {
+
+                               if ( args.unchanged ) {
+                                       expand = args.completeCallback;
+                               } else {
+                                       expand = function () {
+                                               content.stop().slideDown( args.duration, args.completeCallback );
+                                               section.container.addClass( 'open' );
+                                       };
+                               }
+
+                               if ( ! args.allowMultiple ) {
+                                       api.section.each( function ( otherSection ) {
+                                               if ( otherSection !== section ) {
+                                                       otherSection.collapse( { duration: args.duration } );
+                                               }
+                                       });
+                               }
+
+                               if ( section.panel() ) {
+                                       api.panel( section.panel() ).expand({
+                                               duration: args.duration,
+                                               completeCallback: expand
+                                       });
+                               } else {
+                                       expand();
+                               }
+
+                       } else {
+                               section.container.removeClass( 'open' );
+                               content.slideUp( args.duration, args.completeCallback );
+                       }
+               }
+       });
+
+       /**
+        * @constructor
+        * @augments wp.customize.Class
+        */
+       api.Panel = Container.extend({
+               initialize: function ( id, options ) {
+                       var panel = this;
+                       Container.prototype.initialize.call( panel, id, options );
+                       panel.embed();
+                       panel.deferred.ready.done( function () {
+                               panel.ready();
+                       });
+               },
+
+               /**
+                * Embed the container in the DOM when any parent panel is ready.
+                */
+               embed: function () {
+                       var panel = this,
+                               parentContainer = $( '#customize-theme-controls > ul' ); // @todo This should be defined elsewhere, and to be configurable
+
+                       if ( ! panel.container.parent().is( parentContainer ) ) {
+                               parentContainer.append( panel.container );
+                       }
+                       panel.deferred.ready.resolve();
+               },
+
+               /**
+                *
+                */
+               attachEvents: function () {
+                       var meta, panel = this;
+
+                       // Expand/Collapse accordion sections on click.
+                       panel.container.find( '.accordion-section-title' ).on( 'click keydown', function( event ) {
+                               if ( isKeydownButNotEnterEvent( event ) ) {
+                                       return;
+                               }
+                               event.preventDefault(); // Keep this AFTER the key filter above
+
+                               if ( ! panel.expanded() ) {
+                                       panel.expand();
+                               }
+                       });
+
+                       meta = panel.container.find( '.panel-meta:first' );
+
+                       meta.find( '> .accordion-section-title' ).on( 'click keydown', function( event ) {
+                               if ( isKeydownButNotEnterEvent( event ) ) {
+                                       return;
+                               }
+                               event.preventDefault(); // Keep this AFTER the key filter above
+
+                               if ( meta.hasClass( 'cannot-expand' ) ) {
+                                       return;
+                               }
+
+                               var content = meta.find( '.accordion-section-content:first' );
+                               if ( meta.hasClass( 'open' ) ) {
+                                       meta.toggleClass( 'open' );
+                                       content.slideUp( panel.defaultExpandedArguments.duration );
+                               } else {
+                                       content.slideDown( panel.defaultExpandedArguments.duration );
+                                       meta.toggleClass( 'open' );
+                               }
+                       });
+
+               },
+
+               /**
+                * Get the sections that are associated with this panel, sorted by their priority Value.
+                *
+                * @returns {Array}
+                */
+               sections: function () {
+                       return this._children( 'panel', 'section' );
+               },
+
+               /**
+                * Return whether this panel has any active sections.
+                *
+                * @returns {boolean}
+                */
+               isContextuallyActive: function () {
+                       var panel = this,
+                               sections = panel.sections(),
+                               activeCount = 0;
+                       _( sections ).each( function ( section ) {
+                               if ( section.active() && section.isContextuallyActive() ) {
+                                       activeCount += 1;
+                               }
+                       } );
+                       return ( activeCount !== 0 );
+               },
+
+               /**
+                * Update UI to reflect expanded state
+                *
+                * @param {Boolean} expanded
+                * @param {Object} args  merged with this.defaultExpandedArguments
+                */
+               onChangeExpanded: function ( expanded, args ) {
+
+                       // Immediately call the complete callback if there were no changes
+                       if ( args.unchanged ) {
+                               if ( args.completeCallback ) {
+                                       args.completeCallback();
+                               }
+                               return;
+                       }
+
+                       // Note: there is a second argument 'args' passed
+                       var position, scroll,
+                               panel = this,
+                               section = panel.container.closest( '.accordion-section' ),
+                               overlay = section.closest( '.wp-full-overlay' ),
+                               container = section.closest( '.accordion-container' ),
+                               siblings = container.find( '.open' ),
+                               topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ),
+                               backBtn = overlay.find( '.control-panel-back' ),
+                               panelTitle = section.find( '.accordion-section-title' ).first(),
+                               content = section.find( '.control-panel-content' );
+
+                       if ( expanded ) {
+
+                               // Collapse any sibling sections/panels
+                               api.section.each( function ( section ) {
+                                       if ( ! section.panel() ) {
+                                               section.collapse( { duration: 0 } );
+                                       }
+                               });
+                               api.panel.each( function ( otherPanel ) {
+                                       if ( panel !== otherPanel ) {
+                                               otherPanel.collapse( { duration: 0 } );
+                                       }
+                               });
+
+                               content.show( 0, function() {
+                                       position = content.offset().top;
+                                       scroll = container.scrollTop();
+                                       content.css( 'margin-top', ( 45 - position - scroll ) );
+                                       section.addClass( 'current-panel' );
+                                       overlay.addClass( 'in-sub-panel' );
+                                       container.scrollTop( 0 );
+                                       if ( args.completeCallback ) {
+                                               args.completeCallback();
+                                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                                 } );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        }) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         topPanel.attr( 'tabindex', '-1' );
+                               backBtn.attr( 'tabindex', '0' );
+                               backBtn.focus();
+                       } else {
+                               siblings.removeClass( 'open' );
+                               section.removeClass( 'current-panel' );
+                               overlay.removeClass( 'in-sub-panel' );
+                               content.delay( 180 ).hide( 0, function() {
+                                       content.css( 'margin-top', 'inherit' ); // Reset
+                                       if ( args.completeCallback ) {
+                                               args.completeCallback();
+                                       }
+                               } );
+                               topPanel.attr( 'tabindex', '0' );
+                               backBtn.attr( 'tabindex', '-1' );
+                               panelTitle.focus();
+                               container.scrollTop( 0 );
+                       }
+               }
+       });
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * @constructor
+        * @augments wp.customize.Class
+        */
+       api.Control = api.Class.extend({
+               defaultActiveArguments: { duration: 'fast' },
+
+               initialize: function( id, options ) {
+                       var control = this,
+                               nodes, radios, settings;
+
+                       control.params = {};
+                       $.extend( control, options || {} );
+
+                       control.id = id;
+                       control.selector = '#customize-control-' + id.replace( /\]/g, '' ).replace( /\[/g, '-' );
+                       control.templateSelector = 'customize-control-' + control.params.type + '-content';
+                       control.container = control.params.content ? $( control.params.content ) : $( control.selector );
+
+                       control.deferred = {
+                               ready: new $.Deferred()
+                       };
+                       control.section = new api.Value();
+                       control.priority = new api.Value();
+                       control.active = new api.Value();
+                       control.activeArgumentsQueue = [];
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         control.elements = [];
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        nodes  = this.container.find('[data-customize-setting-link]');
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 nodes  = control.container.find('[data-customize-setting-link]');
</ins><span class="cx" style="display: block; padding: 0 10px">                         radios = {};
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        nodes.each( function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                var node = $(this),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         var node = $( this ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         name;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( node.is(':radio') ) {
-                                       name = node.prop('name');
-                                       if ( radios[ name ] )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( node.is( ':radio' ) ) {
+                                       name = node.prop( 'name' );
+                                       if ( radios[ name ] ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                        radios[ name ] = true;
</span><span class="cx" style="display: block; padding: 0 10px">                                        node = nodes.filter( '[name="' + name + '"]' );
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                api( node.data('customizeSettingLink'), function( setting ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         api( node.data( 'customizeSettingLink' ), function( setting ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         var element = new api.Element( node );
</span><span class="cx" style="display: block; padding: 0 10px">                                        control.elements.push( element );
</span><span class="cx" style="display: block; padding: 0 10px">                                        element.sync( setting );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -93,31 +675,127 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        control.active.bind( function ( active ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                control.toggle( active );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         var args = control.activeArgumentsQueue.shift();
+                               args = $.extend( {}, control.defaultActiveArguments, args );
+                               control.onChangeActive( active, args );
</ins><span class="cx" style="display: block; padding: 0 10px">                         } );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        control.toggle( control.active() );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       control.section.set( control.params.section );
+                       control.priority.set( isNaN( control.params.priority ) ? 10 : control.params.priority );
+                       control.active.set( control.params.active );
+
+                       bubbleChildValueChanges( control, [ 'section', 'priority', 'active' ] );
+
+                       // Associate this control with its settings when they are created
+                       settings = $.map( control.params.settings, function( value ) {
+                               return value;
+                       });
+                       api.apply( api, settings.concat( function () {
+                               var key;
+
+                               control.settings = {};
+                               for ( key in control.params.settings ) {
+                                       control.settings[ key ] = api( control.params.settings[ key ] );
+                               }
+
+                               control.setting = control.settings['default'] || null;
+
+                               control.embed();
+                       }) );
+
+                       control.deferred.ready.done( function () {
+                               control.ready();
+                       });
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 *
+                */
+               embed: function () {
+                       var control = this,
+                               inject;
+
+                       // Watch for changes to the section state
+                       inject = function ( sectionId ) {
+                               var parentContainer;
+                               if ( ! sectionId ) { // @todo allow a control to be embeded without a section, for instance a control embedded in the frontend
+                                       return;
+                               }
+                               // Wait for the section to be registered
+                               api.section( sectionId, function ( section ) {
+                                       // Wait for the section to be ready/initialized
+                                       section.deferred.ready.done( function () {
+                                               parentContainer = section.container.find( 'ul:first' );
+                                               if ( ! control.container.parent().is( parentContainer ) ) {
+                                                       parentContainer.append( control.container );
+                                                       control.renderContent();
+                                               }
+                                               control.deferred.ready.resolve(); // @todo Better to use `embedded` instead of `ready`
+                                       });
+                               });
+                       };
+                       control.section.bind( inject );
+                       inject( control.section.get() );
+               },
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @abstract
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                ready: function() {},
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * Callback for change to the control's active state.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * Normal controls do not expand, so just expand its parent
</ins><span class="cx" style="display: block; padding: 0 10px">                  *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * Override function for custom behavior for the control being active/inactive.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * @param {Object} [params]
+                */
+               expand: function ( params ) {
+                       api.section( this.section() ).expand( params );
+               },
+
+               /**
+                * Bring the containing section and panel into view and then this control into view, focusing on the first input
+                */
+               focus: focus,
+
+               /**
+                * Update UI in response to a change in the control's active state.
+                * This does not change the active state, it merely handles the behavior
+                * for when it does change.
</ins><span class="cx" style="display: block; padding: 0 10px">                  *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param {Boolean} active
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @param {Object} args  merged on top of this.defaultActiveArguments
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                toggle: function ( active ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         onChangeActive: function ( active, args ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         if ( active ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                this.container.slideDown();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         this.container.slideDown( args.duration, args.completeCallback );
</ins><span class="cx" style="display: block; padding: 0 10px">                         } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                this.container.slideUp();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         this.container.slideUp( args.duration, args.completeCallback );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                /**
+                * @deprecated alias of onChangeActive
+                */
+               toggle: function ( active ) {
+                       return this.onChangeActive( active, this.defaultActiveArguments );
+               },
+
+               /**
+                * Shorthand way to enable the active state.
+                *
+                * @param {Object} [params]
+                * @returns {Boolean} false if already active
+                */
+               activate: Container.prototype.activate,
+
+               /**
+                * Shorthand way to disable the active state.
+                *
+                * @param {Object} [params]
+                * @returns {Boolean} false if already inactive
+                */
+               deactivate: Container.prototype.deactivate,
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 dropdownInit: function() {
</span><span class="cx" style="display: block; padding: 0 10px">                        var control      = this,
</span><span class="cx" style="display: block; padding: 0 10px">                                statuses     = this.container.find('.dropdown-status'),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -132,8 +810,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Support the .dropdown class to open/close complex elements
</span><span class="cx" style="display: block; padding: 0 10px">                        this.container.on( 'click keydown', '.dropdown', function( event ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( event.type === 'keydown' &&  13 !== event.which ) // enter
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( isKeydownButNotEnterEvent( event ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                event.preventDefault();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -157,20 +836,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Render the control from its JS template, if it exists.
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * The control's container must alreasy exist in the DOM.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * The control's container must already exist in the DOM.
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                renderContent: function( callback ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         renderContent: function () {
</ins><span class="cx" style="display: block; padding: 0 10px">                         var template,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                selector = 'customize-control-' + this.params.type + '-content';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         control = this;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        callback = callback || function(){};
-                       if ( 0 !== $( '#tmpl-' + selector ).length ) {
-                               template = wp.template( selector );
-                               if ( template && this.container ) {
-                                       this.container.append( template( this.params ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( 0 !== $( '#tmpl-' + control.templateSelector ).length ) {
+                               template = wp.template( control.templateSelector );
+                               if ( template && control.container ) {
+                                       control.container.append( template( control.params ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        callback();
</del><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">        });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -234,8 +911,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this.remover = this.container.find('.remove');
</span><span class="cx" style="display: block; padding: 0 10px">                        this.remover.on( 'click keydown', function( event ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( event.type === 'keydown' &&  13 !== event.which ) // enter
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( isKeydownButNotEnterEvent( event ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                control.setting.set( control.params.removed );
</span><span class="cx" style="display: block; padding: 0 10px">                                event.preventDefault();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -306,8 +984,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Bind tab switch events
</span><span class="cx" style="display: block; padding: 0 10px">                        this.library.children('ul').on( 'click keydown', 'li', function( event ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( event.type === 'keydown' &&  13 !== event.which ) // enter
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( isKeydownButNotEnterEvent( event ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                var id  = $(this).data('customizeTab'),
</span><span class="cx" style="display: block; padding: 0 10px">                                        tab = control.tabs[ id ];
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -324,8 +1003,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Bind events to switch image urls.
</span><span class="cx" style="display: block; padding: 0 10px">                        this.library.on( 'click keydown', 'a', function( event ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( event.type === 'keydown' && 13 !== event.which ) // enter
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( isKeydownButNotEnterEvent( event ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                var value = $(this).data('customizeImageValue');
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -597,6 +1277,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // Create the collection of Control objects.
</span><span class="cx" style="display: block; padding: 0 10px">        api.control = new api.Values({ defaultConstructor: api.Control });
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        api.section = new api.Values({ defaultConstructor: api.Section });
+       api.panel = new api.Values({ defaultConstructor: api.Panel });
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="cx" style="display: block; padding: 0 10px">         * @constructor
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -632,29 +1314,42 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                loaded = false,
</span><span class="cx" style="display: block; padding: 0 10px">                                ready  = false;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( this._ready )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( this._ready ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 this.unbind( 'ready', this._ready );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this._ready = function() {
</span><span class="cx" style="display: block; padding: 0 10px">                                ready = true;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( loaded )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( loaded ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         deferred.resolveWith( self );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                }
</ins><span class="cx" style="display: block; padding: 0 10px">                         };
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this.bind( 'ready', this._ready );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this.bind( 'ready', function ( data ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( ! data || ! data.activeControls ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( ! data ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         return;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $.each( data.activeControls, function ( id, active ) {
-                                       var control = api.control( id );
-                                       if ( control ) {
-                                               control.active( active );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         var constructs = {
+                                       panel: data.activePanels,
+                                       section: data.activeSections,
+                                       control: data.activeControls
+                               };
+
+                               $.each( constructs, function ( type, activeConstructs ) {
+                                       if ( activeConstructs ) {
+                                               $.each( activeConstructs, function ( id, active ) {
+                                                       var construct = api[ type ]( id );
+                                                       if ( construct ) {
+                                                               construct.active( active );
+                                                       }
+                                               } );
</ins><span class="cx" style="display: block; padding: 0 10px">                                         }
</span><span class="cx" style="display: block; padding: 0 10px">                                } );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                         } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this.request = $.ajax( this.previewUrl(), {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -676,7 +1371,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                // Check if the location response header differs from the current URL.
</span><span class="cx" style="display: block; padding: 0 10px">                                // If so, the request was redirected; try loading the requested page.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( location && location != self.previewUrl() ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( location && location !== self.previewUrl() ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                         deferred.rejectWith( self, [ 'redirect', location ] );
</span><span class="cx" style="display: block; padding: 0 10px">                                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -803,6 +1498,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                rscheme = /^https?/;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $.extend( this, options || {} );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        this.deferred = {
+                               active: $.Deferred()
+                       };
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        /*
</span><span class="cx" style="display: block; padding: 0 10px">                         * Wrap this.refresh to prevent it from hammering the servers:
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -934,6 +1632,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        self.targetWindow( this.targetWindow() );
</span><span class="cx" style="display: block; padding: 0 10px">                                        self.channel( this.channel() );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        self.deferred.active.resolve();
</ins><span class="cx" style="display: block; padding: 0 10px">                                         self.send( 'active' );
</span><span class="cx" style="display: block; padding: 0 10px">                                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1001,6 +1700,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                image:  api.ImageControl,
</span><span class="cx" style="display: block; padding: 0 10px">                header: api.HeaderControl
</span><span class="cx" style="display: block; padding: 0 10px">        };
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        api.panelConstructor = {};
+       api.sectionConstructor = {};
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        $( function() {
</span><span class="cx" style="display: block; padding: 0 10px">                api.settings = window._wpCustomizeSettings;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1031,6 +1732,29 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Expand/Collapse the main customizer customize info
+               $( '#customize-info' ).find( '> .accordion-section-title' ).on( 'click keydown', function( event ) {
+                       if ( isKeydownButNotEnterEvent( event ) ) {
+                               return;
+                       }
+                       event.preventDefault(); // Keep this AFTER the key filter above
+
+                       var section = $( this ).parent(),
+                               content = section.find( '.accordion-section-content:first' );
+
+                       if ( section.hasClass( 'cannot-expand' ) ) {
+                               return;
+                       }
+
+                       if ( section.hasClass( 'open' ) ) {
+                               section.toggleClass( 'open' );
+                               content.slideUp( api.Panel.prototype.defaultExpandedArguments.duration );
+                       } else {
+                               content.slideDown( api.Panel.prototype.defaultExpandedArguments.duration );
+                               section.toggleClass( 'open' );
+                       }
+               });
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Initialize Previewer
</span><span class="cx" style="display: block; padding: 0 10px">                api.previewer = new api.Previewer({
</span><span class="cx" style="display: block; padding: 0 10px">                        container:   '#customize-preview',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1124,6 +1848,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $.extend( this.nonce, nonce );
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Create Settings
</ins><span class="cx" style="display: block; padding: 0 10px">                 $.each( api.settings.settings, function( id, data ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        api.create( id, id, data.value, {
</span><span class="cx" style="display: block; padding: 0 10px">                                transport: data.transport,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1131,16 +1856,132 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        } );
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Create Panels
+               $.each( api.settings.panels, function ( id, data ) {
+                       var constructor = api.panelConstructor[ data.type ] || api.Panel,
+                               panel;
+
+                       panel = new constructor( id, {
+                               params: data
+                       } );
+                       api.panel.add( id, panel );
+               });
+
+               // Create Sections
+               $.each( api.settings.sections, function ( id, data ) {
+                       var constructor = api.sectionConstructor[ data.type ] || api.Section,
+                               section;
+
+                       section = new constructor( id, {
+                               params: data
+                       } );
+                       api.section.add( id, section );
+               });
+
+               // Create Controls
</ins><span class="cx" style="display: block; padding: 0 10px">                 $.each( api.settings.controls, function( id, data ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        var constructor = api.controlConstructor[ data.type ] || api.Control,
</span><span class="cx" style="display: block; padding: 0 10px">                                control;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        control = api.control.add( id, new constructor( id, {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 control = new constructor( id, {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 params: data,
</span><span class="cx" style="display: block; padding: 0 10px">                                previewer: api.previewer
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        } ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 } );
+                       api.control.add( id, control );
</ins><span class="cx" style="display: block; padding: 0 10px">                 });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Focus the autofocused element
+               _.each( [ 'panel', 'section', 'control' ], function ( type ) {
+                       var instance, id = api.settings.autofocus[ type ];
+                       if ( id && api[ type ]( id ) ) {
+                               instance = api[ type ]( id );
+                               // Wait until the element is embedded in the DOM
+                               instance.deferred.ready.done( function () {
+                                       // Wait until the preview has activated and so active panels, sections, controls have been set
+                                       api.previewer.deferred.active.done( function () {
+                                               instance.focus();
+                                       });
+                               });
+                       }
+               });
+
+               /**
+                * Sort panels, sections, controls by priorities. Hide empty sections and panels.
+                */
+               api.reflowPaneContents = _.bind( function () {
+
+                       var appendContainer, activeElement, rootContainers, rootNodes = [], wasReflowed = false;
+
+                       if ( document.activeElement ) {
+                               activeElement = $( document.activeElement );
+                       }
+
+                       // Sort the sections within each panel
+                       api.panel.each( function ( panel ) {
+                               var sections = panel.sections(),
+                                       sectionContainers = _.pluck( sections, 'container' );
+                               rootNodes.push( panel );
+                               appendContainer = panel.container.find( 'ul:first' );
+                               if ( ! areElementListsEqual( sectionContainers, appendContainer.children( '[id]' ) ) ) {
+                                       _( sections ).each( function ( section ) {
+                                               appendContainer.append( section.container );
+                                       } );
+                                       wasReflowed = true;
+                               }
+                       } );
+
+                       // Sort the controls within each section
+                       api.section.each( function ( section ) {
+                               var controls = section.controls(),
+                                       controlContainers = _.pluck( controls, 'container' );
+                               if ( ! section.panel() ) {
+                                       rootNodes.push( section );
+                               }
+                               appendContainer = section.container.find( 'ul:first' );
+                               if ( ! areElementListsEqual( controlContainers, appendContainer.children( '[id]' ) ) ) {
+                                       _( controls ).each( function ( control ) {
+                                               appendContainer.append( control.container );
+                                       } );
+                                       wasReflowed = true;
+                               }
+                       } );
+
+                       // Sort the root panels and sections
+                       rootNodes.sort( function ( a, b ) {
+                               return a.priority() - b.priority();
+                       } );
+                       rootContainers = _.pluck( rootNodes, 'container' );
+                       appendContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
+                       if ( ! areElementListsEqual( rootContainers, appendContainer.children() ) ) {
+                               _( rootNodes ).each( function ( rootNode ) {
+                                       appendContainer.append( rootNode.container );
+                               } );
+                               wasReflowed = true;
+                       }
+
+                       // Now re-trigger the active Value callbacks to that the panels and sections can decide whether they can be rendered
+                       api.panel.each( function ( panel ) {
+                               var value = panel.active();
+                               panel.active.callbacks.fireWith( panel.active, [ value, value ] );
+                       } );
+                       api.section.each( function ( section ) {
+                               var value = section.active();
+                               section.active.callbacks.fireWith( section.active, [ value, value ] );
+                       } );
+
+                       // Restore focus if there was a reflow and there was an active (focused) element
+                       if ( wasReflowed && activeElement ) {
+                               activeElement.focus();
+                       }
+               }, api );
+               api.bind( 'ready', api.reflowPaneContents );
+               api.reflowPaneContents = _.debounce( api.reflowPaneContents, 100 );
+               $( [ api.panel, api.section, api.control ] ).each( function ( i, values ) {
+                       values.bind( 'add', api.reflowPaneContents );
+                       values.bind( 'change', api.reflowPaneContents );
+                       values.bind( 'remove', api.reflowPaneContents );
+               } );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // Check if preview url is valid and load the preview frame.
</span><span class="cx" style="display: block; padding: 0 10px">                if ( api.previewer.previewUrl() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        api.previewer.refresh();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1205,6 +2046,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        event.preventDefault();
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                // Go back to the top-level Customizer accordion.
+               $( '#customize-header-actions' ).on( 'click keydown', '.control-panel-back', function( event ) {
+                       if ( isKeydownButNotEnterEvent( event ) ) {
+                               return;
+                       }
+
+                       event.preventDefault(); // Keep this AFTER the key filter above
+                       api.panel.each( function ( panel ) {
+                               panel.collapse();
+                       });
+               });
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 closeBtn.keydown( function( event ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( 9 === event.which ) // tab
</span><span class="cx" style="display: block; padding: 0 10px">                                return;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1219,8 +2072,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $('.collapse-sidebar').on( 'click keydown', function( event ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( event.type === 'keydown' &&  13 !== event.which ) // enter
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( isKeydownButNotEnterEvent( event ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        overlay.toggleClass( 'collapsed' ).toggleClass( 'expanded' );
</span><span class="cx" style="display: block; padding: 0 10px">                        event.preventDefault();
</span></span></pre></div>
<a id="trunksrcwpadminjscustomizewidgetsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/customize-widgets.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/customize-widgets.js        2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-admin/js/customize-widgets.js  2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -404,6 +404,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @augments wp.customize.Control
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        api.Widgets.WidgetControl = api.Control.extend({
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                defaultExpandedArguments: {
+                       duration: 'fast'
+               },
+
+               initialize: function ( id, options ) {
+                       var control = this;
+                       api.Control.prototype.initialize.call( control, id, options );
+                       control.expanded = new api.Value();
+                       control.expandedArgumentsQueue = [];
+                       control.expanded.bind( function ( expanded ) {
+                               var args = control.expandedArgumentsQueue.shift();
+                               args = $.extend( {}, control.defaultExpandedArguments, args );
+                               control.onChangeExpanded( expanded, args );
+                       });
+                       control.expanded.set( false );
+               },
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Set up the control
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -529,13 +546,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( sidebarWidgetsControl.isReordering ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                self.toggleForm();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         self.expanded( ! self.expanded() );
</ins><span class="cx" style="display: block; padding: 0 10px">                         } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $closeBtn = this.container.find( '.widget-control-close' );
</span><span class="cx" style="display: block; padding: 0 10px">                        $closeBtn.on( 'click', function( e ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                e.preventDefault();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                self.collapseForm();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         self.collapse();
</ins><span class="cx" style="display: block; padding: 0 10px">                                 self.container.find( '.widget-top .widget-action:first' ).focus(); // keyboard accessibility
</span><span class="cx" style="display: block; padding: 0 10px">                        } );
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -777,9 +794,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * Overrides api.Control.toggle()
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param {Boolean} active
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @param {Object} args
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                toggle: function ( active ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         onChangeActive: function ( active, args ) {
+                       // Note: there is a second 'args' parameter being passed, merged on top of this.defaultActiveArguments
</ins><span class="cx" style="display: block; padding: 0 10px">                         this.container.toggleClass( 'widget-rendered', active );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        if ( args.completeCallback ) {
+                               args.completeCallback();
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1101,51 +1123,90 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * Expand the accordion section containing a control
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                expandControlSection: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var $section = this.container.closest( '.accordion-section' );
-
-                       if ( ! $section.hasClass( 'open' ) ) {
-                               $section.find( '.accordion-section-title:first' ).trigger( 'click' );
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 api.Control.prototype.expand.call( this );
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @param {Boolean} expanded
+                * @param {Object} [params]
+                * @returns {Boolean} false if state already applied
+                */
+               _toggleExpanded: api.Section.prototype._toggleExpanded,
+
+               /**
+                * @param {Object} [params]
+                * @returns {Boolean} false if already expanded
+                */
+               expand: api.Section.prototype.expand,
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * Expand the widget form control
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 *
+                * @deprecated alias of expand()
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><span class="cx" style="display: block; padding: 0 10px">                expandForm: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.toggleForm( true );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 this.expand();
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @param {Object} [params]
+                * @returns {Boolean} false if already collapsed
+                */
+               collapse: api.Section.prototype.collapse,
+
+               /**
</ins><span class="cx" style="display: block; padding: 0 10px">                  * Collapse the widget form control
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 *
+                * @deprecated alias of expand()
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><span class="cx" style="display: block; padding: 0 10px">                collapseForm: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.toggleForm( false );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 this.collapse();
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Expand or collapse the widget control
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 * @deprecated this is poor naming, and it is better to directly set control.expanded( showOrHide )
+                *
</ins><span class="cx" style="display: block; padding: 0 10px">                  * @param {boolean|undefined} [showOrHide] If not supplied, will be inverse of current visibility
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                toggleForm: function( showOrHide ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var self = this, $widget, $inside, complete;
-
-                       $widget = this.container.find( 'div.widget:first' );
-                       $inside = $widget.find( '.widget-inside:first' );
</del><span class="cx" style="display: block; padding: 0 10px">                         if ( typeof showOrHide === 'undefined' ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                showOrHide = ! $inside.is( ':visible' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         showOrHide = ! this.expanded();
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        this.expanded( showOrHide );
+               },
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        // Already expanded or collapsed, so noop
-                       if ( $inside.is( ':visible' ) === showOrHide ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /**
+                * Respond to change in the expanded state.
+                *
+                * @param {Boolean} expanded
+                * @param {Object} args  merged on top of this.defaultActiveArguments
+                */
+               onChangeExpanded: function ( expanded, args ) {
+                       var self = this, $widget, $inside, complete, prevComplete;
+
+                       // If the expanded state is unchanged only manipulate container expanded states
+                       if ( args.unchanged ) {
+                               if ( expanded ) {
+                                       api.Control.prototype.expand.call( self, {
+                                               completeCallback:  args.completeCallback
+                                       });
+                               }
</ins><span class="cx" style="display: block; padding: 0 10px">                                 return;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( showOrHide ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $widget = this.container.find( 'div.widget:first' );
+                       $inside = $widget.find( '.widget-inside:first' );
+
+                       if ( expanded ) {
+
+                               self.expandControlSection();
+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 // Close all other widget controls before expanding this one
</span><span class="cx" style="display: block; padding: 0 10px">                                api.control.each( function( otherControl ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                        if ( self.params.type === otherControl.params.type && self !== otherControl ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                otherControl.collapseForm();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         otherControl.collapse();
</ins><span class="cx" style="display: block; padding: 0 10px">                                         }
</span><span class="cx" style="display: block; padding: 0 10px">                                } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1154,29 +1215,44 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        self.container.addClass( 'expanded' );
</span><span class="cx" style="display: block; padding: 0 10px">                                        self.container.trigger( 'expanded' );
</span><span class="cx" style="display: block; padding: 0 10px">                                };
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                if ( args.completeCallback ) {
+                                       prevComplete = complete;
+                                       complete = function () {
+                                               prevComplete();
+                                               args.completeCallback();
+                                       };
+                               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( self.params.is_wide ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        $inside.fadeIn( 'fast', complete );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 $inside.fadeIn( args.duration, complete );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        $inside.slideDown( 'fast', complete );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 $inside.slideDown( args.duration, complete );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                self.container.trigger( 'expand' );
</span><span class="cx" style="display: block; padding: 0 10px">                                self.container.addClass( 'expanding' );
</span><span class="cx" style="display: block; padding: 0 10px">                        } else {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                                 complete = function() {
</span><span class="cx" style="display: block; padding: 0 10px">                                        self.container.removeClass( 'collapsing' );
</span><span class="cx" style="display: block; padding: 0 10px">                                        self.container.removeClass( 'expanded' );
</span><span class="cx" style="display: block; padding: 0 10px">                                        self.container.trigger( 'collapsed' );
</span><span class="cx" style="display: block; padding: 0 10px">                                };
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                if ( args.completeCallback ) {
+                                       prevComplete = complete;
+                                       complete = function () {
+                                               prevComplete();
+                                               args.completeCallback();
+                                       };
+                               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                self.container.trigger( 'collapse' );
</span><span class="cx" style="display: block; padding: 0 10px">                                self.container.addClass( 'collapsing' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( self.params.is_wide ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        $inside.fadeOut( 'fast', complete );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 $inside.fadeOut( args.duration, complete );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 } else {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        $inside.slideUp( 'fast', function() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 $inside.slideUp( args.duration, function() {
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 $widget.css( { width:'', margin:'' } );
</span><span class="cx" style="display: block; padding: 0 10px">                                                complete();
</span><span class="cx" style="display: block; padding: 0 10px">                                        } );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1185,16 +1261,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * Expand the containing sidebar section, expand the form, and focus on
-                * the first input in the control
-                */
-               focus: function() {
-                       this.expandControlSection();
-                       this.expandForm();
-                       this.container.find( '.widget-content :focusable:first' ).focus();
-               },
-
-               /**
</del><span class="cx" style="display: block; padding: 0 10px">                  * Get the position (index) of the widget in the containing sidebar
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @returns {Number}
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1304,6 +1370,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @augments wp.customize.Control
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        api.Widgets.SidebarControl = api.Control.extend({
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Set up the control
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1325,7 +1392,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                registeredSidebar = api.Widgets.registeredSidebars.get( this.params.sidebar_id );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this.setting.bind( function( newWidgetIds, oldWidgetIds ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                var widgetFormControls, $sidebarWidgetsAddControl, finalControlContainers, removedWidgetIds;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         var widgetFormControls, removedWidgetIds, priority;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                removedWidgetIds = _( oldWidgetIds ).difference( newWidgetIds );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1350,22 +1417,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                widgetFormControls.sort( function( a, b ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                        var aIndex = _.indexOf( newWidgetIds, a.params.widget_id ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                bIndex = _.indexOf( newWidgetIds, b.params.widget_id );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        return aIndex - bIndex;
+                               });
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        if ( aIndex === bIndex ) {
-                                               return 0;
-                                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         priority = 0;
+                               _( widgetFormControls ).each( function ( control ) {
+                                       control.priority( priority );
+                                       control.section( self.section() );
+                                       priority += 1;
+                               });
+                               self.priority( priority ); // Make sure sidebar control remains at end
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        return aIndex < bIndex ? -1 : 1;
-                               } );
-
-                               // Append the controls to put them in the right order
-                               finalControlContainers = _( widgetFormControls ).map( function( widgetFormControls ) {
-                                       return widgetFormControls.container[0];
-                               } );
-
-                               $sidebarWidgetsAddControl = self.$sectionContent.find( '.customize-control-sidebar_widgets' );
-                               $sidebarWidgetsAddControl.before( finalControlContainers );
-
</del><span class="cx" style="display: block; padding: 0 10px">                                 // Re-sort widget form controls (including widgets form other sidebars newly moved here)
</span><span class="cx" style="display: block; padding: 0 10px">                                self._applyCardinalOrderClassNames();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1434,39 +1496,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        // Update the model with whether or not the sidebar is rendered
</span><span class="cx" style="display: block; padding: 0 10px">                        self.active.bind( function ( active ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                registeredSidebar.set( 'is_rendered', active );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                api.section( self.section.get() ).active( active );
</ins><span class="cx" style="display: block; padding: 0 10px">                         } );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        api.section( self.section.get() ).active( self.active() );
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * Show the sidebar section when it becomes visible.
-                *
-                * Overrides api.Control.toggle()
-                *
-                * @param {Boolean} active
-                */
-               toggle: function ( active ) {
-                       var $section, sectionSelector;
-
-                       sectionSelector = '#accordion-section-sidebar-widgets-' + this.params.sidebar_id;
-                       $section = $( sectionSelector );
-
-                       if ( active ) {
-                               $section.stop().slideDown( function() {
-                                       $( this ).css( 'height', 'auto' ); // so that the .accordion-section-content won't overflow
-                               } );
-
-                       } else {
-                               // Make sure that hidden sections get closed first
-                               if ( $section.hasClass( 'open' ) ) {
-                                       // it would be nice if accordionSwitch() in accordion.js was public
-                                       $section.find( '.accordion-section-title' ).trigger( 'click' );
-                               }
-
-                               $section.stop().slideUp();
-                       }
-               },
-
-               /**
</del><span class="cx" style="display: block; padding: 0 10px">                  * Allow widgets in sidebar to be re-ordered, and for the order to be previewed
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                _setupSortable: function() {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1500,12 +1535,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        this.$controlSection.find( '.accordion-section-title' ).droppable({
</span><span class="cx" style="display: block; padding: 0 10px">                                accept: '.customize-control-widget_form',
</span><span class="cx" style="display: block; padding: 0 10px">                                over: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        if ( ! self.$controlSection.hasClass( 'open' ) ) {
-                                               self.$controlSection.addClass( 'open' );
-                                               self.$sectionContent.toggle( false ).slideToggle( 150, function() {
-                                                       self.$sectionContent.sortable( 'refreshPositions' );
-                                               } );
-                                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 var section = api.section( self.section.get() );
+                                       section.expand({
+                                               allowMultiple: true, // Prevent the section being dragged from to be collapsed
+                                               completeCallback: function () {
+                                                       // @todo It is not clear when refreshPositions should be called on which sections, or if it is even needed
+                                                       api.section.each( function ( otherSection ) {
+                                                               if ( otherSection.container.find( '.customize-control-sidebar_widgets' ).length ) {
+                                                                       otherSection.container.find( '.accordion-section-content:first' ).sortable( 'refreshPositions' );
+                                                               }
+                                                       } );
+                                               }
+                                       });
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><span class="cx" style="display: block; padding: 0 10px">                        });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1548,16 +1589,30 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * Add classes to the widget_form controls to assist with styling
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                _applyCardinalOrderClassNames: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.$sectionContent.find( '.customize-control-widget_form' )
-                               .removeClass( 'first-widget' )
-                               .removeClass( 'last-widget' )
-                               .find( '.move-widget-down, .move-widget-up' ).prop( 'tabIndex', 0 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var widgetControls = [];
+                       _.each( this.setting(), function ( widgetId ) {
+                               var widgetControl = api.Widgets.getWidgetFormControlForWidget( widgetId );
+                               if ( widgetControl ) {
+                                       widgetControls.push( widgetControl );
+                               }
+                       });
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.$sectionContent.find( '.customize-control-widget_form:first' )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( ! widgetControls.length ) {
+                               return;
+                       }
+
+                       $( widgetControls ).each( function () {
+                               $( this.container )
+                                       .removeClass( 'first-widget' )
+                                       .removeClass( 'last-widget' )
+                                       .find( '.move-widget-down, .move-widget-up' ).prop( 'tabIndex', 0 );
+                       });
+
+                       _.first( widgetControls ).container
</ins><span class="cx" style="display: block; padding: 0 10px">                                 .addClass( 'first-widget' )
</span><span class="cx" style="display: block; padding: 0 10px">                                .find( '.move-widget-up' ).prop( 'tabIndex', -1 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.$sectionContent.find( '.customize-control-widget_form:last' )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 _.last( widgetControls ).container
</ins><span class="cx" style="display: block; padding: 0 10px">                                 .addClass( 'last-widget' )
</span><span class="cx" style="display: block; padding: 0 10px">                                .find( '.move-widget-down' ).prop( 'tabIndex', -1 );
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1571,6 +1626,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * Enable/disable the reordering UI
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param {Boolean} showOrHide to enable/disable reordering
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 *
+                * @todo We should have a reordering state instead and rename this to onChangeReordering
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><span class="cx" style="display: block; padding: 0 10px">                toggleReordering: function( showOrHide ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        showOrHide = Boolean( showOrHide );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1584,7 +1641,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( showOrHide ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                _( this.getWidgetFormControls() ).each( function( formControl ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        formControl.collapseForm();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 formControl.collapse();
</ins><span class="cx" style="display: block; padding: 0 10px">                                 } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                this.$sectionContent.find( '.first-widget .move-widget' ).focus();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1619,7 +1676,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * @returns {object|false} widget_form control instance, or false on error
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><span class="cx" style="display: block; padding: 0 10px">                addWidget: function( widgetId ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        var self = this, controlHtml, $widget, controlType = 'widget_form', $control, controlConstructor,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var self = this, controlHtml, $widget, controlType = 'widget_form', controlContainer, controlConstructor,
</ins><span class="cx" style="display: block; padding: 0 10px">                                 parsedWidgetId = parseWidgetId( widgetId ),
</span><span class="cx" style="display: block; padding: 0 10px">                                widgetNumber = parsedWidgetId.number,
</span><span class="cx" style="display: block; padding: 0 10px">                                widgetIdBase = parsedWidgetId.id_base,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1651,31 +1708,29 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $widget = $( controlHtml );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $control = $( '<li/>' )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 controlContainer = $( '<li/>' )
</ins><span class="cx" style="display: block; padding: 0 10px">                                 .addClass( 'customize-control' )
</span><span class="cx" style="display: block; padding: 0 10px">                                .addClass( 'customize-control-' + controlType )
</span><span class="cx" style="display: block; padding: 0 10px">                                .append( $widget );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Remove icon which is visible inside the panel
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $control.find( '> .widget-icon' ).remove();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 controlContainer.find( '> .widget-icon' ).remove();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( widget.get( 'is_multi' ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $control.find( 'input[name="widget_number"]' ).val( widgetNumber );
-                               $control.find( 'input[name="multi_number"]' ).val( widgetNumber );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         controlContainer.find( 'input[name="widget_number"]' ).val( widgetNumber );
+                               controlContainer.find( 'input[name="multi_number"]' ).val( widgetNumber );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        widgetId = $control.find( '[name="widget-id"]' ).val();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 widgetId = controlContainer.find( '[name="widget-id"]' ).val();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $control.hide(); // to be slid-down below
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 controlContainer.hide(); // to be slid-down below
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        settingId = 'widget_' + widget.get( 'id_base' );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( widget.get( 'is_multi' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                settingId += '[' + widgetNumber + ']';
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $control.attr( 'id', 'customize-control-' + settingId.replace( /\]/g, '' ).replace( /\[/g, '-' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 controlContainer.attr( 'id', 'customize-control-' + settingId.replace( /\]/g, '' ).replace( /\[/g, '-' ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this.container.after( $control );
-
</del><span class="cx" style="display: block; padding: 0 10px">                         // Only create setting if it doesn't already exist (if we're adding a pre-existing inactive widget)
</span><span class="cx" style="display: block; padding: 0 10px">                        isExistingWidget = api.has( settingId );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! isExistingWidget ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1692,6 +1747,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        settings: {
</span><span class="cx" style="display: block; padding: 0 10px">                                                'default': settingId
</span><span class="cx" style="display: block; padding: 0 10px">                                        },
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                        content: controlContainer,
</ins><span class="cx" style="display: block; padding: 0 10px">                                         sidebar_id: self.params.sidebar_id,
</span><span class="cx" style="display: block; padding: 0 10px">                                        widget_id: widgetId,
</span><span class="cx" style="display: block; padding: 0 10px">                                        widget_id_base: widget.get( 'id_base' ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1731,9 +1787,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                this.setting( sidebarWidgets );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $control.slideDown( function() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 controlContainer.slideDown( function() {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 if ( isExistingWidget ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        widgetFormControl.expandForm();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 widgetFormControl.expand();
</ins><span class="cx" style="display: block; padding: 0 10px">                                         widgetFormControl.updateWidget( {
</span><span class="cx" style="display: block; padding: 0 10px">                                                instance: widgetFormControl.setting(),
</span><span class="cx" style="display: block; padding: 0 10px">                                                complete: function( error ) {
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizecontrolphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-control.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-control.php      2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-includes/class-wp-customize-control.php        2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -74,6 +74,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $input_attrs = array();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @deprecated It is better to just call the json() method
</ins><span class="cx" style="display: block; padding: 0 10px">          * @access public
</span><span class="cx" style="display: block; padding: 0 10px">         * @var array
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -218,12 +219,27 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->json['type']        = $this->type;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->json['priority']    = $this->priority;
+               $this->json['active']      = $this->active();
+               $this->json['section']     = $this->section;
+               $this->json['content']     = $this->get_content();
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->json['label']       = $this->label;
</span><span class="cx" style="display: block; padding: 0 10px">                $this->json['description'] = $this->description;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->json['active']      = $this->active();
</del><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Get the data to export to the client via JSON.
+        *
+        * @since 4.1.0
+        *
+        * @return array
+        */
+       public function json() {
+               $this->to_json();
+               return $this->json;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Check if the theme supports the control and check user capabilities.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 3.4.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -244,6 +260,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Get the control's content for insertion into the Customizer pane.
+        *
+        * @since 4.1.0
+        *
+        * @return string
+        */
+       public final function get_content() {
+               ob_start();
+               $this->maybe_render();
+               $template = trim( ob_get_contents() );
+               ob_end_clean();
+               return $template;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Check capabilities and render the control.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 3.4.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1073,6 +1104,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Widget Area Customize Control Class
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @since 3.9.0
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class WP_Widget_Area_Customize_Control extends WP_Customize_Control {
</span><span class="cx" style="display: block; padding: 0 10px">        public $type = 'sidebar_widgets';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1114,6 +1146,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Widget Form Customize Control Class
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *
+ * @since 3.9.0
</ins><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class WP_Widget_Form_Customize_Control extends WP_Customize_Control {
</span><span class="cx" style="display: block; padding: 0 10px">        public $type = 'widget_form';
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizemanagerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-manager.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-manager.php      2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-includes/class-wp-customize-manager.php        2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -498,6 +498,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $settings = array(
</span><span class="cx" style="display: block; padding: 0 10px">                        'values'  => array(),
</span><span class="cx" style="display: block; padding: 0 10px">                        'channel' => wp_unslash( $_POST['customize_messenger_channel'] ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        'activePanels' => array(),
+                       'activeSections' => array(),
</ins><span class="cx" style="display: block; padding: 0 10px">                         'activeControls' => array(),
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -511,6 +513,12 @@
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $this->settings as $id => $setting ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $settings['values'][ $id ] = $setting->js_value();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                foreach ( $this->panels as $id => $panel ) {
+                       $settings['activePanels'][ $id ] = $panel->active();
+               }
+               foreach ( $this->sections as $id => $section ) {
+                       $settings['activeSections'][ $id ] = $section->active();
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">                 foreach ( $this->controls as $id => $control ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $settings['activeControls'][ $id ] = $control->active();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -911,11 +919,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $section->panel ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                // Top-level section.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $sections[] = $section;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $sections[ $section->id ] = $section;
</ins><span class="cx" style="display: block; padding: 0 10px">                         } else {
</span><span class="cx" style="display: block; padding: 0 10px">                                // This section belongs to a panel.
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( isset( $this->panels [ $section->panel ] ) ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        $this->panels[ $section->panel ]->sections[] = $section;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 $this->panels[ $section->panel ]->sections[ $section->id ] = $section;
</ins><span class="cx" style="display: block; padding: 0 10px">                                 }
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -932,8 +940,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                continue;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        usort( $panel->sections, array( $this, '_cmp_priority' ) );
-                       $panels[] = $panel;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 uasort( $panel->sections, array( $this, '_cmp_priority' ) );
+                       $panels[ $panel->id ] = $panel;
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">                $this->panels = $panels;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizepanelphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-panel.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-panel.php        2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-includes/class-wp-customize-panel.php  2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -83,6 +83,28 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $sections;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.1.0
+        * @access public
+        * @var string
+        */
+       public $type;
+
+       /**
+        * Callback.
+        *
+        * @since 4.1.0
+        * @access public
+        *
+        * @see WP_Customize_Section::active()
+        *
+        * @var callable Callback is called with one argument, the instance of
+        *               WP_Customize_Section, and returns bool to indicate whether
+        *               the section is active (such as it relates to the URL
+        *               currently being previewed).
+        */
+       public $active_callback = '';
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Constructor.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * Any supplied $args override class property defaults.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -103,6 +125,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->manager = $manager;
</span><span class="cx" style="display: block; padding: 0 10px">                $this->id = $id;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( empty( $this->active_callback ) ) {
+                       $this->active_callback = array( $this, 'active_callback' );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->sections = array(); // Users cannot customize the $sections array.
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -110,6 +135,60 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Check whether panel is active to current Customizer preview.
+        *
+        * @since 4.1.0
+        * @access public
+        *
+        * @return bool Whether the panel is active to the current preview.
+        */
+       public final function active() {
+               $panel = $this;
+               $active = call_user_func( $this->active_callback, $this );
+
+               /**
+                * Filter response of WP_Customize_Panel::active().
+                *
+                * @since 4.1.0
+                *
+                * @param bool                 $active  Whether the Customizer panel is active.
+                * @param WP_Customize_Panel $panel WP_Customize_Panel instance.
+                */
+               $active = apply_filters( 'customize_panel_active', $active, $panel );
+
+               return $active;
+       }
+
+       /**
+        * Default callback used when invoking WP_Customize_Panel::active().
+        *
+        * Subclasses can override this with their specific logic, or they may
+        * provide an 'active_callback' argument to the constructor.
+        *
+        * @since 4.1.0
+        * @access public
+        *
+        * @return bool Always true.
+        */
+       public function active_callback() {
+               return true;
+       }
+
+       /**
+        * Gather the parameters passed to client JavaScript via JSON.
+        *
+        * @since 4.1.0
+        *
+        * @return array The array to be exported to the client as JSON
+        */
+       public function json() {
+               $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'type' ) );
+               $array['content'] = $this->get_content();
+               $array['active'] = $this->active();
+               return $array;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Checks required user capabilities and whether the theme has the
</span><span class="cx" style="display: block; padding: 0 10px">         * feature support required by the panel.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -130,6 +209,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Get the panel's content template for insertion into the Customizer pane.
+        *
+        * @since 4.1.0
+        *
+        * @return string
+        */
+       public final function get_content() {
+               ob_start();
+               $this->maybe_render();
+               $template = trim( ob_get_contents() );
+               ob_end_clean();
+               return $template;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Check capabilities and render the panel.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.0.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -189,7 +283,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        protected function render_content() {
</span><span class="cx" style="display: block; padding: 0 10px">                ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <li class="accordion-section control-section<?php if ( empty( $this->description ) ) echo ' cannot-expand'; ?>">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <li class="panel-meta accordion-section control-section<?php if ( empty( $this->description ) ) { echo ' cannot-expand'; } ?>">
</ins><span class="cx" style="display: block; padding: 0 10px">                         <div class="accordion-section-title" tabindex="0">
</span><span class="cx" style="display: block; padding: 0 10px">                                <span class="preview-notice"><?php
</span><span class="cx" style="display: block; padding: 0 10px">                                        /* translators: %s is the site/panel title in the Customizer */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -203,8 +297,5 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                </li>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( $this->sections as $section ) {
-                       $section->maybe_render();
-               }
</del><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizesectionphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-section.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-section.php      2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-includes/class-wp-customize-section.php        2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -92,6 +92,28 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $controls;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @since 4.1.0
+        * @access public
+        * @var string
+        */
+       public $type;
+
+       /**
+        * Callback.
+        *
+        * @since 4.1.0
+        * @access public
+        *
+        * @see WP_Customize_Section::active()
+        *
+        * @var callable Callback is called with one argument, the instance of
+        *               WP_Customize_Section, and returns bool to indicate whether
+        *               the section is active (such as it relates to the URL
+        *               currently being previewed).
+        */
+       public $active_callback = '';
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Constructor.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * Any supplied $args override class property defaults.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -105,12 +127,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public function __construct( $manager, $id, $args = array() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $keys = array_keys( get_object_vars( $this ) );
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $keys as $key ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( isset( $args[ $key ] ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( isset( $args[ $key ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $this->$key = $args[ $key ];
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        }
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->manager = $manager;
</span><span class="cx" style="display: block; padding: 0 10px">                $this->id = $id;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( empty( $this->active_callback ) ) {
+                       $this->active_callback = array( $this, 'active_callback' );
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $this->controls = array(); // Users cannot customize the $controls array.
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -118,6 +144,60 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Check whether section is active to current Customizer preview.
+        *
+        * @since 4.1.0
+        * @access public
+        *
+        * @return bool Whether the section is active to the current preview.
+        */
+       public final function active() {
+               $section = $this;
+               $active = call_user_func( $this->active_callback, $this );
+
+               /**
+                * Filter response of WP_Customize_Section::active().
+                *
+                * @since 4.1.0
+                *
+                * @param bool                 $active  Whether the Customizer section is active.
+                * @param WP_Customize_Section $section WP_Customize_Section instance.
+                */
+               $active = apply_filters( 'customize_section_active', $active, $section );
+
+               return $active;
+       }
+
+       /**
+        * Default callback used when invoking WP_Customize_Section::active().
+        *
+        * Subclasses can override this with their specific logic, or they may
+        * provide an 'active_callback' argument to the constructor.
+        *
+        * @since 4.1.0
+        * @access public
+        *
+        * @return bool Always true.
+        */
+       public function active_callback() {
+               return true;
+       }
+
+       /**
+        * Gather the parameters passed to client JavaScript via JSON.
+        *
+        * @since 4.1.0
+        *
+        * @return array The array to be exported to the client as JSON
+        */
+       public function json() {
+               $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'panel', 'type' ) );
+               $array['content'] = $this->get_content();
+               $array['active'] = $this->active();
+               return $array;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Checks required user capabilities and whether the theme has the
</span><span class="cx" style="display: block; padding: 0 10px">         * feature support required by the section.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -126,23 +206,41 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return bool False if theme doesn't support the section or user doesn't have the capability.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public final function check_capabilities() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $this->capability && ! call_user_func_array( 'current_user_can', (array) $this->capability ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return false;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $this->theme_supports && ! call_user_func_array( 'current_theme_supports', (array) $this->theme_supports ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return false;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return true;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Get the section's content template for insertion into the Customizer pane.
+        *
+        * @since 4.1.0
+        *
+        * @return string
+        */
+       public final function get_content() {
+               ob_start();
+               $this->maybe_render();
+               $template = trim( ob_get_contents() );
+               ob_end_clean();
+               return $template;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Check capabilities and render the section.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 3.4.0
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public final function maybe_render() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! $this->check_capabilities() )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! $this->check_capabilities() ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         return;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * Fires before rendering a Customizer section.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -172,9 +270,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        protected function render() {
</span><span class="cx" style="display: block; padding: 0 10px">                $classes = 'control-section accordion-section';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $this->panel ) {
-                       $classes .= ' control-subsection';
-               }
</del><span class="cx" style="display: block; padding: 0 10px">                 ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
</span><span class="cx" style="display: block; padding: 0 10px">                        <h3 class="accordion-section-title" tabindex="0">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -183,12 +278,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        </h3>
</span><span class="cx" style="display: block; padding: 0 10px">                        <ul class="accordion-section-content">
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php if ( ! empty( $this->description ) ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <li><p class="description customize-section-description"><?php echo $this->description; ?></p></li>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <li class="customize-section-description-container">
+                                               <p class="description customize-section-description"><?php echo $this->description; ?></p>
+                                       </li>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <?php endif; ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <?php
-                               foreach ( $this->controls as $control )
-                                       $control->maybe_render();
-                               ?>
</del><span class="cx" style="display: block; padding: 0 10px">                         </ul>
</span><span class="cx" style="display: block; padding: 0 10px">                </li>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php
</span></span></pre></div>
<a id="trunksrcwpincludesjscustomizebasejs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/js/customize-base.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/customize-base.js        2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-includes/js/customize-base.js  2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -184,8 +184,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        to = this.validate( to );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        // Bail if the sanitized value is null or unchanged.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( null === to || _.isEqual( from, to ) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( null === to || _.isEqual( from, to ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 return this;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        this._value = to;
</span><span class="cx" style="display: block; padding: 0 10px">                        this._dirty = true;
</span></span></pre></div>
<a id="trunksrcwpincludesjscustomizepreviewjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/js/customize-preview.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/js/customize-preview.js     2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/src/wp-includes/js/customize-preview.js       2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -107,6 +107,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">         });
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                preview.send( 'ready', {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        activePanels: api.settings.activePanels,
+                       activeSections: api.settings.activeSections,
</ins><span class="cx" style="display: block; padding: 0 10px">                         activeControls: api.settings.activeControls
</span><span class="cx" style="display: block; padding: 0 10px">                } );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="trunktestsqunitindexhtml"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/index.html</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/index.html      2014-10-29 22:35:27 UTC (rev 30101)
+++ trunk/tests/qunit/index.html        2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8,7 +8,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">   <script src="../../src/wp-includes/js/underscore.min.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">   <script src="../../src/wp-includes/js/backbone.min.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">   <script src="../../src/wp-includes/js/zxcvbn.min.js"></script>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">   <!-- QUnit -->
</span><span class="cx" style="display: block; padding: 0 10px">   <link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen" />
</span><span class="cx" style="display: block; padding: 0 10px">   <script src="vendor/qunit.js"></script>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,14 +28,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">     <!-- Tested files -->
</span><span class="cx" style="display: block; padding: 0 10px">     <script src="../../src/wp-admin/js/password-strength-meter.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    <script src="../../src/wp-includes/js/customize-base.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px">     <script src="../../src/wp-includes/js/customize-models.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">     <script src="../../src/wp-includes/js/shortcode.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">     <!-- Unit tests -->
</span><span class="cx" style="display: block; padding: 0 10px">     <script src="wp-admin/js/password-strength-meter.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    <script src="wp-admin/js/customize-base.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px">     <script src="wp-admin/js/customize-header.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">     <script src="wp-includes/js/shortcode.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">   </div>
</span><span class="cx" style="display: block; padding: 0 10px"> </body>
</span><span class="cx" style="display: block; padding: 0 10px"> </html>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del></span></pre></div>
<a id="trunktestsqunitwpadminjscustomizebasejs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/qunit/wp-admin/js/customize-base.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/wp-admin/js/customize-base.js                           (rev 0)
+++ trunk/tests/qunit/wp-admin/js/customize-base.js     2014-10-29 22:50:21 UTC (rev 30102)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,85 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/* global wp */
+
+jQuery( function( $ ) {
+       var FooSuperClass, BarSubClass, foo, bar;
+
+       module( 'Customize Base: Class' );
+
+       FooSuperClass = wp.customize.Class.extend(
+               {
+                       initialize: function ( instanceProps ) {
+                               $.extend( this, instanceProps || {} );
+                       },
+                       protoProp: 'protoPropValue'
+               },
+               {
+                       staticProp: 'staticPropValue'
+               }
+       );
+       test( 'FooSuperClass is a function ', function () {
+               equal( typeof FooSuperClass, 'function' );
+       });
+       test( 'FooSuperClass prototype has protoProp', function () {
+               equal( FooSuperClass.prototype.protoProp, 'protoPropValue' );
+       });
+       test( 'FooSuperClass does not have protoProp', function () {
+               equal( typeof FooSuperClass.protoProp, 'undefined' );
+       });
+       test( 'FooSuperClass has staticProp', function () {
+               equal( FooSuperClass.staticProp, 'staticPropValue' );
+       });
+       test( 'FooSuperClass prototype does not have staticProp', function () {
+               equal( typeof FooSuperClass.prototype.staticProp, 'undefined' );
+       });
+
+       foo = new FooSuperClass( { instanceProp: 'instancePropValue' } );
+       test( 'FooSuperClass instance foo extended Class', function () {
+               equal( foo.extended( wp.customize.Class ), true );
+       });
+       test( 'foo instance has protoProp', function () {
+               equal( foo.protoProp, 'protoPropValue' );
+       });
+       test( 'foo instance does not have staticProp', function () {
+               equal( typeof foo.staticProp, 'undefined' );
+       });
+       test( 'FooSuperClass instance foo ran initialize() and has supplied instanceProp', function () {
+               equal( foo.instanceProp, 'instancePropValue' );
+       });
+
+       // @todo Test Class.constructor() manipulation
+       // @todo Test Class.applicator?
+       // @todo do we test object.instance?
+
+
+       module( 'Customize Base: Subclass' );
+
+       BarSubClass = FooSuperClass.extend(
+               {
+                       initialize: function ( instanceProps ) {
+                               FooSuperClass.prototype.initialize.call( this, instanceProps );
+                               this.subInstanceProp = 'subInstancePropValue';
+                       },
+                       subProtoProp: 'subProtoPropValue'
+               },
+               {
+                       subStaticProp: 'subStaticPropValue'
+               }
+       );
+       test( 'BarSubClass prototype has subProtoProp', function () {
+               equal( BarSubClass.prototype.subProtoProp, 'subProtoPropValue' );
+       });
+       test( 'BarSubClass prototype has parent FooSuperClass protoProp', function () {
+               equal( BarSubClass.prototype.protoProp, 'protoPropValue' );
+       });
+
+       bar = new BarSubClass( { instanceProp: 'instancePropValue' } );
+       test( 'BarSubClass instance bar its initialize() and parent initialize() run', function () {
+               equal( bar.instanceProp, 'instancePropValue' );
+               equal( bar.subInstanceProp, 'subInstancePropValue' );
+       });
+
+       test( 'BarSubClass instance bar extended FooSuperClass', function () {
+               equal( bar.extended( FooSuperClass ), true );
+       });
+
+});
</ins></span></pre>
</div>
</div>

</body>
</html>