<!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>[32658] trunk: Add JS templates for Customizer Panels and Sections.</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/32658">32658</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/32658","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>westonruter</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2015-05-30 00:02:13 +0000 (Sat, 30 May 2015)</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'>Add JS templates for Customizer Panels and Sections.

This extends the approach taken for Customizer Controls in <a href="https://core.trac.wordpress.org/ticket/29572">#29572</a>.

Props celloexpressions, westonruter, ocean90.
See <a href="https://core.trac.wordpress.org/ticket/30737">#30737</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincustomizephp">trunk/src/wp-admin/customize.php</a></li>
<li><a href="#trunksrcwpadminjscustomizecontrolsjs">trunk/src/wp-admin/js/customize-controls.js</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="#trunktestsqunitfixturescustomizesettingsjs">trunk/tests/qunit/fixtures/customize-settings.js</a></li>
<li><a href="#trunktestsqunitindexhtml">trunk/tests/qunit/index.html</a></li>
<li><a href="#trunktestsqunitwpadminjscustomizecontrolsjs">trunk/tests/qunit/wp-admin/js/customize-controls.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsphpunittestscustomizepanelphp">trunk/tests/phpunit/tests/customize/panel.php</a></li>
<li><a href="#trunktestsphpunittestscustomizesectionphp">trunk/tests/phpunit/tests/customize/section.php</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  2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/src/wp-admin/customize.php    2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -175,7 +175,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">        <div id="customize-preview" class="wp-full-overlay-main"></div>
</span><span class="cx" style="display: block; padding: 0 10px">        <?php
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        // Render control templates.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ // Render Panel, Section, and Control templates.
+       $wp_customize->render_panel_templates();
+       $wp_customize->render_section_templates();
</ins><span class="cx" style="display: block; padding: 0 10px">         $wp_customize->render_control_templates();
</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">@@ -259,28 +261,38 @@
</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="cx" style="display: block; padding: 0 10px">        foreach ( $wp_customize->settings() as $id => $setting ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $settings['settings'][ $id ] = array(
-                       'value'     => $setting->js_value(),
-                       'transport' => $setting->transport,
-                       'dirty'     => $setting->dirty,
-               );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $setting->check_capabilities() ) {
+                       $settings['settings'][ $id ] = array(
+                               'value'     => $setting->js_value(),
+                               'transport' => $setting->transport,
+                               'dirty'     => $setting->dirty,
+                       );
+               }
</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 Control objects to pass to JavaScript.
</span><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">-                $settings['controls'][ $id ] = $control->json();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $control->check_capabilities() ) {
+                       $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><span class="cx" style="display: block; padding: 0 10px">        // Prepare Customize Section objects to pass to JavaScript.
</span><span class="cx" style="display: block; padding: 0 10px">        foreach ( $wp_customize->sections() as $id => $section ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $settings['sections'][ $id ] = $section->json();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( $section->check_capabilities() ) {
+                       $settings['sections'][ $id ] = $section->json();
+               }
</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 Panel objects to pass to JavaScript.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        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();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ foreach ( $wp_customize->panels() as $panel_id => $panel ) {
+               if ( $panel->check_capabilities() ) {
+                       $settings['panels'][ $panel_id ] = $panel->json();
+                       foreach ( $panel->sections as $section_id => $section ) {
+                               if ( $section->check_capabilities() ) {
+                                       $settings['sections'][ $section_id ] = $section->json();
+                               }
+                       }
</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></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       2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/src/wp-admin/js/customize-controls.js 2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -156,6 +156,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        Container = api.Class.extend({
</span><span class="cx" style="display: block; padding: 0 10px">                defaultActiveArguments: { duration: 'fast', completeCallback: $.noop },
</span><span class="cx" style="display: block; padding: 0 10px">                defaultExpandedArguments: { duration: 'fast', completeCallback: $.noop },
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                containerType: 'container',
</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">                 * @since 4.1.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -168,7 +169,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        container.id = id;
</span><span class="cx" style="display: block; padding: 0 10px">                        container.params = {};
</span><span class="cx" style="display: block; padding: 0 10px">                        $.extend( container, options || {} );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        container.templateSelector = 'customize-' + container.containerType + '-' + container.params.type;
</ins><span class="cx" style="display: block; padding: 0 10px">                         container.container = $( container.params.content );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        if ( 0 === container.container.length ) {
+                               container.container = $( container.getContainer() );
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        container.deferred = {
</span><span class="cx" style="display: block; padding: 0 10px">                                embedded: new $.Deferred()
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -191,7 +196,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                container.onChangeExpanded( expanded, args );
</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">-                        container.attachEvents();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 container.deferred.embedded.done( function () {
+                               container.attachEvents();
+                       });
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        api.utils.bubbleChildValueChanges( container, [ 'priority', 'active' ] );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -366,7 +373,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 * Bring the container into view and then expand this and bring it into view
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param {Object} [params]
</span><span class="cx" style="display: block; padding: 0 10px">                 */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                focus: focus
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         focus: focus,
+
+               /**
+                * Return the container html, generated from its JS template, if it exists.
+                *
+                * @since 4.3.0
+                */
+               getContainer: function () {
+                       var template,
+                               container = this;
+
+                       if ( 0 !== $( '#tmpl-' + container.templateSelector ).length ) {
+                               template = wp.template( container.templateSelector );
+                               if ( template && container.container ) {
+                                       return $.trim( template( container.params ) );
+                               }
+                       }
+
+                       return '<li></li>';
+               }
</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">@@ -376,6 +402,7 @@
</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><span class="cx" style="display: block; padding: 0 10px">        api.Section = Container.extend({
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                containerType: '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">                 * @since 4.1.0
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -977,6 +1004,8 @@
</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><span class="cx" style="display: block; padding: 0 10px">        api.Panel = Container.extend({
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                containerType: 'panel',
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 /**
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 4.1.0
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1003,6 +1032,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! panel.container.parent().is( parentContainer ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                parentContainer.append( panel.container );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                panel.renderContent();
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                        panel.deferred.embedded.resolve();
</span><span class="cx" style="display: block; padding: 0 10px">                },
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1045,6 +1075,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                                event.preventDefault(); // Keep this AFTER the key filter above
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                meta = panel.container.find( '.panel-meta' );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 if ( meta.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="lines" style="display: block; padding: 0 10px; color: #888">@@ -1169,6 +1200,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                panelTitle.focus();
</span><span class="cx" style="display: block; padding: 0 10px">                                container.scrollTop( 0 );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                },
+
+               /**
+                * Render the panel from its JS template, if it exists.
+                *
+                * The panel's container must already exist in the DOM.
+                *
+                * @since 4.3.0
+                */
+               renderContent: function () {
+                       var template,
+                               panel = this;
+
+                       // Add the content to the container.
+                       if ( 0 !== $( '#tmpl-' + panel.templateSelector + '-content' ).length ) {
+                               template = wp.template( panel.templateSelector + '-content' );
+                               if ( template && panel.container ) {
+                                       panel.container.find( '.accordion-sub-container' ).html( template( panel.params ) );
+                               }
+                       }
</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></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      2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/src/wp-includes/class-wp-customize-manager.php        2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -60,8 +60,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">        protected $customized;
</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">-         * Controls that may be rendered from JS templates.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Panel types that may be rendered from JS templates.
</ins><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.3.0
+        * @access protected
+        * @var array
+        */
+       protected $registered_panel_types = array();
+
+       /**
+        * Section types that may be rendered from JS templates.
+        *
+        * @since 4.3.0
+        * @access protected
+        * @var array
+        */
+       protected $registered_section_types = array();
+
+       /**
+        * Control types that may be rendered from JS templates.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 4.1.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @access protected
</span><span class="cx" style="display: block; padding: 0 10px">         * @var array
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -612,19 +630,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">                foreach ( $this->settings as $id => $setting ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $settings['values'][ $id ] = $setting->js_value();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( $setting->check_capabilities() ) {
+                               $settings['values'][ $id ] = $setting->js_value();
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                foreach ( $this->panels as $id => $panel ) {
-                       $settings['activePanels'][ $id ] = $panel->active();
-                       foreach ( $panel->sections as $id => $section ) {
-                               $settings['activeSections'][ $id ] = $section->active();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         foreach ( $this->panels as $panel_id => $panel ) {
+                       if ( $panel->check_capabilities() ) {
+                               $settings['activePanels'][ $panel_id ] = $panel->active();
+                               foreach ( $panel->sections as $section_id => $section ) {
+                                       if ( $section->check_capabilities() ) {
+                                               $settings['activeSections'][ $section_id ] = $section->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">                foreach ( $this->sections as $id => $section ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $settings['activeSections'][ $id ] = $section->active();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( $section->check_capabilities() ) {
+                               $settings['activeSections'][ $id ] = $section->active();
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $this->controls as $id => $control ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $settings['activeControls'][ $id ] = $control->active();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( $control->check_capabilities() ) {
+                               $settings['activeControls'][ $id ] = $control->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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -965,6 +993,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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Register a customize panel type.
+        *
+        * Registered types are eligible to be rendered via JS and created dynamically.
+        *
+        * @since 4.3.0
+        * @access public
+        *
+        * @param string $panel Name of a custom panel which is a subclass of
+        *                        {@see WP_Customize_Panel}.
+        */
+       public function register_panel_type( $panel ) {
+               $this->registered_panel_types[] = $panel;
+       }
+
+       /**
+        * Render JS templates for all registered panel types.
+        *
+        * @since 4.3.0
+        * @access public
+        */
+       public function render_panel_templates() {
+               foreach ( $this->registered_panel_types as $panel_type ) {
+                       $panel = new $panel_type( $this, 'temp', array() );
+                       $panel->print_template();
+               }
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Add a customize 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="lines" style="display: block; padding: 0 10px; color: #888">@@ -1006,6 +1062,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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Register a customize section type.
+        *
+        * Registered types are eligible to be rendered via JS and created dynamically.
+        *
+        * @since 4.3.0
+        * @access public
+        *
+        * @param string $section Name of a custom section which is a subclass of
+        *                        {@see WP_Customize_Section}.
+        */
+       public function register_section_type( $section ) {
+               $this->registered_section_types[] = $section;
+       }
+
+       /**
+        * Render JS templates for all registered section types.
+        *
+        * @since 4.3.0
+        * @access public
+        */
+       public function render_section_templates() {
+               foreach ( $this->registered_section_types as $section_type ) {
+                       $section = new $section_type( $this, 'temp', array() );
+                       $section->print_template();
+               }
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Add a customize 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">@@ -1176,7 +1260,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function register_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">-                /* Control Types (custom control classes) */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         /* Panel, Section, and Control Types */
+               $this->register_panel_type( 'WP_Customize_Panel' );
+               $this->register_section_type( 'WP_Customize_Section' );
+               $this->register_section_type( 'WP_Customize_Sidebar_Section' );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->register_control_type( 'WP_Customize_Color_Control' );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->register_control_type( 'WP_Customize_Media_Control' );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->register_control_type( 'WP_Customize_Upload_Control' );
</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        2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/src/wp-includes/class-wp-customize-panel.php  2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -214,7 +214,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array The array to be exported to the client as JSON.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function json() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'type' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $array = wp_array_slice_assoc( (array) $this, array( 'id', 'title', 'description', 'priority', 'type' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $array['content'] = $this->get_content();
</span><span class="cx" style="display: block; padding: 0 10px">                $array['active'] = $this->active();
</span><span class="cx" style="display: block; padding: 0 10px">                $array['instanceNumber'] = $this->instance_number;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -289,48 +289,92 @@
</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">-         * Render the panel container, and then its contents.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Render the panel container, and then its contents (via `this->render_content()`) in a subclass.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Panel containers are now rendered in JS by default, see {@see WP_Customize_Panel::print_template()}.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 4.0.0
</span><span class="cx" style="display: block; padding: 0 10px">         * @access protected
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        protected function render() {
-               $classes = 'accordion-section control-section control-panel control-panel-' . $this->type;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected function render() {}
+
+       /**
+        * Render the panel UI in a subclass.
+        *
+        * Panel contents are now rendered in JS by default, see {@see WP_Customize_Panel::print_template()}.
+        *
+        * @since 4.1.0
+        * @access protected
+        */
+       protected function render_content() {}
+
+       /**
+        * Render the panel's JS templates.
+        *
+        * This function is only run for panel types that have been registered with
+        * {@see WP_Customize_Manager::register_panel_type()}.
+        *
+        * @since 4.3.0
+        */
+       public function print_template() {
</ins><span class="cx" style="display: block; padding: 0 10px">                 ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <li id="accordion-panel-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <script type="text/html" id="tmpl-customize-panel-<?php echo esc_attr( $this->type ); ?>-content">
+                       <?php $this->content_template(); ?>
+               </script>
+               <script type="text/html" id="tmpl-customize-panel-<?php echo esc_attr( $this->type ); ?>">
+                       <?php $this->render_template(); ?>
+               </script>
+        <?php
+       }
+
+       /**
+        * An Underscore (JS) template for rendering this panel's container.
+        *
+        * Class variables for this panel class are available in the `data` JS object;
+        * export custom variables by overriding {@see WP_Customize_Panel::json()}.
+        *
+        * @see WP_Customize_Panel::print_template()
+        *
+        * @since 4.3.0
+        */
+       protected function render_template() {
+               ?>
+               <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
</ins><span class="cx" style="display: block; padding: 0 10px">                         <h3 class="accordion-section-title" tabindex="0">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <?php echo esc_html( $this->title ); ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         {{ data.title }}
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <span class="screen-reader-text"><?php _e( 'Press return or enter to open this panel' ); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                        </h3>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <ul class="accordion-sub-container control-panel-content">
-                               <?php $this->render_content(); ?>
-                       </ul>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <ul class="accordion-sub-container control-panel-content"></ul>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </li>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php
</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">-         * Render the sections that have been added to the panel.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * An Underscore (JS) template for this panel's content (but not its container).
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @since 4.1.0
-        * @access protected
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Class variables for this panel class are available in the `data` JS object;
+        * export custom variables by overriding {@see WP_Customize_Panel::json()}.
+        *
+        * @see WP_Customize_Panel::print_template()
+        *
+        * @since 4.3.0
</ins><span class="cx" style="display: block; padding: 0 10px">          */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        protected function render_content() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected function content_template() {
</ins><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="panel-meta customize-info accordion-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 customize-info accordion-section <# if ( ! data.description ) { #> cannot-expand<# } #>">
</ins><span class="cx" style="display: block; padding: 0 10px">                         <button class="customize-panel-back" tabindex="-1"><span class="screen-reader-text"><?php _e( 'Back' ); ?></span></button>
</span><span class="cx" style="display: block; padding: 0 10px">                        <div class="accordion-section-title">
</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><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">' . esc_html( $this->title ) . '</strong>' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">{{ data.title }}</strong>' );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                <button class="customize-help-toggle dashicons dashicons-editor-help" tabindex="0" aria-expanded="false"><span class="screen-reader-text"><?php _e( 'Help' ); ?></span></button>
</span><span class="cx" style="display: block; padding: 0 10px">                        </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <?php if ( ! empty( $this->description ) ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <# if ( data.description ) { #>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <div class="description customize-panel-description">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <?php echo $this->description; ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 {{{ data.description }}}
</ins><span class="cx" style="display: block; padding: 0 10px">                                 </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <?php endif; ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <# } #>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </li>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php
</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      2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/src/wp-includes/class-wp-customize-section.php        2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -223,10 +223,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return array The array to be exported to the client as JSON.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function json() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'panel', 'type' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $array = wp_array_slice_assoc( (array) $this, array( 'id', 'title', 'description', 'priority', 'panel', 'type' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 $array['content'] = $this->get_content();
</span><span class="cx" style="display: block; padding: 0 10px">                $array['active'] = $this->active();
</span><span class="cx" style="display: block; padding: 0 10px">                $array['instanceNumber'] = $this->instance_number;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               if ( $this->panel ) {
+                       /* translators: &#9656; is the unicode right-pointing triangle, and %s is the section title in the Customizer */
+                       $array['customizeAction'] = sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( $this->panel )->title ) );
+               } else {
+                       $array['customizeAction'] = __( 'Customizing' );
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 return $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">@@ -251,7 +259,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">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Get the section's content template for insertion into the Customizer pane.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Get the section's content for insertion into the Customizer pane.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.1.0
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -297,16 +305,45 @@
</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">-         * Render the section, and the controls that have been added to it.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Render the section UI in a subclass.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Sections are now rendered in JS by default, see {@see WP_Customize_Section::print_template()}.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @since 3.4.0
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        protected function render() {
-               $classes = 'accordion-section control-section control-section-' . $this->type;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ protected function render() {}
+
+       /**
+        * Render the section's JS template.
+        *
+        * This function is only run for section types that have been registered with
+        * {@see WP_Customize_Manager::register_section_type()}.
+        *
+        * @since 4.3.0
+        */
+       public function print_template() {
+        ?>
+               <script type="text/html" id="tmpl-customize-section-<?php echo $this->type; ?>">
+                       <?php $this->render_template(); ?>
+               </script>
+        <?php
+       }
+
+       /**
+        * An Underscore (JS) template for rendering this section.
+        *
+        * Class variables for this section class are available in the `data` JS object;
+        * export custom variables by overriding {@see WP_Customize_Section::json()}.
+        *
+        * @see WP_Customize_Section::print_template()
+        *
+        * @since 4.3.0
+        */
+       protected function render_template() {
</ins><span class="cx" style="display: block; padding: 0 10px">                 ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
</ins><span class="cx" style="display: block; padding: 0 10px">                         <h3 class="accordion-section-title" tabindex="0">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <?php echo esc_html( $this->title ); ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         {{ data.title }}
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <span class="screen-reader-text"><?php _e( 'Press return or enter to open' ); ?></span>
</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="lines" style="display: block; padding: 0 10px; color: #888">@@ -316,20 +353,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                                </button>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <h3>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        <span class="customize-action"><?php
-                                                               if ( $this->panel ) {
-                                                                       /* translators: &#9656; is the unicode right-pointing triangle, and %s is the section title in the Customizer */
-                                                                       echo sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( $this->panel )->title ) );
-                                                               } else {
-                                                                       _e( 'Customizing' );
-                                                               }
-                                                       ?></span>
-                                                       <?php echo esc_html( $this->title ); ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 <span class="customize-action">
+                                                               {{{ data.customizeAction }}}
+                                                       </span>
+                                                       {{ data.title }}
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 </h3>
</span><span class="cx" style="display: block; padding: 0 10px">                                        </div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <?php if ( ! empty( $this->description ) ) : ?>
-                                               <p class="description customize-section-description"><?php echo $this->description; ?></p>
-                                       <?php endif; ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <# if ( data.description ) { #>
+                                               <p class="description customize-section-description">{{{ data.description }}}</p>
+                                       <# } #>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 </li>
</span><span class="cx" style="display: block; padding: 0 10px">                        </ul>
</span><span class="cx" style="display: block; padding: 0 10px">                </li>
</span></span></pre></div>
<a id="trunktestsphpunittestscustomizepanelphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/customize/panel.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/customize/panel.php                             (rev 0)
+++ trunk/tests/phpunit/tests/customize/panel.php       2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,238 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Tests for the WP_Customize_Panel class.
+ *
+ * @group customize
+ */
+class Tests_WP_Customize_Panel extends WP_UnitTestCase {
+
+       /**
+        * @var WP_Customize_Manager
+        */
+       protected $manager;
+
+       function setUp() {
+               parent::setUp();
+               require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
+               $GLOBALS['wp_customize'] = new WP_Customize_Manager();
+               $this->manager = $GLOBALS['wp_customize'];
+               $this->undefined = new stdClass();
+       }
+
+       function tearDown() {
+               $this->manager = null;
+               unset( $GLOBALS['wp_customize'] );
+               parent::tearDown();
+       }
+
+       /**
+        * @see WP_Customize_Panel::__construct()
+        */
+       function test_construct_default_args() {
+               $panel = new WP_Customize_Panel( $this->manager, 'foo' );
+               $this->assertInternalType( 'int', $panel->instance_number );
+               $this->assertEquals( $this->manager, $panel->manager );
+               $this->assertEquals( 'foo', $panel->id );
+               $this->assertEquals( 160, $panel->priority );
+               $this->assertEquals( 'edit_theme_options', $panel->capability );
+               $this->assertEquals( '', $panel->theme_supports );
+               $this->assertEquals( '', $panel->title );
+               $this->assertEquals( '', $panel->description );
+               $this->assertEmpty( $panel->sections );
+               $this->assertEquals( 'default', $panel->type );
+               $this->assertEquals( array( $panel, 'active_callback' ), $panel->active_callback );
+       }
+
+       /**
+        * @see WP_Customize_Panel::__construct()
+        */
+       function test_construct_custom_args() {
+               $args = array(
+                       'priority' => 200,
+                       'capability' => 'edit_posts',
+                       'theme_supports' => 'html5',
+                       'title' => 'Hello World',
+                       'description' => 'Lorem Ipsum',
+                       'type' => 'horizontal',
+                       'active_callback' => '__return_true',
+               );
+
+               $panel = new WP_Customize_Panel( $this->manager, 'foo', $args );
+               foreach ( $args as $key => $value ) {
+                       $this->assertEquals( $value, $panel->$key );
+               }
+       }
+
+       /**
+        * @see WP_Customize_Panel::__construct()
+        */
+       function test_construct_custom_type() {
+               $panel = new Custom_Panel_Test( $this->manager, 'foo' );
+               $this->assertEquals( 'titleless', $panel->type );
+       }
+
+       /**
+        * @see WP_Customize_Panel::active()
+        * @see WP_Customize_Panel::active_callback()
+        */
+       function test_active() {
+               $panel = new WP_Customize_Panel( $this->manager, 'foo' );
+               $this->assertTrue( $panel->active() );
+
+               $panel = new WP_Customize_Panel( $this->manager, 'foo', array(
+                       'active_callback' => '__return_false',
+               ) );
+               $this->assertFalse( $panel->active() );
+               add_filter( 'customize_panel_active', array( $this, 'filter_active_test' ), 10, 2 );
+               $this->assertTrue( $panel->active() );
+       }
+
+       /**
+        * @param bool $active
+        * @param WP_Customize_Panel $panel
+        * @return bool
+        */
+       function filter_active_test( $active, $panel ) {
+               $this->assertFalse( $active );
+               $this->assertInstanceOf( 'WP_Customize_Panel', $panel );
+               $active = true;
+               return $active;
+       }
+
+       /**
+        * @see WP_Customize_Panel::json()
+        */
+       function test_json() {
+               $args = array(
+                       'priority' => 200,
+                       'capability' => 'edit_posts',
+                       'theme_supports' => 'html5',
+                       'title' => 'Hello World',
+                       'description' => 'Lorem Ipsum',
+                       'type' => 'horizontal',
+                       'active_callback' => '__return_true',
+               );
+               $panel = new WP_Customize_Panel( $this->manager, 'foo', $args );
+               $data = $panel->json();
+               $this->assertEquals( 'foo', $data['id'] );
+               foreach ( array( 'title', 'description', 'priority', 'type' ) as $key ) {
+                       $this->assertEquals( $args[ $key ], $data[ $key ] );
+               }
+               $this->assertEmpty( $data['content'] );
+               $this->assertTrue( $data['active'] );
+               $this->assertInternalType( 'int', $data['instanceNumber'] );
+       }
+
+       /**
+        * @see WP_Customize_Panel::check_capabilities()
+        */
+       function test_check_capabilities() {
+               $user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+               wp_set_current_user( $user_id );
+
+               $panel = new WP_Customize_Panel( $this->manager, 'foo' );
+               $this->assertTrue( $panel->check_capabilities() );
+               $old_cap = $panel->capability;
+               $panel->capability = 'do_not_allow';
+               $this->assertFalse( $panel->check_capabilities() );
+               $panel->capability = $old_cap;
+               $this->assertTrue( $panel->check_capabilities() );
+               $panel->theme_supports = 'impossible_feature';
+               $this->assertFalse( $panel->check_capabilities() );
+       }
+
+       /**
+        * @see WP_Customize_Panel::get_content()
+        */
+       function test_get_content() {
+               $panel = new WP_Customize_Panel( $this->manager, 'foo' );
+               $this->assertEmpty( $panel->get_content() );
+       }
+
+       /**
+        * @see WP_Customize_Panel::maybe_render()
+        */
+       function test_maybe_render() {
+               wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
+               $panel = new WP_Customize_Panel( $this->manager, 'bar' );
+               $customize_render_panel_count = did_action( 'customize_render_panel' );
+               add_action( 'customize_render_panel', array( $this, 'action_customize_render_panel_test' ) );
+               ob_start();
+               $panel->maybe_render();
+               $content = ob_get_clean();
+               $this->assertTrue( $panel->check_capabilities() );
+               $this->assertEmpty( $content );
+               $this->assertEquals( $customize_render_panel_count + 1, did_action( 'customize_render_panel' ), 'Unexpected did_action count for customize_render_panel' );
+               $this->assertEquals( 1, did_action( "customize_render_panel_{$panel->id}" ), "Unexpected did_action count for customize_render_panel_{$panel->id}" );
+       }
+
+       /**
+        * @see WP_Customize_Panel::maybe_render()
+        * @param WP_Customize_Panel $panel
+        */
+       function action_customize_render_panel_test( $panel ) {
+               $this->assertInstanceOf( 'WP_Customize_Panel', $panel );
+       }
+
+       /**
+        * @see WP_Customize_Panel::print_template()
+        */
+       function test_print_templates_standard() {
+               wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
+
+               $panel = new WP_Customize_Panel( $this->manager, 'baz' );
+               ob_start();
+               $panel->print_template();
+               $content = ob_get_clean();
+               $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-default-content">', $content );
+               $this->assertContains( 'accordion-section-title', $content );
+               $this->assertContains( 'control-panel-content', $content );
+               $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-default">', $content );
+               $this->assertContains( 'customize-panel-description', $content );
+               $this->assertContains( 'preview-notice', $content );
+       }
+
+       /**
+        * @see WP_Customize_Panel::print_template()
+        */
+       function test_print_templates_custom() {
+               wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
+
+               $panel = new Custom_Panel_Test( $this->manager, 'baz' );
+               ob_start();
+               $panel->print_template();
+               $content = ob_get_clean();
+               $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-titleless-content">', $content );
+               $this->assertNotContains( 'accordion-section-title', $content );
+
+               $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-titleless">', $content );
+               $this->assertNotContains( 'preview-notice', $content );
+       }
+}
+
+require_once ABSPATH . WPINC . '/class-wp-customize-panel.php';
+class Custom_Panel_Test extends WP_Customize_Panel {
+       public $type = 'titleless';
+
+       protected function render_template() {
+               ?>
+               <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
+                       <ul class="accordion-sub-container control-panel-content"></ul>
+               </li>
+               <?php
+       }
+
+       protected function content_template() {
+               ?>
+               <li class="panel-meta accordion-section control-section<# if ( ! data.description ) { #> cannot-expand<# } #>">
+                       <# if ( data.description ) { #>
+                               <div class="accordion-section-content description">
+                                       {{{ data.description }}}
+                               </div>
+                       <# } #>
+               </li>
+               <?php
+       }
+
+}
</ins></span></pre></div>
<a id="trunktestsphpunittestscustomizesectionphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/phpunit/tests/customize/section.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/customize/section.php                           (rev 0)
+++ trunk/tests/phpunit/tests/customize/section.php     2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,234 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+/**
+ * Tests for the WP_Customize_Section class.
+ *
+ * @group customize
+ */
+class Tests_WP_Customize_Section extends WP_UnitTestCase {
+
+       /**
+        * @var WP_Customize_Manager
+        */
+       protected $manager;
+
+       function setUp() {
+               parent::setUp();
+               require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
+               $GLOBALS['wp_customize'] = new WP_Customize_Manager();
+               $this->manager = $GLOBALS['wp_customize'];
+               $this->undefined = new stdClass();
+       }
+
+       function tearDown() {
+               $this->manager = null;
+               unset( $GLOBALS['wp_customize'] );
+               parent::tearDown();
+       }
+
+       /**
+        * @see WP_Customize_Section::__construct()
+        */
+       function test_construct_default_args() {
+               $section = new WP_Customize_Section( $this->manager, 'foo' );
+               $this->assertInternalType( 'int', $section->instance_number );
+               $this->assertEquals( $this->manager, $section->manager );
+               $this->assertEquals( 'foo', $section->id );
+               $this->assertEquals( 160, $section->priority );
+               $this->assertEquals( 'edit_theme_options', $section->capability );
+               $this->assertEquals( '', $section->theme_supports );
+               $this->assertEquals( '', $section->title );
+               $this->assertEquals( '', $section->description );
+               $this->assertEmpty( $section->panel );
+               $this->assertEquals( 'default', $section->type );
+               $this->assertEquals( array( $section, 'active_callback' ), $section->active_callback );
+       }
+
+       /**
+        * @see WP_Customize_Section::__construct()
+        */
+       function test_construct_custom_args() {
+               $args = array(
+                       'priority' => 200,
+                       'capability' => 'edit_posts',
+                       'theme_supports' => 'html5',
+                       'title' => 'Hello World',
+                       'description' => 'Lorem Ipsum',
+                       'type' => 'horizontal',
+                       'active_callback' => '__return_true',
+                       'panel' => 'bar',
+               );
+
+               $this->manager->add_panel( 'bar' );
+
+               $section = new WP_Customize_Section( $this->manager, 'foo', $args );
+               foreach ( $args as $key => $value ) {
+                       $this->assertEquals( $value, $section->$key );
+               }
+       }
+
+       /**
+        * @see WP_Customize_Section::__construct()
+        */
+       function test_construct_custom_type() {
+               $section = new Custom_Section_Test( $this->manager, 'foo' );
+               $this->assertEquals( 'titleless', $section->type );
+       }
+
+       /**
+        * @see WP_Customize_Section::active()
+        * @see WP_Customize_Section::active_callback()
+        */
+       function test_active() {
+               $section = new WP_Customize_Section( $this->manager, 'foo' );
+               $this->assertTrue( $section->active() );
+
+               $section = new WP_Customize_Section( $this->manager, 'foo', array(
+                       'active_callback' => '__return_false',
+               ) );
+               $this->assertFalse( $section->active() );
+               add_filter( 'customize_section_active', array( $this, 'filter_active_test' ), 10, 2 );
+               $this->assertTrue( $section->active() );
+       }
+
+       /**
+        * @param bool $active
+        * @param WP_Customize_Section $section
+        * @return bool
+        */
+       function filter_active_test( $active, $section ) {
+               $this->assertFalse( $active );
+               $this->assertInstanceOf( 'WP_Customize_Section', $section );
+               $active = true;
+               return $active;
+       }
+
+       /**
+        * @see WP_Customize_Section::json()
+        */
+       function test_json() {
+               $args = array(
+                       'priority' => 200,
+                       'capability' => 'edit_posts',
+                       'theme_supports' => 'html5',
+                       'title' => 'Hello World',
+                       'description' => 'Lorem Ipsum',
+                       'type' => 'horizontal',
+                       'panel' => 'bar',
+                       'active_callback' => '__return_true',
+               );
+
+               $this->manager->add_panel( 'bar' );
+
+               $section = new WP_Customize_Section( $this->manager, 'foo', $args );
+               $data = $section->json();
+               $this->assertEquals( 'foo', $data['id'] );
+               foreach ( array( 'title', 'description', 'priority', 'panel', 'type' ) as $key ) {
+                       $this->assertEquals( $args[ $key ], $data[ $key ] );
+               }
+               $this->assertEmpty( $data['content'] );
+               $this->assertTrue( $data['active'] );
+               $this->assertInternalType( 'int', $data['instanceNumber'] );
+       }
+
+       /**
+        * @see WP_Customize_Section::check_capabilities()
+        */
+       function test_check_capabilities() {
+               $user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
+               wp_set_current_user( $user_id );
+
+               $section = new WP_Customize_Section( $this->manager, 'foo' );
+               $this->assertTrue( $section->check_capabilities() );
+               $old_cap = $section->capability;
+               $section->capability = 'do_not_allow';
+               $this->assertFalse( $section->check_capabilities() );
+               $section->capability = $old_cap;
+               $this->assertTrue( $section->check_capabilities() );
+               $section->theme_supports = 'impossible_feature';
+               $this->assertFalse( $section->check_capabilities() );
+       }
+
+       /**
+        * @see WP_Customize_Section::get_content()
+        */
+       function test_get_content() {
+               $section = new WP_Customize_Section( $this->manager, 'foo' );
+               $this->assertEmpty( $section->get_content() );
+       }
+
+       /**
+        * @see WP_Customize_Section::maybe_render()
+        */
+       function test_maybe_render() {
+               wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
+               $section = new WP_Customize_Section( $this->manager, 'bar' );
+               $customize_render_section_count = did_action( 'customize_render_section' );
+               add_action( 'customize_render_section', array( $this, 'action_customize_render_section_test' ) );
+               ob_start();
+               $section->maybe_render();
+               $content = ob_get_clean();
+               $this->assertTrue( $section->check_capabilities() );
+               $this->assertEmpty( $content );
+               $this->assertEquals( $customize_render_section_count + 1, did_action( 'customize_render_section' ), 'Unexpected did_action count for customize_render_section' );
+               $this->assertEquals( 1, did_action( "customize_render_section_{$section->id}" ), "Unexpected did_action count for customize_render_section_{$section->id}" );
+       }
+
+       /**
+        * @see WP_Customize_Section::maybe_render()
+        * @param WP_Customize_Section $section
+        */
+       function action_customize_render_section_test( $section ) {
+               $this->assertInstanceOf( 'WP_Customize_Section', $section );
+       }
+
+       /**
+        * @see WP_Customize_Section::print_template()
+        */
+       function test_print_templates_standard() {
+               wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
+
+               $section = new WP_Customize_Section( $this->manager, 'baz' );
+               ob_start();
+               $section->print_template();
+               $content = ob_get_clean();
+               $this->assertContains( '<script type="text/html" id="tmpl-customize-section-default">', $content );
+               $this->assertContains( 'accordion-section-title', $content );
+               $this->assertContains( 'accordion-section-content', $content );
+       }
+
+       /**
+        * @see WP_Customize_Section::print_template()
+        */
+       function test_print_templates_custom() {
+               wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
+
+               $section = new Custom_Section_Test( $this->manager, 'baz' );
+               ob_start();
+               $section->print_template();
+               $content = ob_get_clean();
+               $this->assertContains( '<script type="text/html" id="tmpl-customize-section-titleless">', $content );
+               $this->assertNotContains( 'accordion-section-title', $content );
+               $this->assertContains( 'accordion-section-content', $content );
+       }
+}
+
+require_once ABSPATH . WPINC . '/class-wp-customize-section.php';
+class Custom_Section_Test extends WP_Customize_Section {
+       public $type = 'titleless';
+
+       protected function render_template() {
+               ?>
+               <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
+                       <ul class="accordion-section-content">
+                               <# if ( data.description ) { #>
+                                       <li class="customize-section-description-container">
+                                               <p class="description customize-section-description">{{{ data.description }}}</p>
+                                       </li>
+                               <# } #>
+                       </ul>
+               </li>
+               <?php
+       }
+
+}
</ins></span></pre></div>
<a id="trunktestsqunitfixturescustomizesettingsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/fixtures/customize-settings.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/fixtures/customize-settings.js  2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/tests/qunit/fixtures/customize-settings.js    2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -15,7 +15,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        'controls': {
</span><span class="cx" style="display: block; padding: 0 10px">                'fixture-control': {
</span><span class="cx" style="display: block; padding: 0 10px">                        'active': true,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'content': '<li id="customize-control-fixture-control" class="customize-control customize-control-text">\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t\t\t\t<span class="customize-control-title">Site Title</span>\n\t\t\t\t\t\t\t\t\t\t<input type="text"  value="sWordPress Developssa!" data-customize-setting-link="blogname" />\n\t\t\t\t</label>\n\t\t\t\t\t\t</li>',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'content': '<li id="accordion-section-fixture-section" class="accordion-section control-section control-section-default"> <h3 class="accordion-section-title" tabindex="0"> Section Fixture <span class="screen-reader-text">Press return or enter to open</span> </h3> <ul class="accordion-section-content"> <li class="customize-section-description-container"> <div class="customize-section-title"> <button class="customize-section-back" tabindex="-1"> <span class="screen-reader-text">Back</span> </button> <h3> <span class="customize-action">Customizing &#9656; Fixture Panel</span> Section Fixture </h3> </div> </li> </ul> </li>',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'description': '',
</span><span class="cx" style="display: block; padding: 0 10px">                        'instanceNumber': 8,
</span><span class="cx" style="display: block; padding: 0 10px">                        'label': 'Fixture Control',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -35,24 +35,58 @@
</span><span class="cx" style="display: block; padding: 0 10px">        'panels': {
</span><span class="cx" style="display: block; padding: 0 10px">                'fixture-panel': {
</span><span class="cx" style="display: block; padding: 0 10px">                        'active': true,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'content': '<li id="accordion-panel-fixture-panel" class="accordion-section control-section control-panel control-panel-default">\n\t\t\t<h3 class="accordion-section-title" tabindex="0">\n\t\t\t\tLipsum\t\t\t\t<span class="screen-reader-text">Press return or enter to open this panel</span>\n\t\t\t</h3>\n\t\t\t<ul class="accordion-sub-container control-panel-content">\n\t\t\t\t\t\t<li class="panel-meta accordion-section control-section">\n\t\t\t<div class="accordion-section-title" tabindex="0">\n\t\t\t\t<span class="preview-notice">You are customizing <strong class="panel-title">Lipsum</strong></span>\n\t\t\t</div>\n\t\t\t\t\t\t\t<div class="accordion-section-content description">\n\t\
 t\t\t\tLorem Ipsum\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t</li>',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'content': '<li id="accordion-panel-fixture-panel" class="accordion-section control-section control-panel control-panel-default"> <h3 class="accordion-section-title" tabindex="0"> Fixture Panel <span class="screen-reader-text">Press return or enter to open this panel</span> </h3> <ul class="accordion-sub-container control-panel-content"> <li class="panel-meta customize-info accordion-section cannot-expand"> <button class="customize-panel-back" tabindex="-1"><span class="screen-reader-text">Back</span></button> <div class="accordion-section-title"> <span class="preview-notice">You are customizing <strong class="panel-title">Fixture Panel</strong></span> <button 
 class="customize-help-toggle dashicons dashicons-editor-help" tabindex="0" aria-expanded="false"><span class="screen-reader-text">Help</span></button> </div> </li> </ul> </li>',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'description': 'Lorem ipsum',
</span><span class="cx" style="display: block; padding: 0 10px">                        'instanceNumber': 1,
</span><span class="cx" style="display: block; padding: 0 10px">                        'priority': 110,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'title': 'Lorem Ipsum',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'title': 'Fixture panel with content',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'type': 'default'
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                },
+               'fixture-panel-default-templated': {
+                       'active': true,
+                       'description': 'Lorem ipsum',
+                       'instanceNumber': 2,
+                       'priority': 110,
+                       'title': 'Fixture default panel using template',
+                       'type': 'default'
+               },
+               'fixture-panel-titleless-templated': {
+                       'active': true,
+                       'description': 'Lorem ipsum',
+                       'instanceNumber': 3,
+                       'priority': 110,
+                       'title': 'Fixture titleless panel using template',
+                       'type': 'titleless'
</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">        'sections': {
</span><span class="cx" style="display: block; padding: 0 10px">                'fixture-section': {
</span><span class="cx" style="display: block; padding: 0 10px">                        'active': true,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'content': '<li id="accordion-section-fixture-section" class="accordion-section control-section control-section-default">\n\t\t\t<h3 class="accordion-section-title" tabindex="0">\n\t\t\t\tSite Title &amp; Tagline\t\t\t\t<span class="screen-reader-text">Press return or enter to expand</span>\n\t\t\t</h3>\n\t\t\t<ul class="accordion-section-content">\n\t\t\t\t\t\t\t</ul>\n\t\t</li>',
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'content': '<li id="accordion-section-fixture-section" class="accordion-section control-section control-section-default"> <h3 class="accordion-section-title" tabindex="0"> Section Fixture <span class="screen-reader-text">Press return or enter to open</span> </h3> <ul class="accordion-section-content"> <li class="customize-section-description-container"> <div class="customize-section-title"> <button class="customize-section-back" tabindex="-1"> <span class="screen-reader-text">Back</span> </button> <h3> <span class="customize-action">Customizing &#9656; Fixture Panel</span> Section Fixture </h3> </div> </li> </ul> </li>',
</ins><span class="cx" style="display: block; padding: 0 10px">                         'description': '',
</span><span class="cx" style="display: block; padding: 0 10px">                        'instanceNumber': 2,
</span><span class="cx" style="display: block; padding: 0 10px">                        'panel': 'fixture-panel',
</span><span class="cx" style="display: block; padding: 0 10px">                        'priority': 20,
</span><span class="cx" style="display: block; padding: 0 10px">                        'title': 'Fixture Section',
</span><span class="cx" style="display: block; padding: 0 10px">                        'type': 'default'
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                },
+               'fixture-section-default-templated': {
+                       'active': true,
+                       'description': '',
+                       'instanceNumber': 3,
+                       'panel': 'fixture-panel',
+                       'priority': 20,
+                       'title': 'Fixture default section using template',
+                       'type': 'default'
+               },
+               'fixture-section-titleless-templated': {
+                       'active': true,
+                       'description': '',
+                       'instanceNumber': 4,
+                       'panel': 'fixture-panel',
+                       'priority': 20,
+                       'title': 'Fixture titleless section using template',
+                       'type': 'titleless'
</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">        'settings': {
</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      2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/tests/qunit/index.html        2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -9,6 +9,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><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <script src="../../src/wp-includes/js/wp-util.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><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="lines" style="display: block; padding: 0 10px; color: #888">@@ -39,5 +40,73 @@
</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">                <script src="wp-admin/js/customize-controls.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="wp-admin/js/customize-controls-utils.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               <!-- Customizer templates for sections -->
+               <script type="text/html" id="tmpl-customize-section-default">
+                       <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
+                               <h3 class="accordion-section-title" tabindex="0">
+                                       {{ data.title }}
+                                       <span class="screen-reader-text">Press return or enter to expand</span>
+                               </h3>
+                               <ul class="accordion-section-content">
+                                       <# if ( data.description ) { #>
+                                               <li class="customize-section-description-container">
+                                                       <p class="description customize-section-description">{{{ data.description }}}</p>
+                                               </li>
+                                       <# } #>
+                               </ul>
+                       </li>
+               </script>
+               <script type="text/html" id="tmpl-customize-section-titleless">
+                       <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
+                               <!-- Notice the lack of an h3 with title displayed inside. -->
+                               <ul class="accordion-section-content">
+                                       <# if ( data.description ) { #>
+                                               <li class="customize-section-description-container">
+                                                       <p class="description customize-section-description">{{{ data.description }}}</p>
+                                               </li>
+                                       <# } #>
+                               </ul>
+                       </li>
+               </script>
+
+               <!-- Customizer templates for panels -->
+               <script type="text/html" id="tmpl-customize-panel-default">
+                       <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
+                               <h3 class="accordion-section-title" tabindex="0">
+                                       {{ data.title }}
+                                       <span class="screen-reader-text">Press return or enter to open this panel</span>
+                               </h3>
+                               <ul class="accordion-sub-container control-panel-content"></ul>
+                       </li>
+               </script>
+               <script type="text/html" id="tmpl-customize-panel-default-content">
+                       <li class="panel-meta accordion-section control-section<# if ( ! data.description ) { #> cannot-expand<# } #>">
+                               <div class="accordion-section-title" tabindex="0">
+                                       <span class="preview-notice">You are customizing <strong class="panel-title">{{ data.title }}</strong></span>
+                               </div>
+                               <# if ( data.description ) { #>
+                                       <div class="accordion-section-content description">
+                                               {{{ data.description }}}
+                                       </div>
+                               <# } #>
+                       </li>
+               </script>
+               <script type="text/html" id="tmpl-customize-panel-titleless">
+                       <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
+                               <!-- Notice the lack of an h3 with title displayed inside. -->
+                               <ul class="accordion-sub-container control-panel-content"></ul>
+                       </li>
+               </script>
+               <script type="text/html" id="tmpl-customize-panel-titleless-content">
+                       <li class="panel-meta accordion-section control-section<# if ( ! data.description ) { #> cannot-expand<# } #>">
+                               <!-- Notice lack of title containing preview notice -->
+                               <# if ( data.description ) { #>
+                                       <div class="accordion-section-content description">
+                                               {{{ data.description }}}
+                                       </div>
+                               <# } #>
+                       </li>
+               </script>
</ins><span class="cx" style="display: block; padding: 0 10px">         </body>
</span><span class="cx" style="display: block; padding: 0 10px"> </html>
</span></span></pre></div>
<a id="trunktestsqunitwpadminjscustomizecontrolsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/qunit/wp-admin/js/customize-controls.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/wp-admin/js/customize-controls.js       2015-05-29 21:36:38 UTC (rev 32657)
+++ trunk/tests/qunit/wp-admin/js/customize-controls.js 2015-05-30 00:02:13 UTC (rev 32658)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -95,29 +95,62 @@
</span><span class="cx" style="display: block; padding: 0 10px">                equal( control.section(), 'fixture-section' );
</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">+        // Begin sections.
</ins><span class="cx" style="display: block; padding: 0 10px">         module( 'Customizer Section in Fixture' );
</span><span class="cx" style="display: block; padding: 0 10px">        test( 'Fixture section exists', function () {
</span><span class="cx" style="display: block; padding: 0 10px">                ok( wp.customize.section.has( 'fixture-section' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        } );
</span><span class="cx" style="display: block; padding: 0 10px">        test( 'Fixture section has control among controls()', function () {
</span><span class="cx" style="display: block; padding: 0 10px">                var section = wp.customize.section( 'fixture-section' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                equal( section.controls().length, 1 );
-               equal( section.controls()[0].id, 'fixture-control' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         ok( -1 !== _.pluck( section.controls(), 'id' ).indexOf( 'fixture-control' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">         } );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        test( 'Fixture section has control among controls()', function () {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ test( 'Fixture section has has expected panel', function () {
</ins><span class="cx" style="display: block; padding: 0 10px">                 var section = wp.customize.section( 'fixture-section' );
</span><span class="cx" style="display: block; padding: 0 10px">                equal( section.panel(), 'fixture-panel' );
</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">+        module( 'Customizer Default Section with Template in Fixture' );
+       test( 'Fixture section exists', function () {
+               ok( wp.customize.section.has( 'fixture-section-default-templated' ) );
+       } );
+       test( 'Fixture section has expected content', function () {
+               var id = 'fixture-section-default-templated', section;
+               section = wp.customize.section( id );
+               ok( ! section.params.content );
+               ok( !! section.container );
+               ok( section.container.is( '.control-section.control-section-default' ) );
+               ok( 1 === section.container.find( '> .accordion-section-title' ).length );
+               ok( 1 === section.container.find( '> .accordion-section-content' ).length );
+       } );
+
+       module( 'Customizer Custom Type (titleless) Section with Template in Fixture' );
+       test( 'Fixture section exists', function () {
+               ok( wp.customize.section.has( 'fixture-section-titleless-templated' ) );
+       } );
+       test( 'Fixture section has expected content', function () {
+               var id = 'fixture-section-titleless-templated', section;
+               section = wp.customize.section( id );
+               ok( ! section.params.content );
+               ok( !! section.container );
+               ok( section.container.is( '.control-section.control-section-titleless' ) );
+               ok( 0 === section.container.find( '> .accordion-section-title' ).length );
+               ok( 1 === section.container.find( '> .accordion-section-content' ).length );
+       } );
+
+       // Begin panels.
</ins><span class="cx" style="display: block; padding: 0 10px">         module( 'Customizer Panel in Fixture' );
</span><span class="cx" style="display: block; padding: 0 10px">        test( 'Fixture panel exists', function () {
</span><span class="cx" style="display: block; padding: 0 10px">                ok( wp.customize.panel.has( 'fixture-panel' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        } );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        test( 'Fixture section has control among controls()', function () {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ test( 'Fixture panel has content', function () {
</ins><span class="cx" style="display: block; padding: 0 10px">                 var panel = wp.customize.panel( 'fixture-panel' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                equal( panel.sections().length, 1 );
-               equal( panel.sections()[0].id, 'fixture-section' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         ok( !! panel.params.content );
+               ok( !! panel.container );
</ins><span class="cx" style="display: block; padding: 0 10px">         } );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        test( 'Fixture panel has section among its sections()', function () {
+               var panel = wp.customize.panel( 'fixture-panel' );
+               ok( -1 !== _.pluck( panel.sections(), 'id' ).indexOf( 'fixture-section' ) );
+       } );
</ins><span class="cx" style="display: block; padding: 0 10px">         test( 'Panel is not expanded by default', function () {
</span><span class="cx" style="display: block; padding: 0 10px">                var panel = wp.customize.panel( 'fixture-panel' );
</span><span class="cx" style="display: block; padding: 0 10px">                ok( ! panel.expanded() );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -138,7 +171,35 @@
</span><span class="cx" style="display: block; padding: 0 10px">                ok( panel.expanded() );
</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">+        module( 'Customizer Default Panel with Template in Fixture' );
+       test( 'Fixture section exists', function () {
+               ok( wp.customize.panel.has( 'fixture-panel-default-templated' ) );
+       } );
+       test( 'Fixture panel has expected content', function () {
+               var id = 'fixture-panel-default-templated', panel;
+               panel = wp.customize.panel( id );
+               ok( ! panel.params.content );
+               ok( !! panel.container );
+               ok( panel.container.is( '.control-panel.control-panel-default' ) );
+               ok( 1 === panel.container.find( '> .accordion-section-title' ).length );
+               ok( 1 === panel.container.find( '> .control-panel-content' ).length );
+       } );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        module( 'Customizer Custom Type Panel (titleless) with Template in Fixture' );
+       test( 'Fixture panel exists', function () {
+               ok( wp.customize.panel.has( 'fixture-panel-titleless-templated' ) );
+       } );
+       test( 'Fixture panel has expected content', function () {
+               var id = 'fixture-panel-titleless-templated', panel;
+               panel = wp.customize.panel( id );
+               ok( ! panel.params.content );
+               ok( !! panel.container );
+               ok( panel.container.is( '.control-panel.control-panel-titleless' ) );
+               ok( 0 === panel.container.find( '> .accordion-section-title' ).length );
+               ok( 1 === panel.container.find( '> .control-panel-content' ).length );
+       } );
+
+
</ins><span class="cx" style="display: block; padding: 0 10px">         module( 'Dynamically-created Customizer Setting Model' );
</span><span class="cx" style="display: block; padding: 0 10px">        settingId = 'new_blogname';
</span><span class="cx" style="display: block; padding: 0 10px">        settingValue = 'Hello World';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -160,10 +221,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        module( 'Dynamically-created Customizer Section Model' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        sectionId = 'mock_title_tagline';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        sectionContent = '<li id="accordion-section-mock_title_tagline" class="control-section accordion-section"></li>';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ sectionContent = '<li id="accordion-section-mock_title_tagline" class="accordion-section control-section control-section-default"> <h3 class="accordion-section-title" tabindex="0"> Section Fixture <span class="screen-reader-text">Press return or enter to open</span> </h3> <ul class="accordion-section-content"> <li class="customize-section-description-container"> <div class="customize-section-title"> <button class="customize-section-back" tabindex="-1"> <span class="screen-reader-text">Back</span> </button> <h3> <span class="customize-action">Customizing &#9656; Fixture Panel</span> Section Fixture </h3> </div> </li> </ul> </li>';
</ins><span class="cx" style="display: block; padding: 0 10px">         sectionData = {
</span><span class="cx" style="display: block; padding: 0 10px">                content: sectionContent,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                active: true
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         active: true,
+               type: 'default'
</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">        mockSection = new wp.customize.Section( sectionId, { params: sectionData } );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -277,7 +339,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                content: panelContent,
</span><span class="cx" style="display: block; padding: 0 10px">                title: panelTitle,
</span><span class="cx" style="display: block; padding: 0 10px">                description: panelDescription,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                active: true // @todo This should default to true
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         active: true, // @todo This should default to true
+               type: 'default'
</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">        mockPanel = new wp.customize.Panel( panelId, { params: panelData } );
</span></span></pre>
</div>
</div>

</body>
</html>