<!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>[34563] trunk: Customizer: Defer embedding widget controls to improve DOM performance and initial load time.</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/34563">34563</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/34563","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-09-25 21:01:46 +0000 (Fri, 25 Sep 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'>Customizer: Defer embedding widget controls to improve DOM performance and initial load time.

The Menu Customizer feature includes a performance technique whereby the controls for nav menu items are only embedded into the DOM once the containing menu section is expanded. This commit implements the same DOM deferral for widgets but goes a step further than just embedding the controls once the widget area's Customizer section is expanded: it also defers the embedding of the widget control's form until the widget is expanded, at which point the `widget-added` event also fires to allow any additional widget initialization to be done. The deferred DOM embedding can speed up initial load time by 10x or more. This DOM deferral also yields a reduction in overall memory usage in the browser process.

Includes changes to `wp_widget_control()` to facilitate separating out the widget form from the surrounding accordion container; also includes unit tests for this previously-untested function. Also included are initial QUnit tests (finally) for widgets in the Customizer.

Fixes <a href="https://core.trac.wordpress.org/ticket/33901">#33901</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludeswidgetsphp">trunk/src/wp-admin/includes/widgets.php</a></li>
<li><a href="#trunksrcwpadminjscustomizewidgetsjs">trunk/src/wp-admin/js/customize-widgets.js</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizecontrolphp">trunk/src/wp-includes/class-wp-customize-control.php</a></li>
<li><a href="#trunksrcwpincludesclasswpcustomizewidgetsphp">trunk/src/wp-includes/class-wp-customize-widgets.php</a></li>
<li><a href="#trunktestsphpunittestscustomizewidgetsphp">trunk/tests/phpunit/tests/customize/widgets.php</a></li>
<li><a href="#trunktestsphpunittestswidgetsphp">trunk/tests/phpunit/tests/widgets.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>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunktestsqunitfixturescustomizewidgetsjs">trunk/tests/qunit/fixtures/customize-widgets.js</a></li>
<li><a href="#trunktestsqunitwpadminjscustomizewidgetsjs">trunk/tests/qunit/wp-admin/js/customize-widgets.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminincludeswidgetsphp"></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/includes/widgets.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/widgets.php   2015-09-25 20:49:47 UTC (rev 34562)
+++ trunk/src/wp-admin/includes/widgets.php     2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -181,6 +181,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        $multi_number = isset($sidebar_args['_multi_num']) ? $sidebar_args['_multi_num'] : '';
</span><span class="cx" style="display: block; padding: 0 10px">        $add_new = isset($sidebar_args['_add']) ? $sidebar_args['_add'] : '';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        $before_form = isset( $sidebar_args['before_form'] ) ? $sidebar_args['before_form'] : '<form method="post">';
+       $after_form = isset( $sidebar_args['after_form'] ) ? $sidebar_args['after_form'] : '</form>';
+       $before_widget_content = isset( $sidebar_args['before_widget_content'] ) ? $sidebar_args['before_widget_content'] : '<div class="widget-content">';
+       $after_widget_content = isset( $sidebar_args['after_widget_content'] ) ? $sidebar_args['after_widget_content'] : '</div>';
+
</ins><span class="cx" style="display: block; padding: 0 10px">         $query_arg = array( 'editwidget' => $widget['id'] );
</span><span class="cx" style="display: block; padding: 0 10px">        if ( $add_new ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $query_arg['addnew'] = 1;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -225,14 +230,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <div class="widget-inside">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <form method="post">
-       <div class="widget-content">
-<?php
-       if ( isset($control['callback']) )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <?php echo $before_form; ?>
+       <?php echo $before_widget_content; ?>
+       <?php
+       if ( isset( $control['callback'] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $has_form = call_user_func_array( $control['callback'], $control['params'] );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        else
-               echo "\t\t<p>" . __('There are no options for this widget.') . "</p>\n"; ?>
-       </div>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ } else {
+               echo "\t\t<p>" . __('There are no options for this widget.') . "</p>\n";
+       }
+       ?>
+       <?php echo $after_widget_content; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         <input type="hidden" name="widget-id" class="widget-id" value="<?php echo esc_attr($id_format); ?>" />
</span><span class="cx" style="display: block; padding: 0 10px">        <input type="hidden" name="id_base" class="id_base" value="<?php echo esc_attr($id_base); ?>" />
</span><span class="cx" style="display: block; padding: 0 10px">        <input type="hidden" name="widget-width" class="widget-width" value="<?php if (isset( $control['width'] )) echo esc_attr($control['width']); ?>" />
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -252,7 +259,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                </div>
</span><span class="cx" style="display: block; padding: 0 10px">                <br class="clear" />
</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">-        </form>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <?php echo $after_form; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <div class="widget-description">
</span></span></pre></div>
<a id="trunksrcwpadminjscustomizewidgetsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/js/customize-widgets.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/js/customize-widgets.js        2015-09-25 20:49:47 UTC (rev 34562)
+++ trunk/src/wp-admin/js/customize-widgets.js  2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -417,37 +417,104 @@
</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="cx" style="display: block; padding: 0 10px">                 */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                initialize: function ( id, options ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         initialize: function( id, options ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         var control = this;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        api.Control.prototype.initialize.call( control, id, options );
-                       control.expanded = new api.Value();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       control.widgetControlEmbedded = false;
+                       control.widgetContentEmbedded = false;
+                       control.expanded = new api.Value( false );
</ins><span class="cx" style="display: block; padding: 0 10px">                         control.expandedArgumentsQueue = [];
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        control.expanded.bind( function ( expanded ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 control.expanded.bind( function( expanded ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 var args = control.expandedArgumentsQueue.shift();
</span><span class="cx" style="display: block; padding: 0 10px">                                args = $.extend( {}, control.defaultExpandedArguments, args );
</span><span class="cx" style="display: block; padding: 0 10px">                                control.onChangeExpanded( expanded, args );
</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.expanded.set( false );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                       api.Control.prototype.initialize.call( control, id, options );
</ins><span class="cx" style="display: block; padding: 0 10px">                 },
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 * Set up the control
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+          * Set up the control.
+                *
+                * @since 3.9.0
</ins><span class="cx" style="display: block; padding: 0 10px">                  */
</span><span class="cx" style="display: block; padding: 0 10px">                ready: function() {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        this._setupModel();
-                       this._setupWideWidget();
-                       this._setupControlToggle();
-                       this._setupWidgetTitle();
-                       this._setupReorderUI();
-                       this._setupHighlightEffects();
-                       this._setupUpdateUI();
-                       this._setupRemoveUI();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 var control = this;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        /*
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         * Embed a placeholder once the section is expanded. The full widget
+                        * form content will be embedded once the control itself is expanded,
+                        * and at this point the widget-added event will be triggered.
+                        */
+                       if ( ! control.section() ) {
+                               control.embedWidgetControl();
+                       } else {
+                               api.section( control.section(), function( section ) {
+                                       var onExpanded = function( isExpanded ) {
+                                               if ( isExpanded ) {
+                                                       control.embedWidgetControl();
+                                                       section.expanded.unbind( onExpanded );
+                                               }
+                                       };
+                                       if ( section.expanded() ) {
+                                               onExpanded( true );
+                                       } else {
+                                               section.expanded.bind( onExpanded );
+                                       }
+                               } );
+                       }
+               },
+
+               /**
+                * Embed the .widget element inside the li container.
+                *
+                * @since 4.4.0
+                */
+               embedWidgetControl: function() {
+                       var control = this, widgetControl;
+
+                       if ( control.widgetControlEmbedded ) {
+                               return;
+                       }
+                       control.widgetControlEmbedded = true;
+
+                       widgetControl = $( control.params.widget_control );
+                       control.container.append( widgetControl );
+
+                       control._setupModel();
+                       control._setupWideWidget();
+                       control._setupControlToggle();
+
+                       control._setupWidgetTitle();
+                       control._setupReorderUI();
+                       control._setupHighlightEffects();
+                       control._setupUpdateUI();
+                       control._setupRemoveUI();
+               },
+
+               /**
+                * Embed the actual widget form inside of .widget-content and finally trigger the widget-added event.
+                *
+                * @since 4.4.0
+                */
+               embedWidgetContent: function() {
+                       var control = this, widgetContent;
+
+                       control.embedWidgetControl();
+                       if ( control.widgetContentEmbedded ) {
+                               return;
+                       }
+                       control.widgetContentEmbedded = true;
+
+                       widgetContent = $( control.params.widget_content );
+                       control.container.find( '.widget-content:first' ).append( widgetContent );
+
+                       /*
</ins><span class="cx" style="display: block; padding: 0 10px">                          * Trigger widget-added event so that plugins can attach any event
</span><span class="cx" style="display: block; padding: 0 10px">                         * listeners and dynamic UI elements.
</span><span class="cx" style="display: block; padding: 0 10px">                         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $( document ).trigger( 'widget-added', [ this.container.find( '.widget:first' ) ] );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $( document ).trigger( 'widget-added', [ control.container.find( '.widget:first' ) ] );
+
</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">@@ -1008,6 +1075,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        var self = this, instanceOverride, completeCallback, $widgetRoot, $widgetContent,
</span><span class="cx" style="display: block; padding: 0 10px">                                updateNumber, params, data, $inputs, processing, jqxhr, isChanged;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        // The updateWidget logic requires that the form fields to be fully present.
+                       self.embedWidgetContent();
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         args = $.extend( {
</span><span class="cx" style="display: block; padding: 0 10px">                                instance: null,
</span><span class="cx" style="display: block; padding: 0 10px">                                complete: null,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1255,6 +1325,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                onChangeExpanded: function ( expanded, args ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        var self = this, $widget, $inside, complete, prevComplete;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        self.embedWidgetControl(); // Make sure the outer form is embedded so that the expanded state can be set in the UI.
+                       if ( expanded ) {
+                               self.embedWidgetContent();
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         // If the expanded state is unchanged only manipulate container expanded states
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( args.unchanged ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( expanded ) {
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizecontrolphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/class-wp-customize-control.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-control.php      2015-09-25 20:49:47 UTC (rev 34562)
+++ trunk/src/wp-includes/class-wp-customize-control.php        2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1487,20 +1487,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public $height;
</span><span class="cx" style="display: block; padding: 0 10px">        public $is_wide = false;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Gather control params for exporting to JavaScript.
+        *
+        * @global array $wp_registered_widgets
+        */
</ins><span class="cx" style="display: block; padding: 0 10px">         public function to_json() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                global $wp_registered_widgets;
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 parent::to_json();
</span><span class="cx" style="display: block; padding: 0 10px">                $exported_properties = array( 'widget_id', 'widget_id_base', 'sidebar_id', 'width', 'height', 'is_wide' );
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $exported_properties as $key ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->json[ $key ] = $this->$key;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        }
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        /**
-        *
-        * @global array $wp_registered_widgets
-        */
-       public function render_content() {
-               global $wp_registered_widgets;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         // Get the widget_control and widget_content.
</ins><span class="cx" style="display: block; padding: 0 10px">                 require_once ABSPATH . '/wp-admin/includes/widgets.php';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $widget = $wp_registered_widgets[ $this->widget_id ];
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1514,10 +1515,18 @@
</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">                $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                echo $this->manager->widgets->get_widget_control( $args );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $widget_control_parts = $this->manager->widgets->get_widget_control_parts( $args );
+
+               $this->json['widget_control'] = $widget_control_parts['control'];
+               $this->json['widget_content'] = $widget_control_parts['content'];
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Override render_content to be no-op since content is exported via to_json for deferred embedding.
+        */
+       public function render_content() {}
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Whether the current widget is rendered on the page.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 4.0.0
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizewidgetsphp"></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-widgets.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/class-wp-customize-widgets.php      2015-09-25 20:49:47 UTC (rev 34562)
+++ trunk/src/wp-includes/class-wp-customize-widgets.php        2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -898,19 +898,45 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return string Widget control form HTML markup.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_widget_control( $args ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $args[0]['before_form'] = '<div class="form">';
+               $args[0]['after_form'] = '</div><!-- .form -->';
+               $args[0]['before_widget_content'] = '<div class="widget-content">';
+               $args[0]['after_widget_content'] = '</div><!-- .widget-content -->';
</ins><span class="cx" style="display: block; padding: 0 10px">                 ob_start();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 call_user_func_array( 'wp_widget_control', $args );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $replacements = array(
-                       '<form method="post">' => '<div class="form">',
-                       '</form>' => '</div><!-- .form -->',
-               );
-
</del><span class="cx" style="display: block; padding: 0 10px">                 $control_tpl = ob_get_clean();
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                return $control_tpl;
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $control_tpl = str_replace( array_keys( $replacements ), array_values( $replacements ), $control_tpl );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ /**
+        * Get the widget control markup parts.
+        *
+        * @since 4.4.0
+        * @access public
+        *
+        * @param array $args Widget control arguments.
+        * @return array {
+        *     @type string $control  Markup for widget control wrapping form.
+        *     @type string $content  The contents of the widget form itself.
+        * }
+        */
+       public function get_widget_control_parts( $args ) {
+               $args[0]['before_widget_content'] = '<div class="widget-content">';
+               $args[0]['after_widget_content'] = '</div><!-- .widget-content -->';
+               $control_markup = $this->get_widget_control( $args );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                return $control_tpl;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $content_start_pos = strpos( $control_markup, $args[0]['before_widget_content'] );
+               $content_end_pos = strrpos( $control_markup, $args[0]['after_widget_content'] );
+
+               $control = substr( $control_markup, 0, $content_start_pos + strlen( $args[0]['before_widget_content'] ) );
+               $control .= substr( $control_markup, $content_end_pos );
+               $content = trim( substr(
+                       $control_markup,
+                       $content_start_pos + strlen( $args[0]['before_widget_content'] ),
+                       $content_end_pos - $content_start_pos - strlen( $args[0]['before_widget_content'] )
+               ) );
+
+               return compact( 'control', 'content' );
</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="trunktestsphpunittestscustomizewidgetsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/customize/widgets.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/customize/widgets.php   2015-09-25 20:49:47 UTC (rev 34562)
+++ trunk/tests/phpunit/tests/customize/widgets.php     2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -195,4 +195,74 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $unsanitized_from_js = $this->manager->widgets->sanitize_widget_instance( $sanitized_for_js );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEquals( $unsanitized_from_js, $new_categories_instance );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * Get the widget control args for tests.
+        *
+        * @return array
+        */
+       function get_test_widget_control_args() {
+               global $wp_registered_widgets;
+               require_once ABSPATH . '/wp-admin/includes/widgets.php';
+               $widget_id = 'search-2';
+               $widget = $wp_registered_widgets[ $widget_id ];
+               $args = array(
+                       'widget_id' => $widget['id'],
+                       'widget_name' => $widget['name'],
+               );
+               $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) );
+               return $args;
+       }
+
+       /**
+        * @see WP_Customize_Widgets::get_widget_control()
+        */
+       function test_get_widget_control() {
+               $this->do_customize_boot_actions();
+               $widget_control = $this->manager->widgets->get_widget_control( $this->get_test_widget_control_args() );
+
+               $this->assertContains( '<div class="form">', $widget_control );
+               $this->assertContains( '<div class="widget-content">', $widget_control );
+               $this->assertContains( '<input type="hidden" name="id_base" class="id_base" value="search"', $widget_control );
+               $this->assertContains( '<input class="widefat"', $widget_control );
+       }
+
+       /**
+        * @see WP_Customize_Widgets::get_widget_control_parts()
+        */
+       function test_get_widget_control_parts() {
+               $this->do_customize_boot_actions();
+               $widget_control_parts = $this->manager->widgets->get_widget_control_parts( $this->get_test_widget_control_args() );
+               $this->assertArrayHasKey( 'content', $widget_control_parts );
+               $this->assertArrayHasKey( 'control', $widget_control_parts );
+
+               $this->assertContains( '<div class="form">', $widget_control_parts['control'] );
+               $this->assertContains( '<div class="widget-content">', $widget_control_parts['control'] );
+               $this->assertContains( '<input type="hidden" name="id_base" class="id_base" value="search"', $widget_control_parts['control'] );
+               $this->assertNotContains( '<input class="widefat"', $widget_control_parts['control'] );
+               $this->assertContains( '<input class="widefat"', $widget_control_parts['content'] );
+       }
+
+       /**
+        * @see WP_Widget_Form_Customize_Control::json()
+        */
+       function test_wp_widget_form_customize_control_json() {
+               $this->do_customize_boot_actions();
+               $control = $this->manager->get_control( 'widget_search[2]' );
+               $params = $control->json();
+
+               $this->assertEquals( 'widget_form', $params['type'] );
+               $this->assertRegExp( '#^<li[^>]+>\s+</li>$#', $params['content'] );
+               $this->assertRegExp( '#^<div[^>]*class=\'widget\'[^>]*#s', $params['widget_control'] );
+               $this->assertContains( '<div class="widget-content"></div>', $params['widget_control'] );
+               $this->assertNotContains( '<input class="widefat"', $params['widget_control'] );
+               $this->assertContains( '<input class="widefat"', $params['widget_content'] );
+               $this->assertEquals( 'search-2', $params['widget_id'] );
+               $this->assertEquals( 'search', $params['widget_id_base'] );
+               $this->assertArrayHasKey( 'sidebar_id', $params );
+               $this->assertArrayHasKey( 'width', $params );
+               $this->assertArrayHasKey( 'height', $params );
+               $this->assertInternalType( 'bool', $params['is_wide'] );
+
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="trunktestsphpunittestswidgetsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/widgets.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/widgets.php     2015-09-25 20:49:47 UTC (rev 34562)
+++ trunk/tests/phpunit/tests/widgets.php       2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -306,8 +306,63 @@
</span><span class="cx" style="display: block; padding: 0 10px">                ob_start();
</span><span class="cx" style="display: block; padding: 0 10px">                $result = dynamic_sidebar( 'Sidebar 1' );
</span><span class="cx" style="display: block; padding: 0 10px">                ob_end_clean();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                 
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->assertFalse( $result );
</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">+        /**
+        * @see wp_widget_control()
+        */
+       function test_wp_widget_control() {
+               global $wp_registered_widgets;
+
+               wp_widgets_init();
+               require_once ABSPATH . '/wp-admin/includes/widgets.php';
+               $widget_id = 'search-2';
+               $widget = $wp_registered_widgets[ $widget_id ];
+               $params = array(
+                       'widget_id' => $widget['id'],
+                       'widget_name' => $widget['name'],
+               );
+               $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $params, 1 => $widget['params'][0] ) );
+
+               ob_start();
+               call_user_func_array( 'wp_widget_control', $args );
+               $control = ob_get_clean();
+               $this->assertNotEmpty( $control );
+
+               $this->assertContains( '<div class="widget-top">', $control );
+               $this->assertContains( '<div class="widget-title-action">', $control );
+               $this->assertContains( '<div class="widget-title">', $control );
+               $this->assertContains( '<form method="post">', $control );
+               $this->assertContains( '<div class="widget-content">', $control );
+               $this->assertContains( '<input class="widefat"', $control );
+               $this->assertContains( '<input type="hidden" name="id_base" class="id_base" value="search"', $control );
+               $this->assertContains( '<div class="widget-control-actions">', $control );
+               $this->assertContains( '<div class="alignleft">', $control );
+               $this->assertContains( 'widget-control-remove', $control );
+               $this->assertContains( 'widget-control-close', $control );
+               $this->assertContains( '<div class="alignright">', $control );
+               $this->assertContains( '<input type="submit" name="savewidget"', $control );
+
+               $param_overrides = array(
+                       'before_form' => '<!-- before_form -->',
+                       'after_form' => '<!-- after_form -->',
+                       'before_widget_content' => '<!-- before_widget_content -->',
+                       'after_widget_content' => '<!-- after_widget_content -->',
+               );
+               $params = array_merge( $params, $param_overrides );
+               $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $params, 1 => $widget['params'][0] ) );
+
+               ob_start();
+               call_user_func_array( 'wp_widget_control', $args );
+               $control = ob_get_clean();
+               $this->assertNotEmpty( $control );
+               $this->assertNotContains( '<form method="post">', $control );
+               $this->assertNotContains( '<div class="widget-content">', $control );
+
+               foreach ( $param_overrides as $contained ) {
+                       $this->assertContains( $contained, $control );
+               }
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></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-09-25 20:49:47 UTC (rev 34562)
+++ trunk/tests/qunit/fixtures/customize-settings.js    2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,9 +1,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> window.wp = window.wp || {};
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-window.wp.customize = window.wp.customize || { get: function(){}  };
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+window.wp.customize = window.wp.customize || { get: function() {} };
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> var customizerRootElement;
</span><span class="cx" style="display: block; padding: 0 10px"> customizerRootElement = jQuery( '<div id="customize-theme-controls"><ul></ul></div>' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-customizerRootElement.css( { position: 'absolute', left: -10000, top: -10000 } ); // remove from view
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+customizerRootElement.css( { position: 'absolute', left: -10000, top: -10000 } ); // Remove from view.
</ins><span class="cx" style="display: block; padding: 0 10px"> jQuery( document.body ).append( customizerRootElement );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> window._wpCustomizeSettings = {
</span></span></pre></div>
<a id="trunktestsqunitfixturescustomizewidgetsjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/qunit/fixtures/customize-widgets.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/fixtures/customize-widgets.js                           (rev 0)
+++ trunk/tests/qunit/fixtures/customize-widgets.js     2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,138 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+window._wpCustomizeWidgetsSettings = {
+       'nonce': '12cc9d3284',
+       'registeredSidebars': [{
+               'name': 'Widget Area',
+               'id': 'sidebar-1',
+               'description': 'Add widgets here to appear in your sidebar.',
+               'class': '',
+               'before_widget': '<aside id="%1$s" class="widget %2$s">',
+               'after_widget': '</aside>',
+               'before_title': '<h2 class="widget-title">',
+               'after_title': '</h2>'
+       }],
+       'registeredWidgets': {
+               'search-2': {
+                       'name': 'Search',
+                       'id': 'search-2',
+                       'params': [
+                               {
+                                       'number': 2
+                               }
+                       ],
+                       'classname': 'widget_search',
+                       'description': 'A search form for your site.'
+               }
+       },
+       'availableWidgets': [
+               {
+                       'name': 'Search',
+                       'id': 'search-2',
+                       'params': [
+                               {
+                                       'number': 2
+                               }
+                       ],
+                       'classname': 'widget_search',
+                       'description': 'A search form for your site.',
+                       'temp_id': 'search-__i__',
+                       'is_multi': true,
+                       'multi_number': 3,
+                       'is_disabled': false,
+                       'id_base': 'search',
+                       'transport': 'refresh',
+                       'width': 250,
+                       'height': 200,
+                       'is_wide': false
+               }
+       ],
+       'l10n': {
+               'saveBtnLabel': 'Apply',
+               'saveBtnTooltip': 'Save and preview changes before publishing them.',
+               'removeBtnLabel': 'Remove',
+               'removeBtnTooltip': 'Trash widget by moving it to the inactive widgets sidebar.',
+               'error': 'An error has occurred. Please reload the page and try again.',
+               'widgetMovedUp': 'Widget moved up',
+               'widgetMovedDown': 'Widget moved down'
+       },
+       'tpl': {
+               'widgetReorderNav': '<div class="widget-reorder-nav"><span class="move-widget" tabindex="0">Move to another area&hellip;</span><span class="move-widget-down" tabindex="0">Move down</span><span class="move-widget-up" tabindex="0">Move up</span></div>',
+               'moveWidgetArea': '<div class="move-widget-area"> <p class="description">Select an area to move this widget into:</p> <ul class="widget-area-select"> <% _.each( sidebars, function ( sidebar ){ %> <li class="" data-id="<%- sidebar.id %>" title="<%- sidebar.description %>" tabindex="0"><%- sidebar.name %></li> <% }); %> </ul> <div class="move-widget-actions"> <button class="move-widget-btn button-secondary" type="button">Move</button> </div> </div>'
+       }
+};
+
+window._wpCustomizeSettings.panels.widgets = {
+       'id': 'widgets',
+       'description': 'Widgets are independent sections of content that can be placed into widgetized areas provided by your theme (commonly called sidebars).',
+       'priority': 110,
+       'type': 'default',
+       'title': 'Widgets',
+       'content': '',
+       'active': true,
+       'instanceNumber': 1
+};
+
+window._wpCustomizeSettings.sections['sidebar-widgets-sidebar-1'] = {
+       'id': 'sidebar-widgets-sidebar-1',
+       'description': 'Add widgets here to appear in your sidebar.',
+       'priority': 0,
+       'panel': 'widgets',
+       'type': 'sidebar',
+       'title': 'Widget Area',
+       'content': '',
+       'active': false,
+       'instanceNumber': 1,
+       'customizeAction': 'Customizing &#9656; Widgets',
+       'sidebarId': 'sidebar-1'
+};
+
+window._wpCustomizeSettings.settings['widget_search[2]'] = {
+       'value': {
+               'encoded_serialized_instance': 'YToxOntzOjU6InRpdGxlIjtzOjY6IkJ1c2NhciI7fQ==',
+               'title': 'Buscar',
+               'is_widget_customizer_js_value': true,
+               'instance_hash_key': '45f0a7f15e50bd3be86b141e2a8b3aaf'
+       },
+       'transport': 'refresh',
+       'dirty': false
+};
+window._wpCustomizeSettings.settings['sidebars_widgets[sidebar-1]'] = {
+       'value': [ 'search-2' ],
+       'transport': 'refresh',
+       'dirty': false
+};
+
+window._wpCustomizeSettings.controls['widget_search[2]'] = {
+       'settings': {
+               'default': 'widget_search[2]'
+       },
+       'type': 'widget_form',
+       'priority': 0,
+       'active': false,
+       'section': 'sidebar-widgets-sidebar-1',
+       'content': '<li id="customize-control-widget_search-2" class="customize-control customize-control-widget_form"> <\/li>',
+       'label': 'Search',
+       'description': '',
+       'instanceNumber': 2,
+       'widget_id': 'search-2',
+       'widget_id_base': 'search',
+       'sidebar_id': 'sidebar-1',
+       'width': 250,
+       'height': 200,
+       'is_wide': false,
+       'widget_control': '<div id="widget-15_search-2" class="widget"> <div class="widget-top"> <div class="widget-title-action"> <a class="widget-action hide-if-no-js" href="#available-widgets"><\/a> <a class="widget-control-edit hide-if-js" href="\/wp-admin\/customize.php?editwidget=search-2&#038;key=-1"> <span class="edit">Edit<\/span> <span class="add">Add<\/span> <span class="screen-reader-text">Search<\/span> <\/a> <\/div> <div class="widget-title"><h4>Search<span class="in-widget-title"><\/span><\/h4><\/div> <\/div> <div class="widget-inside"> <div class="form"> <div class="widget-content"><\/div><!-- .widget-content --> <input type="hidden" name="wi
 dget-id" class="widget-id" value="search-2" \/> <input type="hidden" name="id_base" class="id_base" value="search" \/> <input type="hidden" name="widget-width" class="widget-width" value="250" \/> <input type="hidden" name="widget-height" class="widget-height" value="200" \/> <input type="hidden" name="widget_number" class="widget_number" value="2" \/> <input type="hidden" name="multi_number" class="multi_number" value="" \/> <input type="hidden" name="add_new" class="add_new" value="" \/> <div class="widget-control-actions"> <div class="alignleft"> <a class="widget-control-remove" href="#remove">Delete<\/a> |   <a cla
 ss="widget-control-close" href="#close">Close<\/a> <\/div> <div class="alignright"> <input type="submit" name="savewidget" id="widget-search-2-savewidget" class="button button-primary widget-control-save right" value="Save"  \/> <span class="spinner"><\/span> <\/div> <br class="clear" \/> <\/div> <\/div><!-- .form --> <\/div> <div class="widget-description"> A search form for your site.  <\/div> <\/div>',
+       'widget_content': '<p><label for="widget-search-2-title">Title: <input class="widefat" id="widget-search-2-title" name="widget-search[2][title]" type="text" value="Buscar" \/><\/label><\/p>'
+};
+window._wpCustomizeSettings.controls['sidebars_widgets[sidebar-1]'] = {
+       'settings': {
+               'default': 'sidebars_widgets[sidebar-1]'
+       },
+       'type': 'sidebar_widgets',
+       'priority': 99,
+       'active': true,
+       'section': 'sidebar-widgets-sidebar-1',
+       'content': '<li id="customize-control-sidebars_widgets-sidebar-1" class="customize-control customize-control-sidebar_widgets"> <span class="button-secondary add-new-widget" tabindex="0">    Add a Widget  <\/span> <span class="reorder-toggle" tabindex="0"> <span class="reorder">Reorder<\/span> <span class="reorder-done">Done<\/span> <\/span> <\/li>',
+       'label': '',
+       'description': '',
+       'instanceNumber': 1,
+       'sidebar_id': 'sidebar-1'
+};
</ins></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-09-25 20:49:47 UTC (rev 34562)
+++ trunk/tests/qunit/index.html        2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -25,6 +25,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        <script src="fixtures/customize-header.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                        <script src="fixtures/customize-settings.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                        <script src="fixtures/customize-menus.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        <script src="fixtures/customize-widgets.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px">                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">                <p><a href="editor">TinyMCE tests</a></p>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -44,6 +45,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="../../src/wp-admin/js/nav-menu.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="../../src/wp-admin/js/customize-nav-menus.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <script src="../../src/wp-admin/js/customize-widgets.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <script src="../../src/wp-admin/js/word-count.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                <!-- Unit tests -->
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -54,6 +56,7 @@
</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><span class="cx" style="display: block; padding: 0 10px">                <script src="wp-admin/js/customize-nav-menus.js"></script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <script src="wp-admin/js/customize-widgets.js"></script>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <script src="wp-admin/js/word-count.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                <!-- Customizer templates for sections -->
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -267,6 +270,36 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        <div class="menu-item-reorder-nav">
</span><span class="cx" style="display: block; padding: 0 10px">                                <button type="button" class="menus-move-up">Move up</button><button type="button" class="menus-move-down">Move down</button><button type="button" class="menus-move-left">Move one level up</button><button type="button" class="menus-move-right">Move one level down</button>                 </div>
</span><span class="cx" style="display: block; padding: 0 10px">                </script>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               <script type="text/html" id="tmpl-customize-section-sidebar">
+                       <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 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">
+                                                               {{{ data.customizeAction }}}
+                                                       </span>
+                                                       {{ data.title }}
+                                               </h3>
+                                       </div>
+                                       <# if ( data.description ) { #>
+                                               <div class="description customize-section-description">
+                                                       {{{ data.description }}}
+                                               </div>
+                                       <# } #>
+                               </li>
+                       </ul>
+                       </li>
+               </script>
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 <div hidden>
</span><span class="cx" style="display: block; padding: 0 10px">                        <div id="available-menu-items" class="accordion-container">
</span><span class="cx" style="display: block; padding: 0 10px">                        <div class="customize-section-title">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -375,7 +408,69 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                </div><!-- #available-menu-items -->
</span><span class="cx" style="display: block; padding: 0 10px">                        </div><!-- end nav menu templates -->
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <div hidden>
+                       <div id="widgets-left"><!-- compatibility with JS which looks for widget templates here -->
+                               <div id="available-widgets">
+                                       <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; Widgets</span>
+                                                       Add a Widget                            </h3>
+                                       </div>
+                                       <div id="available-widgets-filter">
+                                               <label class="screen-reader-text" for="widgets-search">Search Widgets</label>
+                                               <input type="search" id="widgets-search" placeholder="Search widgets&hellip;" />
+                                       </div>
+                                       <div id="available-widgets-list">
+                                                                       <div id="widget-tpl-search-2" data-widget-id="search-2" class="widget-tpl search-2" tabindex="0">
+                                                       <div id='widget-11_search-__i__' class='widget'>  <div class="widget-top">
+                       <div class="widget-title-action">
+                               <a class="widget-action hide-if-no-js" href="#available-widgets"></a>
+                               <a class="widget-control-edit hide-if-js" href="/wp-admin/customize.php?editwidget=search-2&#038;addnew=1&#038;num=3&#038;base=search">
+                                       <span class="edit">Edit</span>
+                                       <span class="add">Add</span>
+                                       <span class="screen-reader-text">Search</span>
+                               </a>
+                       </div>
+                       <div class="widget-title"><h4>Search<span class="in-widget-title"></span></h4></div>
+                       </div>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        <div class="widget-inside">
+                       <div class="form">
+                       <div class="widget-content">
+                               <p><label for="widget-search-__i__-title">Title: <input class="widefat" id="widget-search-__i__-title" name="widget-search[__i__][title]" type="text" value="" /></label></p>
+                       </div>
+                       <input type="hidden" name="widget-id" class="widget-id" value="search-__i__" />
+                       <input type="hidden" name="id_base" class="id_base" value="search" />
+                       <input type="hidden" name="widget-width" class="widget-width" value="250" />
+                       <input type="hidden" name="widget-height" class="widget-height" value="200" />
+                       <input type="hidden" name="widget_number" class="widget_number" value="2" />
+                       <input type="hidden" name="multi_number" class="multi_number" value="3" />
+                       <input type="hidden" name="add_new" class="add_new" value="multi" />
+
+                       <div class="widget-control-actions">
+                               <div class="alignleft">
+                               <a class="widget-control-remove" href="#remove">Delete</a> |
+                               <a class="widget-control-close" href="#close">Close</a>
+                               </div>
+                               <div class="alignright">
+                                       <input type="submit" name="savewidget" id="widget-search-__i__-savewidget" class="button button-primary widget-control-save right" value="Save"  />                     <span class="spinner"></span>
+                               </div>
+                               <br class="clear" />
+                       </div>
+                       </div><!-- .form -->
+                       </div>
+
+                       <div class="widget-description">
+               A search form for your site.
+                       </div>
+               </div>                            </div>
+                                       </div><!-- #available-widgets-list -->
+                               </div><!-- #available-widgets -->
+                       </div><!-- #widgets-left -->
+               </div><!-- end widget templates -->
</ins><span class="cx" style="display: block; padding: 0 10px">                 <script src="../../src/wp-includes/js/tinymce/tinymce.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="editor/js/utils.js"></script>
</span><span class="cx" style="display: block; padding: 0 10px">                <script src="wp-includes/js/tinymce/plugins/wptextpattern/plugin.js"></script>
</span></span></pre></div>
<a id="trunktestsqunitwpadminjscustomizewidgetsjs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: trunk/tests/qunit/wp-admin/js/customize-widgets.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/qunit/wp-admin/js/customize-widgets.js                                (rev 0)
+++ trunk/tests/qunit/wp-admin/js/customize-widgets.js  2015-09-25 21:01:46 UTC (rev 34563)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,50 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/* global wp */
+jQuery( window ).load( function() {
+
+       var api = wp.customize, $ = jQuery;
+
+       module( 'Customize Widgets' );
+
+       test( 'fixtures should be present', function() {
+               var widgetControl;
+               ok( api.panel( 'widgets' ) );
+               ok( api.section( 'sidebar-widgets-sidebar-1' ) );
+               widgetControl = api.control( 'widget_search[2]' );
+               ok( widgetControl );
+               ok( api.control( 'sidebars_widgets[sidebar-1]' ) );
+               ok( api( 'widget_search[2]' ) );
+               ok( api( 'sidebars_widgets[sidebar-1]' ) );
+               ok( widgetControl.params.content );
+               ok( widgetControl.params.widget_control );
+               ok( widgetControl.params.widget_content );
+               ok( widgetControl.params.widget_id );
+               ok( widgetControl.params.widget_id_base );
+       });
+
+       test( 'widget contents should embed (with widget-added event) when section and control expand', function() {
+               var control, section, widgetAddedEvent = null, widgetControlRootElement = null;
+               control = api.control( 'widget_search[2]' );
+               section = api.section( 'sidebar-widgets-sidebar-1' );
+
+               $( document ).on( 'widget-added', function( event, widgetElement ) {
+                       widgetAddedEvent = event;
+                       widgetControlRootElement = widgetElement;
+               });
+
+               ok( ! section.expanded() );
+               ok( 0 === control.container.find( '> .widget' ).length );
+
+               section.expand();
+               ok( ! widgetAddedEvent );
+               ok( 1 === control.container.find( '> .widget' ).length );
+               ok( 0 === control.container.find( '.widget-content' ).children().length );
+
+               control.expand();
+               ok( 1 === control.container.find( '.widget-content' ).children().length );
+               ok( widgetAddedEvent );
+               ok( widgetControlRootElement.is( control.container.find( '> .widget' ) ) );
+               ok( 1 === control.container.find( '.widget-content #widget-search-2-title' ).length );
+
+               $( document ).off( 'widget-added' );
+       });
+});
</ins></span></pre>
</div>
</div>

</body>
</html>