<!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>[27985] trunk/src: Widget Customizer: Move `WidgetCustomizer` to `wp.customize.Widgets`.</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">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/27985">27985</a></dd>
<dt>Author</dt> <dd>ocean90</dd>
<dt>Date</dt> <dd>2014-04-07 09:03:18 +0000 (Mon, 07 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Widget Customizer: Move `WidgetCustomizer` to `wp.customize.Widgets`. First pass.

see <a href="http://core.trac.wordpress.org/ticket/27690">#27690</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadmincsscustomizecontrolscss">trunk/src/wp-admin/css/customize-controls.css</a></li>
<li><a href="#trunksrcwpadmincsscustomizewidgetscss">trunk/src/wp-admin/css/customize-widgets.css</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="#trunksrcwpincludesjscustomizepreviewwidgetsjs">trunk/src/wp-includes/js/customize-preview-widgets.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadmincsscustomizecontrolscss"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/css/customize-controls.css (27984 => 27985)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/css/customize-controls.css    2014-04-07 09:02:06 UTC (rev 27984)
+++ trunk/src/wp-admin/css/customize-controls.css       2014-04-07 09:03:18 UTC (rev 27985)
</span><span class="lines">@@ -490,6 +490,7 @@
</span><span class="cx">  overflow: hidden;
</span><span class="cx">  -webkit-border-radius: 2px;
</span><span class="cx">  border: 1px solid #eee;
</span><ins>+       -webkit-border-radius: 2px;
</ins><span class="cx">   border-radius: 2px;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpadmincsscustomizewidgetscss"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/css/customize-widgets.css (27984 => 27985)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/css/customize-widgets.css     2014-04-07 09:02:06 UTC (rev 27984)
+++ trunk/src/wp-admin/css/customize-widgets.css        2014-04-07 09:03:18 UTC (rev 27985)
</span><span class="lines">@@ -108,13 +108,6 @@
</span><span class="cx">  display: none;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-
-/* MP6-compat */
-#customize-theme-controls .accordion-section-content .widget {
-       color: black;
-}
-
-
</del><span class="cx"> /**
</span><span class="cx"> * Widget reordering styles
</span><span class="cx"> **/
</span><span class="lines">@@ -326,7 +319,7 @@
</span><span class="cx"> 
</span><span class="cx"> body.adding-widget .add-new-widget,
</span><span class="cx"> body.adding-widget .add-new-widget:hover {
</span><del>-       background: #EEE;
</del><ins>+        background: #eee;
</ins><span class="cx">   border-color: #999;
</span><span class="cx">  color: #333;
</span><span class="cx">  -webkit-box-shadow: inset 0 2px 5px -3px rgba(0, 0, 0, 0.5);
</span></span></pre></div>
<a id="trunksrcwpadminjscustomizewidgetsjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/js/customize-widgets.js (27984 => 27985)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/js/customize-widgets.js       2014-04-07 09:02:06 UTC (rev 27984)
+++ trunk/src/wp-admin/js/customize-widgets.js  2014-04-07 09:03:18 UTC (rev 27985)
</span><span class="lines">@@ -1,45 +1,23 @@
</span><del>-/*global wp, Backbone, _, jQuery, WidgetCustomizer_exports */
-/*exported WidgetCustomizer */
-var WidgetCustomizer = ( function ($) {
-       'use strict';
</del><ins>+/* global _wpCustomizeWidgetsSettings */
+(function( wp, $ ){
</ins><span class="cx"> 
</span><del>-       var Widget,
-               WidgetCollection,
-               Sidebar,
-               SidebarCollection,
-               OldPreviewer,
-               builtin_form_sync_handlers,
-               customize = wp.customize, self = {
-               nonce: null,
-               i18n: {
-                       save_btn_label: '',
-                       save_btn_tooltip: '',
-                       remove_btn_label: '',
-                       remove_btn_tooltip: '',
-                       error: ''
-               },
-               available_widgets: [], // available widgets for instantiating
-               registered_widgets: [], // all widgets registered
-               active_sidebar_control: null,
-               previewer: null,
-               saved_widget_ids: {},
-               registered_sidebars: [],
-               tpl: {
-                       move_widget_area: '',
-                       widget_reorder_nav: ''
-               }
-       };
-       $.extend( self, WidgetCustomizer_exports );
</del><ins>+        if ( ! wp || ! wp.customize ) { return; }
</ins><span class="cx"> 
</span><del>-       // Lots of widgets expect this old ajaxurl global to be available
-       if ( typeof window.ajaxurl === 'undefined' ) {
-               window.ajaxurl = wp.ajax.settings.url;
-       }
</del><ins>+        // Set up our namespace...
+       var api = wp.customize,
+               l10n, OldPreviewer;
</ins><span class="cx"> 
</span><ins>+       api.Widgets = api.Widgets || {};
+
+       // Link settings
+       api.Widgets.data = _wpCustomizeWidgetsSettings || {};
+       l10n = api.Widgets.data.l10n;
+       delete api.Widgets.data.l10n;
+
</ins><span class="cx">   /**
</span><span class="cx">   * Set up model
</span><span class="cx">   */
</span><del>-       Widget = self.Widget = Backbone.Model.extend( {
</del><ins>+        api.Widgets.WidgetModel = Backbone.Model.extend({
</ins><span class="cx">           id: null,
</span><span class="cx">          temp_id: null,
</span><span class="cx">          classname: null,
</span><span class="lines">@@ -54,10 +32,10 @@
</span><span class="cx">          params: [],
</span><span class="cx">          width: null,
</span><span class="cx">          height: null
</span><del>-       } );
</del><ins>+        });
</ins><span class="cx"> 
</span><del>-       WidgetCollection = self.WidgetCollection = Backbone.Collection.extend( {
-               model: Widget,
</del><ins>+        api.Widgets.WidgetCollection = Backbone.Collection.extend({
+               model: api.Widgets.WidgetModel,
</ins><span class="cx"> 
</span><span class="cx">          // Controls searching on the current widget collection
</span><span class="cx">          // and triggers an update event
</span><span class="lines">@@ -80,7 +58,7 @@
</span><span class="cx">                  // If search is blank, show all themes
</span><span class="cx">                  // Useful for resetting the views when you clean the input
</span><span class="cx">                  if ( this.terms === '' ) {
</span><del>-                               this.reset( WidgetCustomizer_exports.available_widgets );
</del><ins>+                                this.reset( api.Widgets.data.availableWidgets );
</ins><span class="cx">                   }
</span><span class="cx"> 
</span><span class="cx">                  // Trigger an 'update' event
</span><span class="lines">@@ -93,7 +71,7 @@
</span><span class="cx">                  var match, results, haystack;
</span><span class="cx"> 
</span><span class="cx">                  // Start with a full collection
</span><del>-                       this.reset( WidgetCustomizer_exports.available_widgets, { silent: true } );
</del><ins>+                        this.reset( api.Widgets.data.availableWidgets, { silent: true } );
</ins><span class="cx"> 
</span><span class="cx">                  // Escape the term string for RegExp meta characters
</span><span class="cx">                  term = term.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' );
</span><span class="lines">@@ -111,10 +89,10 @@
</span><span class="cx"> 
</span><span class="cx">                  this.reset( results );
</span><span class="cx">          }
</span><del>-       } );
-       self.available_widgets = new WidgetCollection( self.available_widgets );
</del><ins>+        });
+       api.Widgets.availableWidgets = new api.Widgets.WidgetCollection( api.Widgets.data.availableWidgets );
</ins><span class="cx"> 
</span><del>-       Sidebar = self.Sidebar = Backbone.Model.extend( {
</del><ins>+        api.Widgets.SidebarModel = Backbone.Model.extend({
</ins><span class="cx">           after_title: null,
</span><span class="cx">          after_widget: null,
</span><span class="cx">          before_title: null,
</span><span class="lines">@@ -124,502 +102,44 @@
</span><span class="cx">          id: null,
</span><span class="cx">          name: null,
</span><span class="cx">          is_rendered: false
</span><del>-       } );
</del><ins>+        });
</ins><span class="cx"> 
</span><del>-       SidebarCollection = self.SidebarCollection = Backbone.Collection.extend( {
-               model: Sidebar
-       } );
-       self.registered_sidebars = new SidebarCollection( self.registered_sidebars );
</del><ins>+        api.Widgets.SidebarCollection = Backbone.Collection.extend({
+               model: api.Widgets.SidebarModel
+       });
+       api.Widgets.registeredSidebars = new api.Widgets.SidebarCollection( api.Widgets.data.registeredSidebars );
</ins><span class="cx"> 
</span><span class="cx">  /**
</span><span class="cx">   * Handlers for the widget-synced event, organized by widget ID base.
</span><span class="cx">   * Other widgets may provide their own update handlers by adding
</span><span class="cx">   * listeners for the widget-synced event.
</span><span class="cx">   */
</span><del>-       builtin_form_sync_handlers = {
</del><ins>+        api.Widgets.formSyncHandlers = {
</ins><span class="cx"> 
</span><span class="cx">          /**
</span><span class="cx">           * @param {jQuery.Event} e
</span><del>-                * @param {jQuery} widget_el
-                * @param {String} new_form
</del><ins>+                 * @param {jQuery} widget
+                * @param {String} newForm
</ins><span class="cx">            */
</span><del>-               rss: function ( e, widget_el, new_form ) {
-                       var old_widget_error = widget_el.find( '.widget-error:first' ),
-                               new_widget_error = $( '<div>' + new_form + '</div>' ).find( '.widget-error:first' );
</del><ins>+                rss: function ( e, widget, newForm ) {
+                       var oldWidgetError = widget.find( '.widget-error:first' ),
+                               newWidgetError = $( '<div>' + newForm + '</div>' ).find( '.widget-error:first' );
</ins><span class="cx"> 
</span><del>-                       if ( old_widget_error.length && new_widget_error.length ) {
-                               old_widget_error.replaceWith( new_widget_error );
-                       } else if ( old_widget_error.length ) {
-                               old_widget_error.remove();
-                       } else if ( new_widget_error.length ) {
-                               widget_el.find( '.widget-content:first' ).prepend( new_widget_error );
</del><ins>+                        if ( oldWidgetError.length && newWidgetError.length ) {
+                               oldWidgetError.replaceWith( newWidgetError );
+                       } else if ( oldWidgetError.length ) {
+                               oldWidgetError.remove();
+                       } else if ( newWidgetError.length ) {
+                               widget.find( '.widget-content:first' ).prepend( newWidgetError );
</ins><span class="cx">                   }
</span><span class="cx">          }
</span><span class="cx">  };
</span><span class="cx"> 
</span><span class="cx">  /**
</span><del>-        * On DOM ready, initialize some meta functionality independent of specific
-        * customizer controls.
-        */
-       self.init = function () {
-               this.availableWidgetsPanel.setup();
-
-               // Highlight widget control
-               this.previewer.bind( 'highlight-widget-control', self.highlightWidgetFormControl );
-
-               // Open and focus widget control
-               this.previewer.bind( 'focus-widget-control', self.focusWidgetFormControl );
-       };
-       wp.customize.bind( 'ready', function () {
-               self.init();
-       } );
-
-       /**
-        * Sidebar Widgets control
-        * Note that 'sidebar_widgets' must match the Sidebar_Widgets_WP_Customize_Control::$type
-        */
-       customize.controlConstructor.sidebar_widgets = customize.Control.extend( {
-
-               /**
-                * Set up the control
-                */
-               ready: function() {
-                       var control = this;
-                       control.control_section = control.container.closest( '.control-section' );
-                       control.section_content = control.container.closest( '.accordion-section-content' );
-                       control._setupModel();
-                       control._setupSortable();
-                       control._setupAddition();
-                       control._applyCardinalOrderClassNames();
-               },
-
-               /**
-                * Update ordering of widget control forms when the setting is updated
-                */
-               _setupModel: function() {
-                       var control = this,
-                               registered_sidebar = self.registered_sidebars.get( control.params.sidebar_id );
-
-                       control.setting.bind( function( new_widget_ids, old_widget_ids ) {
-                               var widget_form_controls,
-                                       sidebar_widgets_add_control,
-                                       final_control_containers,
-                                       removed_widget_ids = _( old_widget_ids ).difference( new_widget_ids );
-
-                               // Filter out any persistent widget_ids for widgets which have been deactivated
-                               new_widget_ids = _( new_widget_ids ).filter( function ( new_widget_id ) {
-                                       var parsed_widget_id = parse_widget_id( new_widget_id );
-                                       return !! self.available_widgets.findWhere( { id_base: parsed_widget_id.id_base } );
-                               } );
-
-                               widget_form_controls = _( new_widget_ids ).map( function ( widget_id ) {
-                                       var widget_form_control = self.getWidgetFormControlForWidget( widget_id );
-                                       if ( ! widget_form_control ) {
-                                               widget_form_control = control.addWidget( widget_id );
-                                       }
-                                       return widget_form_control;
-                               } );
-
-                               // Sort widget controls to their new positions
-                               widget_form_controls.sort( function ( a, b ) {
-                                       var a_index = _.indexOf( new_widget_ids, a.params.widget_id ),
-                                               b_index = _.indexOf( new_widget_ids, b.params.widget_id );
-                                       if ( a_index === b_index ) {
-                                               return 0;
-                                       }
-                                       return a_index < b_index ? -1 : 1;
-                               } );
-
-                               sidebar_widgets_add_control = control.section_content.find( '.customize-control-sidebar_widgets' );
-
-                               // Append the controls to put them in the right order
-                               final_control_containers = _( widget_form_controls ).map( function( widget_form_controls ) {
-                                       return widget_form_controls.container[0];
-                               } );
-
-                               // Re-sort widget form controls (including widgets form other sidebars newly moved here)
-                               sidebar_widgets_add_control.before( final_control_containers );
-                               control._applyCardinalOrderClassNames();
-
-                               // If the widget was dragged into the sidebar, make sure the sidebar_id param is updated
-                               _( widget_form_controls ).each( function ( widget_form_control ) {
-                                       widget_form_control.params.sidebar_id = control.params.sidebar_id;
-                               } );
-
-                               // Cleanup after widget removal
-                               _( removed_widget_ids ).each( function ( removed_widget_id ) {
-
-                                       // Using setTimeout so that when moving a widget to another sidebar, the other sidebars_widgets settings get a chance to update
-                                       setTimeout( function () {
-                                               var is_present_in_another_sidebar = false,
-                                                       removed_control,
-                                                       was_dragged_to_another_sidebar,
-                                                       inactive_widgets,
-                                                       removed_id_base,
-                                                       widget;
-
-                                               // Check if the widget is in another sidebar
-                                               wp.customize.each( function ( other_setting ) {
-                                                       if ( other_setting.id === control.setting.id || 0 !== other_setting.id.indexOf( 'sidebars_widgets[' ) || other_setting.id === 'sidebars_widgets[wp_inactive_widgets]' ) {
-                                                               return;
-                                                       }
-                                                       var other_sidebar_widgets = other_setting(), i;
-
-                                                       i = _.indexOf( other_sidebar_widgets, removed_widget_id );
-                                                       if ( -1 !== i ) {
-                                                               is_present_in_another_sidebar = true;
-                                                       }
-                                               } );
-
-                                               // If the widget is present in another sidebar, abort!
-                                               if ( is_present_in_another_sidebar ) {
-                                                       return;
-                                               }
-
-                                               removed_control = self.getWidgetFormControlForWidget( removed_widget_id );
-
-                                               // Detect if widget control was dragged to another sidebar
-                                               was_dragged_to_another_sidebar = (
-                                                       removed_control &&
-                                                       $.contains( document, removed_control.container[0] ) &&
-                                                       ! $.contains( control.section_content[0], removed_control.container[0] )
-                                               );
-
-                                               // Delete any widget form controls for removed widgets
-                                               if ( removed_control && ! was_dragged_to_another_sidebar ) {
-                                                       wp.customize.control.remove( removed_control.id );
-                                                       removed_control.container.remove();
-                                               }
-
-                                               // Move widget to inactive widgets sidebar (move it to trash) if has been previously saved
-                                               // This prevents the inactive widgets sidebar from overflowing with throwaway widgets
-                                               if ( self.saved_widget_ids[removed_widget_id] ) {
-                                                       inactive_widgets = wp.customize.value( 'sidebars_widgets[wp_inactive_widgets]' )().slice();
-                                                       inactive_widgets.push( removed_widget_id );
-                                                       wp.customize.value( 'sidebars_widgets[wp_inactive_widgets]' )( _( inactive_widgets ).unique() );
-                                               }
-
-                                               // Make old single widget available for adding again
-                                               removed_id_base = parse_widget_id( removed_widget_id ).id_base;
-                                               widget = self.available_widgets.findWhere( { id_base: removed_id_base } );
-                                               if ( widget && ! widget.get( 'is_multi' ) ) {
-                                                       widget.set( 'is_disabled', false );
-                                               }
-                                       } );
-
-                               } );
-                       } );
-
-                       // Update the model with whether or not the sidebar is rendered
-                       self.previewer.bind( 'rendered-sidebars', function ( rendered_sidebars ) {
-                               var is_rendered = !! rendered_sidebars[control.params.sidebar_id];
-                               registered_sidebar.set( 'is_rendered', is_rendered );
-                       } );
-
-                       // Show the sidebar section when it becomes visible
-                       registered_sidebar.on( 'change:is_rendered', function ( ) {
-                               var section_selector = '#accordion-section-sidebar-widgets-' + this.get( 'id' ), section;
-                               section = $( section_selector );
-                               if ( this.get( 'is_rendered' ) ) {
-                                       section.stop().slideDown( function () {
-                                               $( this ).css( 'height', 'auto' ); // so that the .accordion-section-content won't overflow
-                                       } );
-                               } else {
-                                       // Make sure that hidden sections get closed first
-                                       if ( section.hasClass( 'open' ) ) {
-                                               // it would be nice if accordionSwitch() in accordion.js was public
-                                               section.find( '.accordion-section-title' ).trigger( 'click' );
-                                       }
-                                       section.stop().slideUp();
-                               }
-                       } );
-               },
-
-               /**
-                * Allow widgets in sidebar to be re-ordered, and for the order to be previewed
-                */
-               _setupSortable: function () {
-                       var control = this;
-                       control.is_reordering = false;
-
-                       /**
-                        * Update widget order setting when controls are re-ordered
-                        */
-                       control.section_content.sortable( {
-                               items: '> .customize-control-widget_form',
-                               handle: '.widget-top',
-                               axis: 'y',
-                               connectWith: '.accordion-section-content:has(.customize-control-sidebar_widgets)',
-                               update: function () {
-                                       var widget_container_ids = control.section_content.sortable( 'toArray' ), widget_ids;
-                                       widget_ids = $.map( widget_container_ids, function ( widget_container_id ) {
-                                               return $( '#' + widget_container_id ).find( ':input[name=widget-id]' ).val();
-                                       } );
-                                       control.setting( widget_ids );
-                               }
-                       } );
-
-                       /**
-                        * Expand other customizer sidebar section when dragging a control widget over it,
-                        * allowing the control to be dropped into another section
-                        */
-                       control.control_section.find( '.accordion-section-title' ).droppable( {
-                               accept: '.customize-control-widget_form',
-                               over: function () {
-                                       if ( ! control.control_section.hasClass( 'open' ) ) {
-                                               control.control_section.addClass( 'open' );
-                                               control.section_content.toggle( false ).slideToggle( 150, function () {
-                                                       control.section_content.sortable( 'refreshPositions' );
-                                               } );
-                                       }
-                               }
-                       } );
-
-                       /**
-                        * Keyboard-accessible reordering
-                        */
-                       control.container.find( '.reorder-toggle' ).on( 'click keydown', function( event ) {
-                               if ( event.type === 'keydown' && ! ( event.which === 13 || event.which === 32 ) ) { // Enter or Spacebar
-                                       return;
-                               }
-
-                               control.toggleReordering( ! control.is_reordering );
-                       } );
-               },
-
-               /**
-                * Set up UI for adding a new widget
-                */
-               _setupAddition: function () {
-                       var control = this;
-
-                       control.container.find( '.add-new-widget' ).on( 'click keydown', function( event ) {
-                               if ( event.type === 'keydown' && ! ( event.which === 13 || event.which === 32 ) ) { // Enter or Spacebar
-                                       return;
-                               }
-
-                               if ( control.section_content.hasClass( 'reordering' ) ) {
-                                       return;
-                               }
-
-                               // @todo Use an control.is_adding state
-                               if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) {
-                                       self.availableWidgetsPanel.open( control );
-                               } else {
-                                       self.availableWidgetsPanel.close();
-                               }
-                       } );
-               },
-
-               /**
-                * Add classes to the widget_form controls to assist with styling
-                */
-               _applyCardinalOrderClassNames: function () {
-                       var control = this;
-                       control.section_content.find( '.customize-control-widget_form' )
-                               .removeClass( 'first-widget' )
-                               .removeClass( 'last-widget' )
-                               .find( '.move-widget-down, .move-widget-up' ).prop( 'tabIndex', 0 );
-
-                       control.section_content.find( '.customize-control-widget_form:first' )
-                               .addClass( 'first-widget' )
-                               .find( '.move-widget-up' ).prop( 'tabIndex', -1 );
-                       control.section_content.find( '.customize-control-widget_form:last' )
-                               .addClass( 'last-widget' )
-                               .find( '.move-widget-down' ).prop( 'tabIndex', -1 );
-               },
-
-
-               /***********************************************************************
-                * Begin public API methods
-                **********************************************************************/
-
-               /**
-                * Enable/disable the reordering UI
-                *
-                * @param {Boolean} toggle to enable/disable reordering
-                */
-               toggleReordering: function ( toggle ) {
-                       var control = this;
-                       toggle = Boolean( toggle );
-                       if ( toggle === control.section_content.hasClass( 'reordering' ) ) {
-                               return;
-                       }
-
-                       control.is_reordering = toggle;
-                       control.section_content.toggleClass( 'reordering', toggle );
-
-                       if ( toggle ) {
-                               _( control.getWidgetFormControls() ).each( function ( form_control ) {
-                                       form_control.collapseForm();
-                               } );
-                       }
-               },
-
-               /**
-                * @return {wp.customize.controlConstructor.widget_form[]}
-                */
-               getWidgetFormControls: function () {
-                       var control = this, form_controls;
-
-                       form_controls = _( control.setting() ).map( function ( widget_id ) {
-                               var setting_id = widget_id_to_setting_id( widget_id ),
-                                       form_control = customize.control( setting_id );
-
-                               if ( ! form_control ) {
-                                       throw new Error( 'Unable to find widget_form control for ' + widget_id );
-                               }
-                               return form_control;
-                       } );
-                       return form_controls;
-               },
-
-               /**
-                * @param {string} widget_id or an id_base for adding a previously non-existing widget
-                * @returns {object} widget_form control instance
-                */
-               addWidget: function ( widget_id ) {
-                       var control = this,
-                               control_html,
-                               widget_el,
-                               customize_control_type = 'widget_form',
-                               customize_control,
-                               parsed_widget_id = parse_widget_id( widget_id ),
-                               widget_number = parsed_widget_id.number,
-                               widget_id_base = parsed_widget_id.id_base,
-                               widget = self.available_widgets.findWhere( {id_base: widget_id_base} ),
-                               setting_id,
-                               is_existing_widget,
-                               Constructor,
-                               widget_form_control,
-                               sidebar_widgets,
-                               setting_args;
-
-                       if ( ! widget ) {
-                               throw new Error( 'Widget unexpectedly not found.' );
-                       }
-                       if ( widget_number && ! widget.get( 'is_multi' ) ) {
-                               throw new Error( 'Did not expect a widget number to be supplied for a non-multi widget' );
-                       }
-
-                       // Set up new multi widget
-                       if ( widget.get( 'is_multi' ) && ! widget_number ) {
-                               widget.set( 'multi_number', widget.get( 'multi_number' ) + 1 );
-                               widget_number = widget.get( 'multi_number' );
-                       }
-
-                       control_html = $( '#widget-tpl-' + widget.get( 'id' ) ).html();
-                       if ( widget.get( 'is_multi' ) ) {
-                               control_html = control_html.replace( /<[^<>]+>/g, function ( m ) {
-                                       return m.replace( /__i__|%i%/g, widget_number );
-                               } );
-                       } else {
-                               widget.set( 'is_disabled', true ); // Prevent single widget from being added again now
-                       }
-                       widget_el = $( control_html );
-
-                       customize_control = $( '<li></li>' );
-                       customize_control.addClass( 'customize-control' );
-                       customize_control.addClass( 'customize-control-' + customize_control_type );
-                       customize_control.append( widget_el );
-                       customize_control.find( '> .widget-icon' ).remove();
-                       if ( widget.get( 'is_multi' ) ) {
-                               customize_control.find( 'input[name="widget_number"]' ).val( widget_number );
-                               customize_control.find( 'input[name="multi_number"]' ).val( widget_number );
-                       }
-                       widget_id = customize_control.find( '[name="widget-id"]' ).val();
-                       customize_control.hide(); // to be slid-down below
-
-                       setting_id = 'widget_' + widget.get( 'id_base' );
-                       if ( widget.get( 'is_multi' ) ) {
-                               setting_id += '[' + widget_number + ']';
-                       }
-                       customize_control.attr( 'id', 'customize-control-' + setting_id.replace( /\]/g, '' ).replace( /\[/g, '-' ) );
-
-                       control.container.after( customize_control );
-
-                       // Only create setting if it doesn't already exist (if we're adding a pre-existing inactive widget)
-                       is_existing_widget = wp.customize.has( setting_id );
-                       if ( ! is_existing_widget ) {
-                               setting_args = {
-                                       transport: 'refresh',
-                                       previewer: control.setting.previewer
-                               };
-                               wp.customize.create( setting_id, setting_id, {}, setting_args );
-                       }
-
-                       Constructor = wp.customize.controlConstructor[customize_control_type];
-                       widget_form_control = new Constructor( setting_id, {
-                               params: {
-                                       settings: {
-                                               'default': setting_id
-                                       },
-                                       sidebar_id: control.params.sidebar_id,
-                                       widget_id: widget_id,
-                                       widget_id_base: widget.get( 'id_base' ),
-                                       type: customize_control_type,
-                                       is_new: ! is_existing_widget,
-                                       width: widget.get( 'width' ),
-                                       height: widget.get( 'height' ),
-                                       is_wide: widget.get( 'is_wide' )
-                               },
-                               previewer: control.setting.previewer
-                       } );
-                       wp.customize.control.add( setting_id, widget_form_control );
-
-                       // Make sure widget is removed from the other sidebars
-                       wp.customize.each( function ( other_setting ) {
-                               if ( other_setting.id === control.setting.id ) {
-                                       return;
-                               }
-                               if ( 0 !== other_setting.id.indexOf( 'sidebars_widgets[' ) ) {
-                                       return;
-                               }
-                               var other_sidebar_widgets = other_setting().slice(), i;
-                               i = _.indexOf( other_sidebar_widgets, widget_id );
-                               if ( -1 !== i ) {
-                                       other_sidebar_widgets.splice( i );
-                                       other_setting( other_sidebar_widgets );
-                               }
-                       } );
-
-                       // Add widget to this sidebar
-                       sidebar_widgets = control.setting().slice();
-                       if ( -1 === _.indexOf( sidebar_widgets, widget_id ) ) {
-                               sidebar_widgets.push( widget_id );
-                               control.setting( sidebar_widgets );
-                       }
-
-                       customize_control.slideDown( function () {
-                               if ( is_existing_widget ) {
-                                       widget_form_control.expandForm();
-                                       widget_form_control.updateWidget( {
-                                               instance: widget_form_control.setting(),
-                                               complete: function ( error ) {
-                                                       if ( error ) {
-                                                               throw error;
-                                                       }
-                                                       widget_form_control.focus();
-                                               }
-                                       } );
-                               } else {
-                                       widget_form_control.focus();
-                               }
-                       } );
-
-                       $( document ).trigger( 'widget-added', [ widget_el ] );
-
-                       return widget_form_control;
-               }
-
-       } );
-
-       /**
</del><span class="cx">    * Widget Form control
</span><del>-        * Note that 'widget_form' must match the Widget_Form_WP_Customize_Control::$type
</del><ins>+         * Note that 'widget_form' must match the WP_Widget_Form_Customize_Control::$type
</ins><span class="cx">    */
</span><del>-       customize.controlConstructor.widget_form = customize.Control.extend( {
-
</del><ins>+        api.Widgets.WidgetControl = api.Control.extend({
</ins><span class="cx">           /**
</span><span class="cx">           * Set up the control
</span><span class="cx">           */
</span><span class="lines">@@ -638,15 +158,17 @@
</span><span class="cx">          /**
</span><span class="cx">           * Handle changes to the setting
</span><span class="cx">           */
</span><del>-               _setupModel: function () {
</del><ins>+                _setupModel: function() {
</ins><span class="cx">                   var control = this, remember_saved_widget_id;
</span><span class="cx"> 
</span><ins>+                       api.Widgets.savedWidgetIds = api.Widgets.savedWidgetIds || [];
+
</ins><span class="cx">                   // Remember saved widgets so we know which to trash (move to inactive widgets sidebar)
</span><del>-                       remember_saved_widget_id = function () {
-                               self.saved_widget_ids[control.params.widget_id] = true;
</del><ins>+                        remember_saved_widget_id = function() {
+                               api.Widgets.savedWidgetIds[control.params.widget_id] = true;
</ins><span class="cx">                   };
</span><del>-                       wp.customize.bind( 'ready', remember_saved_widget_id );
-                       wp.customize.bind( 'saved', remember_saved_widget_id );
</del><ins>+                        api.bind( 'ready', remember_saved_widget_id );
+                       api.bind( 'saved', remember_saved_widget_id );
</ins><span class="cx"> 
</span><span class="cx">                  control._update_count = 0;
</span><span class="cx">                  control.is_widget_updating = false;
</span><span class="lines">@@ -663,7 +185,7 @@
</span><span class="cx">          /**
</span><span class="cx">           * Add special behaviors for wide widget controls
</span><span class="cx">           */
</span><del>-               _setupWideWidget: function () {
</del><ins>+                _setupWideWidget: function() {
</ins><span class="cx">                   var control = this,
</span><span class="cx">                          widget_inside,
</span><span class="cx">                          widget_form,
</span><span class="lines">@@ -694,7 +216,7 @@
</span><span class="cx">                   * exceed the window height so that the contents of the widget control
</span><span class="cx">                   * will become scrollable (overflow:auto).
</span><span class="cx">                   */
</span><del>-                       position_widget = function () {
</del><ins>+                        position_widget = function() {
</ins><span class="cx">                           var offset_top = control.container.offset().top,
</span><span class="cx">                                  window_height = $( window ).height(),
</span><span class="cx">                                  form_height = widget_form.outerHeight(),
</span><span class="lines">@@ -711,22 +233,22 @@
</span><span class="cx">                  };
</span><span class="cx"> 
</span><span class="cx">                  theme_controls_container = $( '#customize-theme-controls' );
</span><del>-                       control.container.on( 'expand', function () {
</del><ins>+                        control.container.on( 'expand', function() {
</ins><span class="cx">                           position_widget();
</span><span class="cx">                          customize_sidebar.on( 'scroll', position_widget );
</span><span class="cx">                          $( window ).on( 'resize', position_widget );
</span><span class="cx">                          theme_controls_container.on( 'expanded collapsed', position_widget );
</span><span class="cx">                  } );
</span><del>-                       control.container.on( 'collapsed', function () {
</del><ins>+                        control.container.on( 'collapsed', function() {
</ins><span class="cx">                           customize_sidebar.off( 'scroll', position_widget );
</span><span class="cx">                          $( window ).off( 'resize', position_widget );
</span><span class="cx">                          theme_controls_container.off( 'expanded collapsed', position_widget );
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="cx">                  // Reposition whenever a sidebar's widgets are changed
</span><del>-                       wp.customize.each( function ( setting ) {
</del><ins>+                        api.each( function ( setting ) {
</ins><span class="cx">                           if ( 0 === setting.id.indexOf( 'sidebars_widgets[' ) ) {
</span><del>-                                       setting.bind( function () {
</del><ins>+                                        setting.bind( function() {
</ins><span class="cx">                                           if ( control.container.hasClass( 'expanded' ) ) {
</span><span class="cx">                                                  position_widget();
</span><span class="cx">                                          }
</span><span class="lines">@@ -763,10 +285,10 @@
</span><span class="cx">          /**
</span><span class="cx">           * Update the title of the form if a title field is entered
</span><span class="cx">           */
</span><del>-               _setupWidgetTitle: function () {
</del><ins>+                _setupWidgetTitle: function() {
</ins><span class="cx">                   var control = this, update_title;
</span><span class="cx"> 
</span><del>-                       update_title = function () {
</del><ins>+                        update_title = function() {
</ins><span class="cx">                           var title = control.setting().title,
</span><span class="cx">                                  in_widget_title = control.container.find( '.in-widget-title' );
</span><span class="cx"> 
</span><span class="lines">@@ -783,7 +305,7 @@
</span><span class="cx">          /**
</span><span class="cx">           * Set up the widget-reorder-nav
</span><span class="cx">           */
</span><del>-               _setupReorderUI: function () {
</del><ins>+                _setupReorderUI: function() {
</ins><span class="cx">                   var control = this,
</span><span class="cx">                          select_sidebar_item,
</span><span class="cx">                          move_widget_area,
</span><span class="lines">@@ -805,10 +327,10 @@
</span><span class="cx">                  /**
</span><span class="cx">                   * Add the widget reordering elements to the widget control
</span><span class="cx">                   */
</span><del>-                       control.container.find( '.widget-title-action' ).after( $( self.tpl.widget_reorder_nav ) );
</del><ins>+                        control.container.find( '.widget-title-action' ).after( $( api.Widgets.data.tpl.widgetReorderNav ) );
</ins><span class="cx">                   move_widget_area = $(
</span><del>-                               _.template( self.tpl.move_widget_area, {
-                                       sidebars: _( self.registered_sidebars.toArray() ).pluck( 'attributes' )
</del><ins>+                                _.template( api.Widgets.data.tpl.moveWidgetArea, {
+                                       sidebars: _( api.Widgets.registeredSidebars.toArray() ).pluck( 'attributes' )
</ins><span class="cx">                           } )
</span><span class="cx">                  );
</span><span class="cx">                  control.container.find( '.widget-top' ).after( move_widget_area );
</span><span class="lines">@@ -816,18 +338,18 @@
</span><span class="cx">                  /**
</span><span class="cx">                   * Update available sidebars when their rendered state changes
</span><span class="cx">                   */
</span><del>-                       update_available_sidebars = function () {
</del><ins>+                        update_available_sidebars = function() {
</ins><span class="cx">                           var sidebar_items = move_widget_area.find( 'li' ), self_sidebar_item;
</span><span class="cx">                          self_sidebar_item = sidebar_items.filter( function(){
</span><span class="cx">                                  return $( this ).data( 'id' ) === control.params.sidebar_id;
</span><span class="cx">                          } );
</span><del>-                               sidebar_items.each( function () {
</del><ins>+                                sidebar_items.each( function() {
</ins><span class="cx">                                   var li = $( this ),
</span><span class="cx">                                          sidebar_id,
</span><span class="cx">                                          sidebar_model;
</span><span class="cx"> 
</span><span class="cx">                                  sidebar_id = li.data( 'id' );
</span><del>-                                       sidebar_model = self.registered_sidebars.get( sidebar_id );
</del><ins>+                                        sidebar_model = api.Widgets.registeredSidebars.get( sidebar_id );
</ins><span class="cx">                                   li.toggle( sidebar_model.get( 'is_rendered' ) );
</span><span class="cx">                                  if ( li.hasClass( 'selected' ) && ! sidebar_model.get( 'is_rendered' ) ) {
</span><span class="cx">                                          select_sidebar_item( self_sidebar_item );
</span><span class="lines">@@ -835,7 +357,7 @@
</span><span class="cx">                          } );
</span><span class="cx">                  };
</span><span class="cx">                  update_available_sidebars();
</span><del>-                       self.registered_sidebars.on( 'change:is_rendered', update_available_sidebars );
</del><ins>+                        api.Widgets.registeredSidebars.on( 'change:is_rendered', update_available_sidebars );
</ins><span class="cx"> 
</span><span class="cx">                  /**
</span><span class="cx">                   * Handle clicks for up/down/move on the reorder nav
</span><span class="lines">@@ -882,7 +404,7 @@
</span><span class="cx">                  /**
</span><span class="cx">                   * Move widget to another sidebar
</span><span class="cx">                   */
</span><del>-                       control.container.find( '.move-widget-btn' ).click( function () {
</del><ins>+                        control.container.find( '.move-widget-btn' ).click( function() {
</ins><span class="cx">                           control.getSidebarWidgetsControl().toggleReordering( false );
</span><span class="cx"> 
</span><span class="cx">                          var old_sidebar_id = control.params.sidebar_id,
</span><span class="lines">@@ -893,8 +415,8 @@
</span><span class="cx">                                  new_sidebar_widget_ids,
</span><span class="cx">                                  i;
</span><span class="cx"> 
</span><del>-                               old_sidebar_widgets_setting = customize( 'sidebars_widgets[' + old_sidebar_id + ']' );
-                               new_sidebar_widgets_setting = customize( 'sidebars_widgets[' + new_sidebar_id + ']' );
</del><ins>+                                old_sidebar_widgets_setting = api( 'sidebars_widgets[' + old_sidebar_id + ']' );
+                               new_sidebar_widgets_setting = api( 'sidebars_widgets[' + new_sidebar_id + ']' );
</ins><span class="cx">                           old_sidebar_widget_ids = Array.prototype.slice.call( old_sidebar_widgets_setting() );
</span><span class="cx">                          new_sidebar_widget_ids = Array.prototype.slice.call( new_sidebar_widgets_setting() );
</span><span class="cx"> 
</span><span class="lines">@@ -916,17 +438,17 @@
</span><span class="cx">                  var control = this;
</span><span class="cx"> 
</span><span class="cx">                  // Highlight whenever hovering or clicking over the form
</span><del>-                       control.container.on( 'mouseenter click', function () {
</del><ins>+                        control.container.on( 'mouseenter click', function() {
</ins><span class="cx">                           control.setting.previewer.send( 'highlight-widget', control.params.widget_id );
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="cx">                  // Highlight when the setting is updated
</span><del>-                       control.setting.bind( function () {
</del><ins>+                        control.setting.bind( function() {
</ins><span class="cx">                           control.setting.previewer.send( 'highlight-widget', control.params.widget_id );
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="cx">                  // Highlight when the widget form is expanded
</span><del>-                       control.container.on( 'expand', function () {
</del><ins>+                        control.container.on( 'expand', function() {
</ins><span class="cx">                           control.scrollPreviewWidgetIntoView();
</span><span class="cx">                  } );
</span><span class="cx">          },
</span><span class="lines">@@ -934,7 +456,7 @@
</span><span class="cx">          /**
</span><span class="cx">           * Set up event handlers for widget updating
</span><span class="cx">           */
</span><del>-               _setupUpdateUI: function () {
</del><ins>+                _setupUpdateUI: function() {
</ins><span class="cx">                   var control = this,
</span><span class="cx">                          widget_root,
</span><span class="cx">                          widget_content,
</span><span class="lines">@@ -947,15 +469,15 @@
</span><span class="cx"> 
</span><span class="cx">                  // Configure update button
</span><span class="cx">                  save_btn = control.container.find( '.widget-control-save' );
</span><del>-                       save_btn.val( self.i18n.save_btn_label );
-                       save_btn.attr( 'title', self.i18n.save_btn_tooltip );
</del><ins>+                        save_btn.val( l10n.saveBtnLabel );
+                       save_btn.attr( 'title', l10n.saveBtnTooltip );
</ins><span class="cx">                   save_btn.removeClass( 'button-primary' ).addClass( 'button-secondary' );
</span><span class="cx">                  save_btn.on( 'click', function ( e ) {
</span><span class="cx">                          e.preventDefault();
</span><span class="cx">                          control.updateWidget( { disable_form: true } );
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><del>-                       update_widget_debounced = _.debounce( function () {
</del><ins>+                        update_widget_debounced = _.debounce( function() {
</ins><span class="cx">                           // @todo For compatibility with other plugins, should we trigger a click event? What about form submit event?
</span><span class="cx">                          control.updateWidget();
</span><span class="cx">                  }, 250 );
</span><span class="lines">@@ -980,22 +502,22 @@
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="cx">                  // Remove loading indicators when the setting is saved and the preview updates
</span><del>-                       control.setting.previewer.channel.bind( 'synced', function () {
</del><ins>+                        control.setting.previewer.channel.bind( 'synced', function() {
</ins><span class="cx">                           control.container.removeClass( 'previewer-loading' );
</span><span class="cx">                  } );
</span><del>-                       self.previewer.bind( 'widget-updated', function ( updated_widget_id ) {
</del><ins>+                        api.Widgets.Previewer.bind( 'widget-updated', function ( updated_widget_id ) {
</ins><span class="cx">                           if ( updated_widget_id === control.params.widget_id ) {
</span><span class="cx">                                  control.container.removeClass( 'previewer-loading' );
</span><span class="cx">                          }
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="cx">                  // Update widget control to indicate whether it is currently rendered (cf. Widget Visibility)
</span><del>-                       self.previewer.bind( 'rendered-widgets', function ( rendered_widgets ) {
</del><ins>+                        api.Widgets.Previewer.bind( 'rendered-widgets', function ( rendered_widgets ) {
</ins><span class="cx">                           var is_rendered = !! rendered_widgets[control.params.widget_id];
</span><span class="cx">                          control.container.toggleClass( 'widget-rendered', is_rendered );
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><del>-                       form_update_event_handler = builtin_form_sync_handlers[ control.params.widget_id_base ];
</del><ins>+                        form_update_event_handler = api.Widgets.formSyncHandlers[ control.params.widget_id_base ];
</ins><span class="cx">                   if ( form_update_event_handler ) {
</span><span class="cx">                          $( document ).on( 'widget-synced', function ( e, widget_el ) {
</span><span class="cx">                                  if ( widget_root.is( widget_el ) ) {
</span><span class="lines">@@ -1008,7 +530,7 @@
</span><span class="cx">          /**
</span><span class="cx">           * Set up event handlers for widget removal
</span><span class="cx">           */
</span><del>-               _setupRemoveUI: function () {
</del><ins>+                _setupRemoveUI: function() {
</ins><span class="cx">                   var control = this,
</span><span class="cx">                          remove_btn,
</span><span class="cx">                          replace_delete_with_remove;
</span><span class="lines">@@ -1030,7 +552,7 @@
</span><span class="cx">                          }
</span><span class="cx"> 
</span><span class="cx">                          control.container.slideUp( function() {
</span><del>-                                       var sidebars_widgets_control = self.getSidebarWidgetControlContainingWidget( control.params.widget_id ),
</del><ins>+                                        var sidebars_widgets_control = api.Widgets.getSidebarWidgetControlContainingWidget( control.params.widget_id ),
</ins><span class="cx">                                           sidebar_widget_ids,
</span><span class="cx">                                          i;
</span><span class="cx"> 
</span><span class="lines">@@ -1048,12 +570,12 @@
</span><span class="cx">                          } );
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><del>-                       replace_delete_with_remove = function () {
-                               remove_btn.text( self.i18n.remove_btn_label ); // wp_widget_control() outputs the link as "Delete"
-                               remove_btn.attr( 'title', self.i18n.remove_btn_tooltip );
</del><ins>+                        replace_delete_with_remove = function() {
+                               remove_btn.text( l10n.removeBtnLabel ); // wp_widget_control() outputs the link as "Delete"
+                               remove_btn.attr( 'title', l10n.removeBtnTooltip );
</ins><span class="cx">                   };
</span><span class="cx">                  if ( control.params.is_new ) {
</span><del>-                               wp.customize.bind( 'saved', replace_delete_with_remove );
</del><ins>+                                api.bind( 'saved', replace_delete_with_remove );
</ins><span class="cx">                   } else {
</span><span class="cx">                          replace_delete_with_remove();
</span><span class="cx">                  }
</span><span class="lines">@@ -1119,11 +641,11 @@
</span><span class="cx">          /**
</span><span class="cx">           * @return {wp.customize.controlConstructor.sidebar_widgets[]}
</span><span class="cx">           */
</span><del>-               getSidebarWidgetsControl: function () {
</del><ins>+                getSidebarWidgetsControl: function() {
</ins><span class="cx">                   var control = this, setting_id, sidebar_widgets_control;
</span><span class="cx"> 
</span><span class="cx">                  setting_id = 'sidebars_widgets[' + control.params.sidebar_id + ']';
</span><del>-                       sidebar_widgets_control = customize.control( setting_id );
</del><ins>+                        sidebar_widgets_control = api.control( setting_id );
</ins><span class="cx">                   if ( ! sidebar_widgets_control ) {
</span><span class="cx">                          throw new Error( 'Unable to locate sidebar_widgets control for ' + control.params.sidebar_id );
</span><span class="cx">                  }
</span><span class="lines">@@ -1173,7 +695,7 @@
</span><span class="cx"> 
</span><span class="cx">                  control.container.addClass( 'widget-form-loading' );
</span><span class="cx">                  control.container.addClass( 'previewer-loading' );
</span><del>-                       processing = wp.customize.state( 'processing' );
</del><ins>+                        processing = api.state( 'processing' );
</ins><span class="cx">                   processing( processing() + 1 );
</span><span class="cx"> 
</span><span class="cx">                  if ( ! control.live_update_mode ) {
</span><span class="lines">@@ -1183,7 +705,7 @@
</span><span class="cx">                  params = {};
</span><span class="cx">                  params.action = 'update-widget';
</span><span class="cx">                  params.wp_customize = 'on';
</span><del>-                       params.nonce = self.nonce;
</del><ins>+                        params.nonce = api.Widgets.data.nonce;
</ins><span class="cx"> 
</span><span class="cx">                  data = $.param( params );
</span><span class="cx">                  inputs = control._getInputs( widget_content );
</span><span class="lines">@@ -1191,7 +713,7 @@
</span><span class="cx">                  // Store the value we're submitting in data so that when the response comes back,
</span><span class="cx">                  // we know if it got sanitized; if there is no difference in the sanitized value,
</span><span class="cx">                  // then we do not need to touch the UI and mess up the user's ongoing editing.
</span><del>-                       inputs.each( function () {
</del><ins>+                        inputs.each( function() {
</ins><span class="cx">                           var input = $( this ),
</span><span class="cx">                                  property = control._getInputStatePropertyName( this );
</span><span class="cx">                          input.data( 'state' + update_number, input.prop( property ) );
</span><span class="lines">@@ -1213,17 +735,17 @@
</span><span class="cx"> 
</span><span class="cx">                          // Check if the user is logged out.
</span><span class="cx">                          if ( '0' === r ) {
</span><del>-                                       self.previewer.preview.iframe.hide();
-                                       self.previewer.login().done( function() {
</del><ins>+                                        api.Widgets.Previewer.preview.iframe.hide();
+                                       api.Widgets.Previewer.login().done( function() {
</ins><span class="cx">                                           control.updateWidget( args );
</span><del>-                                               self.previewer.preview.iframe.show();
</del><ins>+                                                api.Widgets.Previewer.preview.iframe.show();
</ins><span class="cx">                                   } );
</span><span class="cx">                                  return;
</span><span class="cx">                          }
</span><span class="cx"> 
</span><span class="cx">                          // Check for cheaters.
</span><span class="cx">                          if ( '-1' === r ) {
</span><del>-                                       self.previewer.cheatin();
</del><ins>+                                        api.Widgets.Previewer.cheatin();
</ins><span class="cx">                                   return;
</span><span class="cx">                          }
</span><span class="cx"> 
</span><span class="lines">@@ -1294,7 +816,7 @@
</span><span class="cx">                                          complete_callback.call( control, null, { no_change: ! is_changed, ajax_finished: true } );
</span><span class="cx">                                  }
</span><span class="cx">                          } else {
</span><del>-                                       message = self.i18n.error;
</del><ins>+                                        message = l10n.error;
</ins><span class="cx">                                   if ( r.data && r.data.message ) {
</span><span class="cx">                                          message = r.data.message;
</span><span class="cx">                                  }
</span><span class="lines">@@ -1310,9 +832,9 @@
</span><span class="cx">                                  complete_callback.call( control, textStatus );
</span><span class="cx">                          }
</span><span class="cx">                  } );
</span><del>-                       jqxhr.always( function () {
</del><ins>+                        jqxhr.always( function() {
</ins><span class="cx">                           control.container.removeClass( 'widget-form-loading' );
</span><del>-                               inputs.each( function () {
</del><ins>+                                inputs.each( function() {
</ins><span class="cx">                                   $( this ).removeData( 'state' + update_number );
</span><span class="cx">                          } );
</span><span class="cx"> 
</span><span class="lines">@@ -1324,7 +846,7 @@
</span><span class="cx">           * Expand the accordion section containing a control
</span><span class="cx">           * @todo it would be nice if accordion had a proper API instead of having to trigger UI events on its elements
</span><span class="cx">           */
</span><del>-               expandControlSection: function () {
</del><ins>+                expandControlSection: function() {
</ins><span class="cx">                   var section = this.container.closest( '.accordion-section' );
</span><span class="cx">                  if ( ! section.hasClass( 'open' ) ) {
</span><span class="cx">                          section.find( '.accordion-section-title:first' ).trigger( 'click' );
</span><span class="lines">@@ -1334,14 +856,14 @@
</span><span class="cx">          /**
</span><span class="cx">           * Expand the widget form control
</span><span class="cx">           */
</span><del>-               expandForm: function () {
</del><ins>+                expandForm: function() {
</ins><span class="cx">                   this.toggleForm( true );
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="cx">          /**
</span><span class="cx">           * Collapse the widget form control
</span><span class="cx">           */
</span><del>-               collapseForm: function () {
</del><ins>+                collapseForm: function() {
</ins><span class="cx">                   this.toggleForm( false );
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="lines">@@ -1366,13 +888,13 @@
</span><span class="cx"> 
</span><span class="cx">                  if ( do_expand ) {
</span><span class="cx">                          // Close all other widget controls before expanding this one
</span><del>-                               wp.customize.control.each( function ( other_control ) {
</del><ins>+                                api.control.each( function ( other_control ) {
</ins><span class="cx">                                   if ( control.params.type === other_control.params.type && control !== other_control ) {
</span><span class="cx">                                          other_control.collapseForm();
</span><span class="cx">                                  }
</span><span class="cx">                          } );
</span><span class="cx"> 
</span><del>-                               complete = function () {
</del><ins>+                                complete = function() {
</ins><span class="cx">                                   control.container.removeClass( 'expanding' );
</span><span class="cx">                                  control.container.addClass( 'expanded' );
</span><span class="cx">                                  control.container.trigger( 'expanded' );
</span><span class="lines">@@ -1387,7 +909,7 @@
</span><span class="cx">                  } else {
</span><span class="cx">                          control.container.trigger( 'collapse' );
</span><span class="cx">                          control.container.addClass( 'collapsing' );
</span><del>-                               complete = function () {
</del><ins>+                                complete = function() {
</ins><span class="cx">                                   control.container.removeClass( 'collapsing' );
</span><span class="cx">                                  control.container.removeClass( 'expanded' );
</span><span class="cx">                                  control.container.trigger( 'collapsed' );
</span><span class="lines">@@ -1407,7 +929,7 @@
</span><span class="cx">           * Expand the containing sidebar section, expand the form, and focus on
</span><span class="cx">           * the first input in the control
</span><span class="cx">           */
</span><del>-               focus: function () {
</del><ins>+                focus: function() {
</ins><span class="cx">                   var control = this;
</span><span class="cx">                  control.expandControlSection();
</span><span class="cx">                  control.expandForm();
</span><span class="lines">@@ -1420,7 +942,7 @@
</span><span class="cx">           * @throws Error
</span><span class="cx">           * @returns {Number}
</span><span class="cx">           */
</span><del>-               getWidgetSidebarPosition: function () {
</del><ins>+                getWidgetSidebarPosition: function() {
</ins><span class="cx">                   var control = this,
</span><span class="cx">                          sidebar_widget_ids,
</span><span class="cx">                          position;
</span><span class="lines">@@ -1436,14 +958,14 @@
</span><span class="cx">          /**
</span><span class="cx">           * Move widget up one in the sidebar
</span><span class="cx">           */
</span><del>-               moveUp: function () {
</del><ins>+                moveUp: function() {
</ins><span class="cx">                   this._moveWidgetByOne( -1 );
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="cx">          /**
</span><span class="cx">           * Move widget up one in the sidebar
</span><span class="cx">           */
</span><del>-               moveDown: function () {
</del><ins>+                moveDown: function() {
</ins><span class="cx">                   this._moveWidgetByOne( 1 );
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="lines">@@ -1484,7 +1006,7 @@
</span><span class="cx">                  if ( toggle ) {
</span><span class="cx">                          // reset the selected sidebar
</span><span class="cx">                          move_widget_area.find( '.selected' ).removeClass( 'selected' );
</span><del>-                               move_widget_area.find( 'li' ).filter( function () {
</del><ins>+                                move_widget_area.find( 'li' ).filter( function() {
</ins><span class="cx">                                   return $( this ).data( 'id' ) === control.params.sidebar_id;
</span><span class="cx">                          } ).addClass( 'selected' );
</span><span class="cx">                          control.container.find( '.move-widget-btn' ).prop( 'disabled', true );
</span><span class="lines">@@ -1495,7 +1017,7 @@
</span><span class="cx">          /**
</span><span class="cx">           * Inside of the customizer preview, scroll the widget into view
</span><span class="cx">           */
</span><del>-               scrollPreviewWidgetIntoView: function () {
</del><ins>+                scrollPreviewWidgetIntoView: function() {
</ins><span class="cx">                   // @todo scrollIntoView() provides a robust but very poor experience. Animation is needed. See https://github.com/x-team/wp-widget-customizer/issues/16
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="lines">@@ -1513,7 +1035,7 @@
</span><span class="cx"> 
</span><span class="cx">                  $( '.widget-customizer-highlighted' ).removeClass( 'widget-customizer-highlighted' );
</span><span class="cx">                  target_element.addClass( 'widget-customizer-highlighted' );
</span><del>-                       setTimeout( function () {
</del><ins>+                        setTimeout( function() {
</ins><span class="cx">                           target_element.removeClass( 'widget-customizer-highlighted' );
</span><span class="cx">                  }, 500 );
</span><span class="cx">          }
</span><span class="lines">@@ -1521,12 +1043,467 @@
</span><span class="cx">  } );
</span><span class="cx"> 
</span><span class="cx">  /**
</span><ins>+        * Sidebar Widgets control
+        * Note that 'sidebar_widgets' must match the WP_Widget_Area_Customize_Control::$type
+        */
+       api.Widgets.SidebarControl = api.Control.extend({
+               /**
+                * Set up the control
+                */
+               ready: function() {
+                       var control = this;
+                       control.control_section = control.container.closest( '.control-section' );
+                       control.section_content = control.container.closest( '.accordion-section-content' );
+                       control._setupModel();
+                       control._setupSortable();
+                       control._setupAddition();
+                       control._applyCardinalOrderClassNames();
+               },
+
+               /**
+                * Update ordering of widget control forms when the setting is updated
+                */
+               _setupModel: function() {
+                       var control = this,
+                               registered_sidebar = api.Widgets.registeredSidebars.get( control.params.sidebar_id );
+
+                       control.setting.bind( function( new_widget_ids, old_widget_ids ) {
+                               var widget_form_controls,
+                                       sidebar_widgets_add_control,
+                                       final_control_containers,
+                                       removed_widget_ids = _( old_widget_ids ).difference( new_widget_ids );
+
+                               // Filter out any persistent widget_ids for widgets which have been deactivated
+                               new_widget_ids = _( new_widget_ids ).filter( function ( new_widget_id ) {
+                                       var parsed_widget_id = parse_widget_id( new_widget_id );
+                                       return !! api.Widgets.availableWidgets.findWhere( { id_base: parsed_widget_id.id_base } );
+                               } );
+
+                               widget_form_controls = _( new_widget_ids ).map( function ( widget_id ) {
+                                       var widget_form_control = api.Widgets.getWidgetFormControlForWidget( widget_id );
+                                       if ( ! widget_form_control ) {
+                                               widget_form_control = control.addWidget( widget_id );
+                                       }
+                                       return widget_form_control;
+                               } );
+
+                               // Sort widget controls to their new positions
+                               widget_form_controls.sort( function ( a, b ) {
+                                       var a_index = _.indexOf( new_widget_ids, a.params.widget_id ),
+                                               b_index = _.indexOf( new_widget_ids, b.params.widget_id );
+                                       if ( a_index === b_index ) {
+                                               return 0;
+                                       }
+                                       return a_index < b_index ? -1 : 1;
+                               } );
+
+                               sidebar_widgets_add_control = control.section_content.find( '.customize-control-sidebar_widgets' );
+
+                               // Append the controls to put them in the right order
+                               final_control_containers = _( widget_form_controls ).map( function( widget_form_controls ) {
+                                       return widget_form_controls.container[0];
+                               } );
+
+                               // Re-sort widget form controls (including widgets form other sidebars newly moved here)
+                               sidebar_widgets_add_control.before( final_control_containers );
+                               control._applyCardinalOrderClassNames();
+
+                               // If the widget was dragged into the sidebar, make sure the sidebar_id param is updated
+                               _( widget_form_controls ).each( function ( widget_form_control ) {
+                                       widget_form_control.params.sidebar_id = control.params.sidebar_id;
+                               } );
+
+                               // Cleanup after widget removal
+                               _( removed_widget_ids ).each( function ( removed_widget_id ) {
+
+                                       // Using setTimeout so that when moving a widget to another sidebar, the other sidebars_widgets settings get a chance to update
+                                       setTimeout( function() {
+                                               var is_present_in_another_sidebar = false,
+                                                       removed_control,
+                                                       was_dragged_to_another_sidebar,
+                                                       inactive_widgets,
+                                                       removed_id_base,
+                                                       widget;
+
+                                               // Check if the widget is in another sidebar
+                                               api.each( function ( other_setting ) {
+                                                       if ( other_setting.id === control.setting.id || 0 !== other_setting.id.indexOf( 'sidebars_widgets[' ) || other_setting.id === 'sidebars_widgets[wp_inactive_widgets]' ) {
+                                                               return;
+                                                       }
+                                                       var other_sidebar_widgets = other_setting(), i;
+
+                                                       i = _.indexOf( other_sidebar_widgets, removed_widget_id );
+                                                       if ( -1 !== i ) {
+                                                               is_present_in_another_sidebar = true;
+                                                       }
+                                               } );
+
+                                               // If the widget is present in another sidebar, abort!
+                                               if ( is_present_in_another_sidebar ) {
+                                                       return;
+                                               }
+
+                                               removed_control = api.Widgets.getWidgetFormControlForWidget( removed_widget_id );
+
+                                               // Detect if widget control was dragged to another sidebar
+                                               was_dragged_to_another_sidebar = (
+                                                       removed_control &&
+                                                       $.contains( document, removed_control.container[0] ) &&
+                                                       ! $.contains( control.section_content[0], removed_control.container[0] )
+                                               );
+
+                                               // Delete any widget form controls for removed widgets
+                                               if ( removed_control && ! was_dragged_to_another_sidebar ) {
+                                                       api.control.remove( removed_control.id );
+                                                       removed_control.container.remove();
+                                               }
+
+                                               // Move widget to inactive widgets sidebar (move it to trash) if has been previously saved
+                                               // This prevents the inactive widgets sidebar from overflowing with throwaway widgets
+                                               if ( api.Widgets.savedWidgetIds[removed_widget_id] ) {
+                                                       inactive_widgets = api.value( 'sidebars_widgets[wp_inactive_widgets]' )().slice();
+                                                       inactive_widgets.push( removed_widget_id );
+                                                       api.value( 'sidebars_widgets[wp_inactive_widgets]' )( _( inactive_widgets ).unique() );
+                                               }
+
+                                               // Make old single widget available for adding again
+                                               removed_id_base = parse_widget_id( removed_widget_id ).id_base;
+                                               widget = api.Widgets.availableWidgets.findWhere( { id_base: removed_id_base } );
+                                               if ( widget && ! widget.get( 'is_multi' ) ) {
+                                                       widget.set( 'is_disabled', false );
+                                               }
+                                       } );
+
+                               } );
+                       } );
+
+                       // Update the model with whether or not the sidebar is rendered
+                       api.Widgets.Previewer.bind( 'rendered-sidebars', function ( rendered_sidebars ) {
+                               var is_rendered = !! rendered_sidebars[control.params.sidebar_id];
+                               registered_sidebar.set( 'is_rendered', is_rendered );
+                       } );
+
+                       // Show the sidebar section when it becomes visible
+                       registered_sidebar.on( 'change:is_rendered', function ( ) {
+                               var section_selector = '#accordion-section-sidebar-widgets-' + this.get( 'id' ), section;
+                               section = $( section_selector );
+                               if ( this.get( 'is_rendered' ) ) {
+                                       section.stop().slideDown( function() {
+                                               $( this ).css( 'height', 'auto' ); // so that the .accordion-section-content won't overflow
+                                       } );
+                               } else {
+                                       // Make sure that hidden sections get closed first
+                                       if ( section.hasClass( 'open' ) ) {
+                                               // it would be nice if accordionSwitch() in accordion.js was public
+                                               section.find( '.accordion-section-title' ).trigger( 'click' );
+                                       }
+                                       section.stop().slideUp();
+                               }
+                       } );
+               },
+
+               /**
+                * Allow widgets in sidebar to be re-ordered, and for the order to be previewed
+                */
+               _setupSortable: function() {
+                       var control = this;
+                       control.is_reordering = false;
+
+                       /**
+                        * Update widget order setting when controls are re-ordered
+                        */
+                       control.section_content.sortable( {
+                               items: '> .customize-control-widget_form',
+                               handle: '.widget-top',
+                               axis: 'y',
+                               connectWith: '.accordion-section-content:has(.customize-control-sidebar_widgets)',
+                               update: function() {
+                                       var widget_container_ids = control.section_content.sortable( 'toArray' ), widget_ids;
+                                       widget_ids = $.map( widget_container_ids, function ( widget_container_id ) {
+                                               return $( '#' + widget_container_id ).find( ':input[name=widget-id]' ).val();
+                                       } );
+                                       control.setting( widget_ids );
+                               }
+                       } );
+
+                       /**
+                        * Expand other customizer sidebar section when dragging a control widget over it,
+                        * allowing the control to be dropped into another section
+                        */
+                       control.control_section.find( '.accordion-section-title' ).droppable( {
+                               accept: '.customize-control-widget_form',
+                               over: function() {
+                                       if ( ! control.control_section.hasClass( 'open' ) ) {
+                                               control.control_section.addClass( 'open' );
+                                               control.section_content.toggle( false ).slideToggle( 150, function() {
+                                                       control.section_content.sortable( 'refreshPositions' );
+                                               } );
+                                       }
+                               }
+                       } );
+
+                       /**
+                        * Keyboard-accessible reordering
+                        */
+                       control.container.find( '.reorder-toggle' ).on( 'click keydown', function( event ) {
+                               if ( event.type === 'keydown' && ! ( event.which === 13 || event.which === 32 ) ) { // Enter or Spacebar
+                                       return;
+                               }
+
+                               control.toggleReordering( ! control.is_reordering );
+                       } );
+               },
+
+               /**
+                * Set up UI for adding a new widget
+                */
+               _setupAddition: function() {
+                       var control = this;
+
+                       control.container.find( '.add-new-widget' ).on( 'click keydown', function( event ) {
+                               if ( event.type === 'keydown' && ! ( event.which === 13 || event.which === 32 ) ) { // Enter or Spacebar
+                                       return;
+                               }
+
+                               if ( control.section_content.hasClass( 'reordering' ) ) {
+                                       return;
+                               }
+
+                               // @todo Use an control.is_adding state
+                               if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) {
+                                       api.Widgets.availableWidgetsPanel.open( control );
+                               } else {
+                                       api.Widgets.availableWidgetsPanel.close();
+                               }
+                       } );
+               },
+
+               /**
+                * Add classes to the widget_form controls to assist with styling
+                */
+               _applyCardinalOrderClassNames: function() {
+                       var control = this;
+                       control.section_content.find( '.customize-control-widget_form' )
+                               .removeClass( 'first-widget' )
+                               .removeClass( 'last-widget' )
+                               .find( '.move-widget-down, .move-widget-up' ).prop( 'tabIndex', 0 );
+
+                       control.section_content.find( '.customize-control-widget_form:first' )
+                               .addClass( 'first-widget' )
+                               .find( '.move-widget-up' ).prop( 'tabIndex', -1 );
+                       control.section_content.find( '.customize-control-widget_form:last' )
+                               .addClass( 'last-widget' )
+                               .find( '.move-widget-down' ).prop( 'tabIndex', -1 );
+               },
+
+
+               /***********************************************************************
+                * Begin public API methods
+                **********************************************************************/
+
+               /**
+                * Enable/disable the reordering UI
+                *
+                * @param {Boolean} toggle to enable/disable reordering
+                */
+               toggleReordering: function ( toggle ) {
+                       var control = this;
+                       toggle = Boolean( toggle );
+                       if ( toggle === control.section_content.hasClass( 'reordering' ) ) {
+                               return;
+                       }
+
+                       control.is_reordering = toggle;
+                       control.section_content.toggleClass( 'reordering', toggle );
+
+                       if ( toggle ) {
+                               _( control.getWidgetFormControls() ).each( function ( form_control ) {
+                                       form_control.collapseForm();
+                               } );
+                       }
+               },
+
+               /**
+                * @return {wp.customize.controlConstructor.widget_form[]}
+                */
+               getWidgetFormControls: function() {
+                       var control = this, form_controls;
+
+                       form_controls = _( control.setting() ).map( function ( widget_id ) {
+                               var setting_id = widget_id_to_setting_id( widget_id ),
+                                       form_control = api.control( setting_id );
+
+                               if ( ! form_control ) {
+                                       throw new Error( 'Unable to find widget_form control for ' + widget_id );
+                               }
+                               return form_control;
+                       } );
+                       return form_controls;
+               },
+
+               /**
+                * @param {string} widget_id or an id_base for adding a previously non-existing widget
+                * @returns {object} widget_form control instance
+                */
+               addWidget: function ( widget_id ) {
+                       var control = this,
+                               control_html,
+                               widget_el,
+                               customize_control_type = 'widget_form',
+                               customize_control,
+                               parsed_widget_id = parse_widget_id( widget_id ),
+                               widget_number = parsed_widget_id.number,
+                               widget_id_base = parsed_widget_id.id_base,
+                               widget = api.Widgets.availableWidgets.findWhere( {id_base: widget_id_base} ),
+                               setting_id,
+                               is_existing_widget,
+                               Constructor,
+                               widget_form_control,
+                               sidebar_widgets,
+                               setting_args;
+
+                       if ( ! widget ) {
+                               throw new Error( 'Widget unexpectedly not found.' );
+                       }
+                       if ( widget_number && ! widget.get( 'is_multi' ) ) {
+                               throw new Error( 'Did not expect a widget number to be supplied for a non-multi widget' );
+                       }
+
+                       // Set up new multi widget
+                       if ( widget.get( 'is_multi' ) && ! widget_number ) {
+                               widget.set( 'multi_number', widget.get( 'multi_number' ) + 1 );
+                               widget_number = widget.get( 'multi_number' );
+                       }
+
+                       control_html = $( '#widget-tpl-' + widget.get( 'id' ) ).html();
+                       if ( widget.get( 'is_multi' ) ) {
+                               control_html = control_html.replace( /<[^<>]+>/g, function ( m ) {
+                                       return m.replace( /__i__|%i%/g, widget_number );
+                               } );
+                       } else {
+                               widget.set( 'is_disabled', true ); // Prevent single widget from being added again now
+                       }
+                       widget_el = $( control_html );
+
+                       customize_control = $( '<li></li>' );
+                       customize_control.addClass( 'customize-control' );
+                       customize_control.addClass( 'customize-control-' + customize_control_type );
+                       customize_control.append( widget_el );
+                       customize_control.find( '> .widget-icon' ).remove();
+                       if ( widget.get( 'is_multi' ) ) {
+                               customize_control.find( 'input[name="widget_number"]' ).val( widget_number );
+                               customize_control.find( 'input[name="multi_number"]' ).val( widget_number );
+                       }
+                       widget_id = customize_control.find( '[name="widget-id"]' ).val();
+                       customize_control.hide(); // to be slid-down below
+
+                       setting_id = 'widget_' + widget.get( 'id_base' );
+                       if ( widget.get( 'is_multi' ) ) {
+                               setting_id += '[' + widget_number + ']';
+                       }
+                       customize_control.attr( 'id', 'customize-control-' + setting_id.replace( /\]/g, '' ).replace( /\[/g, '-' ) );
+
+                       control.container.after( customize_control );
+
+                       // Only create setting if it doesn't already exist (if we're adding a pre-existing inactive widget)
+                       is_existing_widget = api.has( setting_id );
+                       if ( ! is_existing_widget ) {
+                               setting_args = {
+                                       transport: 'refresh',
+                                       previewer: control.setting.previewer
+                               };
+                               api.create( setting_id, setting_id, {}, setting_args );
+                       }
+
+                       Constructor = api.controlConstructor[customize_control_type];
+                       widget_form_control = new Constructor( setting_id, {
+                               params: {
+                                       settings: {
+                                               'default': setting_id
+                                       },
+                                       sidebar_id: control.params.sidebar_id,
+                                       widget_id: widget_id,
+                                       widget_id_base: widget.get( 'id_base' ),
+                                       type: customize_control_type,
+                                       is_new: ! is_existing_widget,
+                                       width: widget.get( 'width' ),
+                                       height: widget.get( 'height' ),
+                                       is_wide: widget.get( 'is_wide' )
+                               },
+                               previewer: control.setting.previewer
+                       } );
+                       api.control.add( setting_id, widget_form_control );
+
+                       // Make sure widget is removed from the other sidebars
+                       api.each( function ( other_setting ) {
+                               if ( other_setting.id === control.setting.id ) {
+                                       return;
+                               }
+                               if ( 0 !== other_setting.id.indexOf( 'sidebars_widgets[' ) ) {
+                                       return;
+                               }
+                               var other_sidebar_widgets = other_setting().slice(), i;
+                               i = _.indexOf( other_sidebar_widgets, widget_id );
+                               if ( -1 !== i ) {
+                                       other_sidebar_widgets.splice( i );
+                                       other_setting( other_sidebar_widgets );
+                               }
+                       } );
+
+                       // Add widget to this sidebar
+                       sidebar_widgets = control.setting().slice();
+                       if ( -1 === _.indexOf( sidebar_widgets, widget_id ) ) {
+                               sidebar_widgets.push( widget_id );
+                               control.setting( sidebar_widgets );
+                       }
+
+                       customize_control.slideDown( function() {
+                               if ( is_existing_widget ) {
+                                       widget_form_control.expandForm();
+                                       widget_form_control.updateWidget( {
+                                               instance: widget_form_control.setting(),
+                                               complete: function ( error ) {
+                                                       if ( error ) {
+                                                               throw error;
+                                                       }
+                                                       widget_form_control.focus();
+                                               }
+                                       } );
+                               } else {
+                                       widget_form_control.focus();
+                               }
+                       } );
+
+                       $( document ).trigger( 'widget-added', [ widget_el ] );
+
+                       return widget_form_control;
+               }
+
+       } );
+
+       $.extend( api.controlConstructor, {
+               widget_form: api.Widgets.WidgetControl,
+               sidebar_widgets: api.Widgets.SidebarControl
+       });
+
+       api.bind( 'ready', function() {
+               // Set up the widgets panel
+               api.Widgets.availableWidgetsPanel.setup();
+
+               // Highlight widget control
+               api.Widgets.Previewer.bind( 'highlight-widget-control', api.Widgets.highlightWidgetFormControl );
+
+               // Open and focus widget control
+               api.Widgets.Previewer.bind( 'focus-widget-control', api.Widgets.focusWidgetFormControl );
+       } );
+
+       /**
</ins><span class="cx">    * Capture the instance of the Previewer since it is private
</span><span class="cx">   */
</span><del>-       OldPreviewer = wp.customize.Previewer;
-       wp.customize.Previewer = OldPreviewer.extend( {
</del><ins>+        OldPreviewer = api.Previewer;
+       api.Previewer = OldPreviewer.extend({
</ins><span class="cx">           initialize: function( params, options ) {
</span><del>-                       self.previewer = this;
</del><ins>+                        api.Widgets.Previewer = this;
</ins><span class="cx">                   OldPreviewer.prototype.initialize.call( this, params, options );
</span><span class="cx">                  this.bind( 'refresh', this.refresh );
</span><span class="cx">          }
</span><span class="lines">@@ -1537,8 +1514,8 @@
</span><span class="cx">   *
</span><span class="cx">   * @param {string} widgetId
</span><span class="cx">   */
</span><del>-       self.highlightWidgetFormControl = function( widgetId ) {
-               var control = self.getWidgetFormControlForWidget( widgetId );
</del><ins>+        api.Widgets.highlightWidgetFormControl = function( widgetId ) {
+               var control = api.Widgets.getWidgetFormControlForWidget( widgetId );
</ins><span class="cx"> 
</span><span class="cx">          if ( control ) {
</span><span class="cx">                  control.highlightSectionAndControl();
</span><span class="lines">@@ -1550,8 +1527,8 @@
</span><span class="cx">   *
</span><span class="cx">   * @param {string} widgetId
</span><span class="cx">   */
</span><del>-       self.focusWidgetFormControl = function( widgetId ) {
-               var control = self.getWidgetFormControlForWidget( widgetId );
</del><ins>+        api.Widgets.focusWidgetFormControl = function( widgetId ) {
+               var control = api.Widgets.getWidgetFormControlForWidget( widgetId );
</ins><span class="cx"> 
</span><span class="cx">          if ( control ) {
</span><span class="cx">                  control.focus();
</span><span class="lines">@@ -1563,14 +1540,15 @@
</span><span class="cx">   * @param {string} widget_id
</span><span class="cx">   * @return {object|null}
</span><span class="cx">   */
</span><del>-       self.getSidebarWidgetControlContainingWidget = function ( widget_id ) {
</del><ins>+        api.Widgets.getSidebarWidgetControlContainingWidget = function ( widget_id ) {
</ins><span class="cx">           var found_control = null;
</span><span class="cx">          // @todo this can use widget_id_to_setting_id(), then pass into wp.customize.control( x ).getSidebarWidgetsControl()
</span><del>-               wp.customize.control.each( function ( control ) {
</del><ins>+                api.control.each( function ( control ) {
</ins><span class="cx">                   if ( control.params.type === 'sidebar_widgets' && -1 !== _.indexOf( control.setting(), widget_id ) ) {
</span><span class="cx">                          found_control = control;
</span><span class="cx">                  }
</span><span class="cx">          } );
</span><ins>+
</ins><span class="cx">           return found_control;
</span><span class="cx">  };
</span><span class="cx"> 
</span><span class="lines">@@ -1579,28 +1557,22 @@
</span><span class="cx">   * @param {string} widget_id
</span><span class="cx">   * @return {object|null}
</span><span class="cx">   */
</span><del>-       self.getWidgetFormControlForWidget = function ( widget_id ) {
</del><ins>+        api.Widgets.getWidgetFormControlForWidget = function ( widget_id ) {
</ins><span class="cx">           var found_control = null;
</span><span class="cx">          // @todo We can just use widget_id_to_setting_id() here
</span><del>-               wp.customize.control.each( function ( control ) {
</del><ins>+                api.control.each( function ( control ) {
</ins><span class="cx">                   if ( control.params.type === 'widget_form' && control.params.widget_id === widget_id ) {
</span><span class="cx">                          found_control = control;
</span><span class="cx">                  }
</span><span class="cx">          } );
</span><ins>+
</ins><span class="cx">           return found_control;
</span><span class="cx">  };
</span><span class="cx"> 
</span><span class="cx">  /**
</span><del>-        * @returns {Window}
-        */
-       self.getPreviewWindow = function (){
-               return $( '#customize-preview' ).find( 'iframe' ).prop( 'contentWindow' );
-       };
-
-       /**
</del><span class="cx">    * Available Widgets Panel
</span><span class="cx">   */
</span><del>-       self.availableWidgetsPanel = {
</del><ins>+        api.Widgets.availableWidgetsPanel = {
</ins><span class="cx">           active_sidebar_widgets_control: null,
</span><span class="cx">          selected_widget_tpl: null,
</span><span class="cx">          container: null,
</span><span class="lines">@@ -1609,13 +1581,13 @@
</span><span class="cx">          /**
</span><span class="cx">           * Set up event listeners
</span><span class="cx">           */
</span><del>-               setup: function () {
</del><ins>+                setup: function() {
</ins><span class="cx">                   var panel = this;
</span><span class="cx"> 
</span><span class="cx">                  panel.container = $( '#available-widgets' );
</span><span class="cx">                  panel.filter_input = $( '#available-widgets-filter' ).find( 'input' );
</span><span class="cx"> 
</span><del>-                       self.available_widgets.on( 'change update', panel.update_available_widgets_list );
</del><ins>+                        api.Widgets.availableWidgets.on( 'change update', panel.update_available_widgets_list );
</ins><span class="cx">                   panel.update_available_widgets_list();
</span><span class="cx"> 
</span><span class="cx">                  // If the available widgets panel is open and the customize controls are
</span><span class="lines">@@ -1629,7 +1601,7 @@
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="cx">                  // Close the panel if the URL in the preview changes
</span><del>-                       self.previewer.bind( 'url', function () {
</del><ins>+                        api.Widgets.Previewer.bind( 'url', function() {
</ins><span class="cx">                           panel.close();
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="lines">@@ -1647,7 +1619,7 @@
</span><span class="cx">                  panel.filter_input.on( 'input keyup change', function( event ) {
</span><span class="cx">                          var first_visible_widget;
</span><span class="cx"> 
</span><del>-                               self.available_widgets.doSearch( event.target.value );
</del><ins>+                                api.Widgets.availableWidgets.doSearch( event.target.value );
</ins><span class="cx"> 
</span><span class="cx">                          // Remove a widget from being selected if it is no longer visible
</span><span class="cx">                          if ( panel.selected_widget_tpl && ! panel.selected_widget_tpl.is( ':visible' ) ) {
</span><span class="lines">@@ -1671,7 +1643,7 @@
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="cx">                  // Select a widget when it is focused on
</span><del>-                       panel.container.find( ' > .widget-tpl' ).on( 'focus', function () {
</del><ins>+                        panel.container.find( ' > .widget-tpl' ).on( 'focus', function() {
</ins><span class="cx">                           panel.select( this );
</span><span class="cx">                  } );
</span><span class="cx"> 
</span><span class="lines">@@ -1725,13 +1697,13 @@
</span><span class="cx">           * Updates widgets list.
</span><span class="cx">           */
</span><span class="cx">          update_available_widgets_list: function() {
</span><del>-                       var panel = self.availableWidgetsPanel;
</del><ins>+                        var panel = api.Widgets.availableWidgetsPanel;
</ins><span class="cx"> 
</span><span class="cx">                  // First hide all widgets...
</span><span class="cx">                  panel.container.find( '.widget-tpl' ).hide();
</span><span class="cx"> 
</span><span class="cx">                  // ..and then show only available widgets which could be filtered
</span><del>-                       self.available_widgets.each( function ( widget ) {
</del><ins>+                        api.Widgets.availableWidgets.each( function ( widget ) {
</ins><span class="cx">                           var widget_tpl = $( '#widget-tpl-' + widget.id );
</span><span class="cx">                          widget_tpl.toggle( ! widget.get( 'is_disabled' ) );
</span><span class="cx">                          if ( widget.get( 'is_disabled' ) && widget_tpl.is( panel.selected_widget_tpl ) ) {
</span><span class="lines">@@ -1761,7 +1733,7 @@
</span><span class="cx">                  panel.select( widget_tpl );
</span><span class="cx"> 
</span><span class="cx">                  widget_id = $( panel.selected_widget_tpl ).data( 'widget-id' );
</span><del>-                       widget = self.available_widgets.findWhere( {id: widget_id} );
</del><ins>+                        widget = api.Widgets.availableWidgets.findWhere( {id: widget_id} );
</ins><span class="cx">                   if ( ! widget ) {
</span><span class="cx">                          throw new Error( 'Widget unexpectedly not found.' );
</span><span class="cx">                  }
</span><span class="lines">@@ -1785,7 +1757,7 @@
</span><span class="cx"> 
</span><span class="cx">                  $( 'body' ).addClass( 'adding-widget' );
</span><span class="cx">                  panel.container.find( '.widget-tpl' ).removeClass( 'selected' );
</span><del>-                       self.available_widgets.doSearch( '' );
</del><ins>+                        api.Widgets.availableWidgets.doSearch( '' );
</ins><span class="cx">                   panel.filter_input.focus();
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="lines">@@ -1839,5 +1811,4 @@
</span><span class="cx">          return setting_id;
</span><span class="cx">  }
</span><span class="cx"> 
</span><del>-       return self;
-}( jQuery ));
</del><ins>+})( window.wp, jQuery );
</ins></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizecontrolphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/class-wp-customize-control.php (27984 => 27985)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/class-wp-customize-control.php     2014-04-07 09:02:06 UTC (rev 27984)
+++ trunk/src/wp-includes/class-wp-customize-control.php        2014-04-07 09:03:18 UTC (rev 27985)
</span><span class="lines">@@ -917,12 +917,12 @@
</span><span class="cx">  public function render_content() {
</span><span class="cx">          ?>
</span><span class="cx">          <span class="button-secondary add-new-widget" tabindex="0">
</span><del>-                       <?php esc_html_e( 'Add a Widget' ); ?>
</del><ins>+                        <?php _e( 'Add a Widget' ); ?>
</ins><span class="cx">           </span>
</span><span class="cx"> 
</span><span class="cx">          <span class="reorder-toggle" tabindex="0">
</span><del>-                       <span class="reorder"><?php esc_html_e( 'Reorder' ); ?></span>
-                       <span class="reorder-done"><?php esc_html_e( 'Done' ); ?></span>
</del><ins>+                        <span class="reorder"><?php _ex( 'Reorder', 'Reorder widgets in Customizer' ); ?></span>
+                       <span class="reorder-done"><?php _ex( 'Done', 'Cancel reordering widgets in Customizer'  ); ?></span>
</ins><span class="cx">           </span>
</span><span class="cx">          <?php
</span><span class="cx">  }
</span><span class="lines">@@ -940,11 +940,10 @@
</span><span class="cx">  public $width;
</span><span class="cx">  public $height;
</span><span class="cx">  public $is_wide = false;
</span><del>-       public $is_live_previewable = false;
</del><span class="cx"> 
</span><span class="cx">  public function to_json() {
</span><span class="cx">          parent::to_json();
</span><del>-               $exported_properties = array( 'widget_id', 'widget_id_base', 'sidebar_id', 'width', 'height', 'is_wide', 'is_live_previewable' );
</del><ins>+                $exported_properties = array( 'widget_id', 'widget_id_base', 'sidebar_id', 'width', 'height', 'is_wide' );
</ins><span class="cx">           foreach ( $exported_properties as $key ) {
</span><span class="cx">                  $this->json[ $key ] = $this->$key;
</span><span class="cx">          }
</span></span></pre></div>
<a id="trunksrcwpincludesclasswpcustomizewidgetsphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/class-wp-customize-widgets.php (27984 => 27985)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/class-wp-customize-widgets.php     2014-04-07 09:02:06 UTC (rev 27984)
+++ trunk/src/wp-includes/class-wp-customize-widgets.php        2014-04-07 09:03:18 UTC (rev 27985)
</span><span class="lines">@@ -406,8 +406,6 @@
</span><span class="cx">                          $setting_id        = $this->get_setting_id( $widget_id );
</span><span class="cx">                          $id_base           = $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base'];
</span><span class="cx"> 
</span><del>-                               assert( false !== is_active_widget( $registered_widget['callback'], $registered_widget['id'], false, false ) );
-
</del><span class="cx">                           $control = new WP_Widget_Form_Customize_Control( $this->manager, $setting_id, array(
</span><span class="cx">                                  'label'          => $registered_widget['name'],
</span><span class="cx">                                  'section'        => $section_id,
</span><span class="lines">@@ -599,8 +597,8 @@
</span><span class="cx">          $move_widget_area_tpl = str_replace(
</span><span class="cx">                  array( '{description}', '{btn}' ),
</span><span class="cx">                  array(
</span><del>-                               ( 'Select an area to move this widget into:' ), // @todo translate
-                               esc_html_x( 'Move', 'move widget' ),
</del><ins>+                                __( 'Select an area to move this widget into:' ),
+                               _x( 'Move', 'Move widget' ),
</ins><span class="cx">                   ),
</span><span class="cx">                  '<div class="move-widget-area">
</span><span class="cx">                          <p class="description">{description}</p>
</span><span class="lines">@@ -615,39 +613,34 @@
</span><span class="cx">                  </div>'
</span><span class="cx">          );
</span><span class="cx"> 
</span><del>-               /*
-                * Why not wp_localize_script? Because we're not localizing,
-                * and it forces values into strings.
-                */
</del><span class="cx">           global $wp_scripts;
</span><span class="cx"> 
</span><del>-               $exports = array(
-                       'nonce'               => wp_create_nonce( 'update-widget' ),
-                       'registered_sidebars' => array_values( $GLOBALS['wp_registered_sidebars'] ),
-                       'registered_widgets'  => $GLOBALS['wp_registered_widgets'],
-                       'available_widgets'   => $available_widgets, // @todo Merge this with registered_widgets
-                       'i18n' => array(
-                               'save_btn_label'     => __( 'Apply' ),
-                               // @todo translate? do we want these tooltips?
-                               'save_btn_tooltip'   => ( 'Save and preview changes before publishing them.' ),
-                               'remove_btn_label'   => __( 'Remove' ),
-                               'remove_btn_tooltip' => ( 'Trash widget by moving it to the inactive widgets sidebar.' ),
-                               'error'              => __( 'An error has occurred. Please reload the page and try again.' ),
</del><ins>+                $settings = array(
+                       'nonce'                => wp_create_nonce( 'update-widget' ),
+                       'registeredSidebars'   => array_values( $GLOBALS['wp_registered_sidebars'] ),
+                       'registeredWidgets'    => $GLOBALS['wp_registered_widgets'],
+                       'availableWidgets'     => $available_widgets, // @todo Merge this with registered_widgets
+                       'l10n' => array(
+                               '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.' ),
</ins><span class="cx">                   ),
</span><del>-                       'tpl'                 => array(
-                               'widget_reorder_nav' => $widget_reorder_nav_tpl,
-                               'move_widget_area'   => $move_widget_area_tpl,
</del><ins>+                        'tpl' => array(
+                               'widgetReorderNav' => $widget_reorder_nav_tpl,
+                               'moveWidgetArea'   => $move_widget_area_tpl,
</ins><span class="cx">                   ),
</span><span class="cx">          );
</span><span class="cx"> 
</span><del>-               foreach ( $exports['registered_widgets'] as &$registered_widget ) {
</del><ins>+                foreach ( $settings['registeredWidgets'] as &$registered_widget ) {
</ins><span class="cx">                   unset( $registered_widget['callback'] ); // may not be JSON-serializeable
</span><span class="cx">          }
</span><span class="cx"> 
</span><span class="cx">          $wp_scripts->add_data(
</span><span class="cx">                  'customize-widgets',
</span><span class="cx">                  'data',
</span><del>-                       sprintf( 'var WidgetCustomizer_exports = %s;', json_encode( $exports ) )
</del><ins>+                        sprintf( 'var _wpCustomizeWidgetsSettings = %s;', json_encode( $settings ) )
</ins><span class="cx">           );
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="lines">@@ -662,12 +655,12 @@
</span><span class="cx">          <div id="widgets-left"><!-- compatibility with JS which looks for widget templates here -->
</span><span class="cx">          <div id="available-widgets">
</span><span class="cx">                  <div id="available-widgets-filter">
</span><del>-                               <label class="screen-reader-text" for="widgets-search"><?php _e( 'Find Widgets' ); ?></label>
-                               <input type="search" id="widgets-search" placeholder="<?php esc_attr_e( 'Find widgets&hellip;' ) ?>" />
</del><ins>+                                <label class="screen-reader-text" for="widgets-search"><?php _e( 'Search Widgets' ); ?></label>
+                               <input type="search" id="widgets-search" placeholder="<?php esc_attr_e( 'Search widgets&hellip;' ) ?>" />
</ins><span class="cx">                   </div>
</span><span class="cx">                  <?php foreach ( $this->get_available_widgets() as $available_widget ): ?>
</span><span class="cx">                          <div id="widget-tpl-<?php echo esc_attr( $available_widget['id'] ) ?>" data-widget-id="<?php echo esc_attr( $available_widget['id'] ) ?>" class="widget-tpl <?php echo esc_attr( $available_widget['id'] ) ?>" tabindex="0">
</span><del>-                                       <?php echo $available_widget['control_tpl']; // xss ok ?>
</del><ins>+                                        <?php echo $available_widget['control_tpl']; ?>
</ins><span class="cx">                           </div>
</span><span class="cx">                  <?php endforeach; ?>
</span><span class="cx">          </div><!-- #available-widgets -->
</span><span class="lines">@@ -826,6 +819,7 @@
</span><span class="cx"> 
</span><span class="cx">                  $available_widgets[] = $available_widget;
</span><span class="cx">          }
</span><ins>+
</ins><span class="cx">           return $available_widgets;
</span><span class="cx">  }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpincludesjscustomizepreviewwidgetsjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/customize-preview-widgets.js (27984 => 27985)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/customize-preview-widgets.js    2014-04-07 09:02:06 UTC (rev 27984)
+++ trunk/src/wp-includes/js/customize-preview-widgets.js       2014-04-07 09:03:18 UTC (rev 27985)
</span><span class="lines">@@ -1,4 +1,4 @@
</span><del>-(function( $, wp ){
</del><ins>+(function( wp, $ ){
</ins><span class="cx"> 
</span><span class="cx">  if ( ! wp || ! wp.customize ) { return; }
</span><span class="cx"> 
</span><span class="lines">@@ -124,4 +124,4 @@
</span><span class="cx">          api.WidgetCustomizerPreview.init();
</span><span class="cx">  });
</span><span class="cx"> 
</span><del>-})( jQuery, window.wp );
</del><ins>+})( window.wp, jQuery );
</ins></span></pre>
</div>
</div>

</body>
</html>