// Set up our namespace...
var api = wp.customize,
- l10n, OldPreviewer;
+ l10n;
api.Widgets = api.Widgets || {};
// If the available widgets panel is open and the customize controls are
// interacted with (i.e. available widgets panel is blurred) then close the
// available widgets panel.
- $( '#customize-controls' ).on( 'click keydown', function( e ) {
+ $( '#customize-controls, .customize-overlay-close' ).on( 'click keydown', function( e ) {
var isAddNewBtn = $( e.target ).is( '.add-new-widget, .add-new-widget *' );
if ( $( 'body' ).hasClass( 'adding-widget' ) && ! isAddNewBtn ) {
self.close();
} );
// Close the panel if the URL in the preview changes
- api.Widgets.Previewer.bind( 'url', this.close );
+ api.previewer.bind( 'url', this.close );
},
// Performs a search and handles selected widget
// Adds a selected widget to the sidebar
submit: function( widgetTpl ) {
- var widgetId, widget;
+ var widgetId, widget, widgetFormControl;
if ( ! widgetTpl ) {
widgetTpl = this.selected;
return;
}
- this.currentSidebarControl.addWidget( widget.get( 'id_base' ) );
+ widgetFormControl = this.currentSidebarControl.addWidget( widget.get( 'id_base' ) );
+ if ( widgetFormControl ) {
+ widgetFormControl.focus();
+ }
this.close();
},
// Reset search
this.collection.doSearch( '' );
- this.$search.focus();
+ if ( ! api.settings.browser.mobile ) {
+ this.$search.focus();
+ }
},
// Closes the panel
isEsc = ( event.which === 27 ),
isDown = ( event.which === 40 ),
isUp = ( event.which === 38 ),
+ isTab = ( event.which === 9 ),
+ isShift = ( event.shiftKey ),
selected = null,
firstVisible = this.$el.find( '> .widget-tpl:visible:first' ),
lastVisible = this.$el.find( '> .widget-tpl:visible:last' ),
- isSearchFocused = $( event.target ).is( this.$search );
+ isSearchFocused = $( event.target ).is( this.$search ),
+ isLastWidgetFocused = $( event.target ).is( '.widget-tpl:visible:last' );
if ( isDown || isUp ) {
if ( isDown ) {
} else if ( isEsc ) {
this.close( { returnFocus: true } );
}
+
+ if ( isTab && ( isShift && isSearchFocused || ! isShift && isLastWidgetFocused ) ) {
+ this.currentSidebarControl.container.find( '.add-new-widget' ).focus();
+ event.preventDefault();
+ }
}
});
* @augments wp.customize.Control
*/
api.Widgets.WidgetControl = api.Control.extend({
+ defaultExpandedArguments: {
+ duration: 'fast',
+ completeCallback: $.noop
+ },
+
+ /**
+ * @since 4.1.0
+ */
+ initialize: function ( id, options ) {
+ var control = this;
+ api.Control.prototype.initialize.call( control, id, options );
+ control.expanded = new api.Value();
+ control.expandedArgumentsQueue = [];
+ control.expanded.bind( function ( expanded ) {
+ var args = control.expandedArgumentsQueue.shift();
+ args = $.extend( {}, control.defaultExpandedArguments, args );
+ control.onChangeExpanded( expanded, args );
+ });
+ control.expanded.set( false );
+ },
+
/**
* Set up the control
*/
this._setupHighlightEffects();
this._setupUpdateUI();
this._setupRemoveUI();
+
+ /*
+ * Trigger widget-added event so that plugins can attach any event
+ * listeners and dynamic UI elements.
+ */
+ $( document ).trigger( 'widget-added', [ this.container.find( '.widget:first' ) ] );
},
/**
if ( sidebarWidgetsControl.isReordering ) {
return;
}
- self.toggleForm();
+ self.expanded( ! self.expanded() );
} );
$closeBtn = this.container.find( '.widget-control-close' );
$closeBtn.on( 'click', function( e ) {
e.preventDefault();
- self.collapseForm();
+ self.collapse();
self.container.find( '.widget-top .widget-action:first' ).focus(); // keyboard accessibility
} );
},
* Update available sidebars when their rendered state changes
*/
updateAvailableSidebars = function() {
- var $sidebarItems = $moveWidgetArea.find( 'li' ), selfSidebarItem;
+ var $sidebarItems = $moveWidgetArea.find( 'li' ), selfSidebarItem,
+ renderedSidebarCount = 0;
selfSidebarItem = $sidebarItems.filter( function(){
return $( this ).data( 'id' ) === self.params.sidebar_id;
$sidebarItems.each( function() {
var li = $( this ),
- sidebarId,
- sidebar;
+ sidebarId, sidebar, sidebarIsRendered;
sidebarId = li.data( 'id' );
sidebar = api.Widgets.registeredSidebars.get( sidebarId );
+ sidebarIsRendered = sidebar.get( 'is_rendered' );
- li.toggle( sidebar.get( 'is_rendered' ) );
+ li.toggle( sidebarIsRendered );
- if ( li.hasClass( 'selected' ) && ! sidebar.get( 'is_rendered' ) ) {
+ if ( sidebarIsRendered ) {
+ renderedSidebarCount += 1;
+ }
+
+ if ( li.hasClass( 'selected' ) && ! sidebarIsRendered ) {
selectSidebarItem( selfSidebarItem );
}
} );
+
+ if ( renderedSidebarCount > 1 ) {
+ self.container.find( '.move-widget' ).show();
+ } else {
+ self.container.find( '.move-widget' ).hide();
+ }
};
updateAvailableSidebars();
* Handle clicks for up/down/move on the reorder nav
*/
$reorderNav = this.container.find( '.widget-reorder-nav' );
- $reorderNav.find( '.move-widget, .move-widget-down, .move-widget-up' ).on( 'click keypress', function( event ) {
+ $reorderNav.find( '.move-widget, .move-widget-down, .move-widget-up' ).each( function() {
+ $( this ).prepend( self.container.find( '.widget-title' ).text() + ': ' );
+ } ).on( 'click keypress', function( event ) {
if ( event.type === 'keypress' && ( event.which !== 13 && event.which !== 32 ) ) {
return;
}
if ( isMoveUp ) {
self.moveUp();
+ wp.a11y.speak( l10n.widgetMovedUp );
} else {
self.moveDown();
+ wp.a11y.speak( l10n.widgetMovedDown );
}
$( this ).focus(); // re-focus after the container was moved
/**
* Handle selecting a sidebar to move to
*/
- this.container.find( '.widget-area-select' ).on( 'click keypress', 'li', function( e ) {
+ this.container.find( '.widget-area-select' ).on( 'click keypress', 'li', function( event ) {
if ( event.type === 'keypress' && ( event.which !== 13 && event.which !== 32 ) ) {
return;
}
- e.preventDefault();
+ event.preventDefault();
selectSidebarItem( $( this ) );
} );
},
/**
- * Highlight widgets in preview when interacted with in the customizer
+ * Highlight widgets in preview when interacted with in the Customizer
*/
_setupHighlightEffects: function() {
var self = this;
self.container.removeClass( 'previewer-loading' );
} );
- api.Widgets.Previewer.bind( 'widget-updated', function( updatedWidgetId ) {
+ api.previewer.bind( 'widget-updated', function( updatedWidgetId ) {
if ( updatedWidgetId === self.params.widget_id ) {
self.container.removeClass( 'previewer-loading' );
}
} );
- // Update widget control to indicate whether it is currently rendered
- api.Widgets.Previewer.bind( 'rendered-widgets', function( renderedWidgets ) {
- var isRendered = !! renderedWidgets[self.params.widget_id];
-
- self.container.toggleClass( 'widget-rendered', isRendered );
- } );
-
formSyncHandler = api.Widgets.formSyncHandlers[ this.params.widget_id_base ];
if ( formSyncHandler ) {
$( document ).on( 'widget-synced', function( e, widget ) {
}
},
+ /**
+ * Update widget control to indicate whether it is currently rendered.
+ *
+ * Overrides api.Control.toggle()
+ *
+ * @since 4.1.0
+ *
+ * @param {Boolean} active
+ * @param {Object} args
+ * @param {Callback} args.completeCallback
+ */
+ onChangeActive: function ( active, args ) {
+ // Note: there is a second 'args' parameter being passed, merged on top of this.defaultActiveArguments
+ this.container.toggleClass( 'widget-rendered', active );
+ if ( args.completeCallback ) {
+ args.completeCallback();
+ }
+ },
+
/**
* Set up event handlers for widget removal
*/
},
/**
- * Get the property that represents the state of an input.
+ * Get the state for an input depending on its type.
*
- * @param {jQuery|DOMElement} input
- * @returns {string}
+ * @param {jQuery|Element} input
+ * @returns {string|boolean|array|*}
* @private
*/
- _getInputStatePropertyName: function( input ) {
- var $input = $( input );
+ _getInputState: function( input ) {
+ input = $( input );
+ if ( input.is( ':radio, :checkbox' ) ) {
+ return input.prop( 'checked' );
+ } else if ( input.is( 'select[multiple]' ) ) {
+ return input.find( 'option:selected' ).map( function () {
+ return $( this ).val();
+ } ).get();
+ } else {
+ return input.val();
+ }
+ },
- if ( $input.is( ':radio, :checkbox' ) ) {
- return 'checked';
+ /**
+ * Update an input's state based on its type.
+ *
+ * @param {jQuery|Element} input
+ * @param {string|boolean|array|*} state
+ * @private
+ */
+ _setInputState: function ( input, state ) {
+ input = $( input );
+ if ( input.is( ':radio, :checkbox' ) ) {
+ input.prop( 'checked', state );
+ } else if ( input.is( 'select[multiple]' ) ) {
+ if ( ! $.isArray( state ) ) {
+ state = [];
+ } else {
+ // Make sure all state items are strings since the DOM value is a string
+ state = _.map( state, function ( value ) {
+ return String( value );
+ } );
+ }
+ input.find( 'option' ).each( function () {
+ $( this ).prop( 'selected', -1 !== _.indexOf( state, String( this.value ) ) );
+ } );
} else {
- return 'value';
+ input.val( state );
}
},
// we know if it got sanitized; if there is no difference in the sanitized value,
// then we do not need to touch the UI and mess up the user's ongoing editing.
$inputs.each( function() {
- var input = $( this ),
- property = self._getInputStatePropertyName( this );
- input.data( 'state' + updateNumber, input.prop( property ) );
+ $( this ).data( 'state' + updateNumber, self._getInputState( this ) );
} );
if ( instanceOverride ) {
}
data += '&' + $widgetContent.find( '~ :input' ).serialize();
+ if ( this._previousUpdateRequest ) {
+ this._previousUpdateRequest.abort();
+ }
jqxhr = $.post( wp.ajax.settings.url, data );
+ this._previousUpdateRequest = jqxhr;
jqxhr.done( function( r ) {
var message, sanitizedForm, $sanitizedInputs, hasSameInputsInResponse,
// Check if the user is logged out.
if ( '0' === r ) {
- api.Widgets.Previewer.preview.iframe.hide();
- api.Widgets.Previewer.login().done( function() {
+ api.previewer.preview.iframe.hide();
+ api.previewer.login().done( function() {
self.updateWidget( args );
- api.Widgets.Previewer.preview.iframe.show();
+ api.previewer.preview.iframe.show();
} );
return;
}
// Check for cheaters.
if ( '-1' === r ) {
- api.Widgets.Previewer.cheatin();
+ api.previewer.cheatin();
return;
}
$inputs.each( function( i ) {
var $input = $( this ),
$sanitizedInput = $( $sanitizedInputs[i] ),
- property = self._getInputStatePropertyName( this ),
submittedState, sanitizedState, canUpdateState;
submittedState = $input.data( 'state' + updateNumber );
- sanitizedState = $sanitizedInput.prop( property );
+ sanitizedState = self._getInputState( $sanitizedInput );
$input.data( 'sanitized', sanitizedState );
- canUpdateState = ( submittedState !== sanitizedState && ( args.ignoreActiveElement || ! $input.is( document.activeElement ) ) );
+ canUpdateState = ( ! _.isEqual( submittedState, sanitizedState ) && ( args.ignoreActiveElement || ! $input.is( document.activeElement ) ) );
if ( canUpdateState ) {
- $input.prop( property, sanitizedState );
+ self._setInputState( $input, sanitizedState );
}
} );
* Expand the accordion section containing a control
*/
expandControlSection: function() {
- var $section = this.container.closest( '.accordion-section' );
-
- if ( ! $section.hasClass( 'open' ) ) {
- $section.find( '.accordion-section-title:first' ).trigger( 'click' );
- }
+ api.Control.prototype.expand.call( this );
},
+ /**
+ * @since 4.1.0
+ *
+ * @param {Boolean} expanded
+ * @param {Object} [params]
+ * @returns {Boolean} false if state already applied
+ */
+ _toggleExpanded: api.Section.prototype._toggleExpanded,
+
+ /**
+ * @since 4.1.0
+ *
+ * @param {Object} [params]
+ * @returns {Boolean} false if already expanded
+ */
+ expand: api.Section.prototype.expand,
+
/**
* Expand the widget form control
+ *
+ * @deprecated 4.1.0 Use this.expand() instead.
*/
expandForm: function() {
- this.toggleForm( true );
+ this.expand();
},
+ /**
+ * @since 4.1.0
+ *
+ * @param {Object} [params]
+ * @returns {Boolean} false if already collapsed
+ */
+ collapse: api.Section.prototype.collapse,
+
/**
* Collapse the widget form control
+ *
+ * @deprecated 4.1.0 Use this.collapse() instead.
*/
collapseForm: function() {
- this.toggleForm( false );
+ this.collapse();
},
/**
* Expand or collapse the widget control
*
+ * @deprecated this is poor naming, and it is better to directly set control.expanded( showOrHide )
+ *
* @param {boolean|undefined} [showOrHide] If not supplied, will be inverse of current visibility
*/
toggleForm: function( showOrHide ) {
- var self = this, $widget, $inside, complete;
-
- $widget = this.container.find( 'div.widget:first' );
- $inside = $widget.find( '.widget-inside:first' );
if ( typeof showOrHide === 'undefined' ) {
- showOrHide = ! $inside.is( ':visible' );
+ showOrHide = ! this.expanded();
}
+ this.expanded( showOrHide );
+ },
- // Already expanded or collapsed, so noop
- if ( $inside.is( ':visible' ) === showOrHide ) {
+ /**
+ * Respond to change in the expanded state.
+ *
+ * @param {Boolean} expanded
+ * @param {Object} args merged on top of this.defaultActiveArguments
+ */
+ onChangeExpanded: function ( expanded, args ) {
+ var self = this, $widget, $inside, complete, prevComplete;
+
+ // If the expanded state is unchanged only manipulate container expanded states
+ if ( args.unchanged ) {
+ if ( expanded ) {
+ api.Control.prototype.expand.call( self, {
+ completeCallback: args.completeCallback
+ });
+ }
return;
}
- if ( showOrHide ) {
+ $widget = this.container.find( 'div.widget:first' );
+ $inside = $widget.find( '.widget-inside:first' );
+
+ if ( expanded ) {
+
+ self.expandControlSection();
+
// Close all other widget controls before expanding this one
api.control.each( function( otherControl ) {
if ( self.params.type === otherControl.params.type && self !== otherControl ) {
- otherControl.collapseForm();
+ otherControl.collapse();
}
} );
self.container.addClass( 'expanded' );
self.container.trigger( 'expanded' );
};
+ if ( args.completeCallback ) {
+ prevComplete = complete;
+ complete = function () {
+ prevComplete();
+ args.completeCallback();
+ };
+ }
if ( self.params.is_wide ) {
- $inside.fadeIn( 'fast', complete );
+ $inside.fadeIn( args.duration, complete );
} else {
- $inside.slideDown( 'fast', complete );
+ $inside.slideDown( args.duration, complete );
}
self.container.trigger( 'expand' );
self.container.addClass( 'expanding' );
} else {
+
complete = function() {
self.container.removeClass( 'collapsing' );
self.container.removeClass( 'expanded' );
self.container.trigger( 'collapsed' );
};
+ if ( args.completeCallback ) {
+ prevComplete = complete;
+ complete = function () {
+ prevComplete();
+ args.completeCallback();
+ };
+ }
self.container.trigger( 'collapse' );
self.container.addClass( 'collapsing' );
if ( self.params.is_wide ) {
- $inside.fadeOut( 'fast', complete );
+ $inside.fadeOut( args.duration, complete );
} else {
- $inside.slideUp( 'fast', function() {
+ $inside.slideUp( args.duration, function() {
$widget.css( { width:'', margin:'' } );
complete();
} );
}
},
- /**
- * Expand the containing sidebar section, expand the form, and focus on
- * the first input in the control
- */
- focus: function() {
- this.expandControlSection();
- this.expandForm();
- this.container.find( '.widget-content :focusable:first' ).focus();
- },
-
/**
* Get the position (index) of the widget in the containing sidebar
*
}
} );
+ /**
+ * wp.customize.Widgets.SidebarSection
+ *
+ * Customizer section representing a widget area widget
+ *
+ * @since 4.1.0
+ */
+ api.Widgets.SidebarSection = api.Section.extend({
+
+ /**
+ * Sync the section's active state back to the Backbone model's is_rendered attribute
+ *
+ * @since 4.1.0
+ */
+ ready: function () {
+ var section = this, registeredSidebar;
+ api.Section.prototype.ready.call( this );
+ registeredSidebar = api.Widgets.registeredSidebars.get( section.params.sidebarId );
+ section.active.bind( function ( active ) {
+ registeredSidebar.set( 'is_rendered', active );
+ });
+ registeredSidebar.set( 'is_rendered', section.active() );
+ }
+ });
+
/**
* wp.customize.Widgets.SidebarControl
*
* Customizer control for widgets.
* Note that 'sidebar_widgets' must match the WP_Widget_Area_Customize_Control::$type
*
+ * @since 3.9.0
+ *
* @constructor
* @augments wp.customize.Control
*/
api.Widgets.SidebarControl = api.Control.extend({
+
/**
* Set up the control
*/
* Update ordering of widget control forms when the setting is updated
*/
_setupModel: function() {
- var self = this,
- registeredSidebar = api.Widgets.registeredSidebars.get( this.params.sidebar_id );
+ var self = this;
this.setting.bind( function( newWidgetIds, oldWidgetIds ) {
- var widgetFormControls, $sidebarWidgetsAddControl, finalControlContainers, removedWidgetIds;
+ var widgetFormControls, removedWidgetIds, priority;
removedWidgetIds = _( oldWidgetIds ).difference( newWidgetIds );
widgetFormControls.sort( function( a, b ) {
var aIndex = _.indexOf( newWidgetIds, a.params.widget_id ),
bIndex = _.indexOf( newWidgetIds, b.params.widget_id );
+ return aIndex - bIndex;
+ });
- if ( aIndex === bIndex ) {
- return 0;
- }
-
- return aIndex < bIndex ? -1 : 1;
- } );
-
- // Append the controls to put them in the right order
- finalControlContainers = _( widgetFormControls ).map( function( widgetFormControls ) {
- return widgetFormControls.container[0];
- } );
-
- $sidebarWidgetsAddControl = self.$sectionContent.find( '.customize-control-sidebar_widgets' );
- $sidebarWidgetsAddControl.before( finalControlContainers );
+ priority = 0;
+ _( widgetFormControls ).each( function ( control ) {
+ control.priority( priority );
+ control.section( self.section() );
+ priority += 1;
+ });
+ self.priority( priority ); // Make sure sidebar control remains at end
// Re-sort widget form controls (including widgets form other sidebars newly moved here)
self._applyCardinalOrderClassNames();
} );
} );
-
- // Update the model with whether or not the sidebar is rendered
- api.Widgets.Previewer.bind( 'rendered-sidebars', function( renderedSidebars ) {
- var isRendered = !! renderedSidebars[self.params.sidebar_id];
-
- registeredSidebar.set( 'is_rendered', isRendered );
- } );
-
- // Show the sidebar section when it becomes visible
- registeredSidebar.on( 'change:is_rendered', function( ) {
- var sectionSelector = '#accordion-section-sidebar-widgets-' + this.get( 'id' ), $section;
-
- $section = $( sectionSelector );
- 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();
- }
- } );
},
/**
} );
/**
- * Expand other customizer sidebar section when dragging a control widget over it,
+ * Expand other Customizer sidebar section when dragging a control widget over it,
* allowing the control to be dropped into another section
*/
this.$controlSection.find( '.accordion-section-title' ).droppable({
accept: '.customize-control-widget_form',
over: function() {
- if ( ! self.$controlSection.hasClass( 'open' ) ) {
- self.$controlSection.addClass( 'open' );
- self.$sectionContent.toggle( false ).slideToggle( 150, function() {
- self.$sectionContent.sortable( 'refreshPositions' );
- } );
- }
+ var section = api.section( self.section.get() );
+ section.expand({
+ allowMultiple: true, // Prevent the section being dragged from to be collapsed
+ completeCallback: function () {
+ // @todo It is not clear when refreshPositions should be called on which sections, or if it is even needed
+ api.section.each( function ( otherSection ) {
+ if ( otherSection.container.find( '.customize-control-sidebar_widgets' ).length ) {
+ otherSection.container.find( '.accordion-section-content:first' ).sortable( 'refreshPositions' );
+ }
+ } );
+ }
+ });
}
});
* Add classes to the widget_form controls to assist with styling
*/
_applyCardinalOrderClassNames: function() {
- this.$sectionContent.find( '.customize-control-widget_form' )
- .removeClass( 'first-widget' )
- .removeClass( 'last-widget' )
- .find( '.move-widget-down, .move-widget-up' ).prop( 'tabIndex', 0 );
+ var widgetControls = [];
+ _.each( this.setting(), function ( widgetId ) {
+ var widgetControl = api.Widgets.getWidgetFormControlForWidget( widgetId );
+ if ( widgetControl ) {
+ widgetControls.push( widgetControl );
+ }
+ });
+
+ if ( ! widgetControls.length ) {
+ this.container.find( '.reorder-toggle' ).hide();
+ return;
+ } else {
+ this.container.find( '.reorder-toggle' ).show();
+ }
- this.$sectionContent.find( '.customize-control-widget_form:first' )
+ $( widgetControls ).each( function () {
+ $( this.container )
+ .removeClass( 'first-widget' )
+ .removeClass( 'last-widget' )
+ .find( '.move-widget-down, .move-widget-up' ).prop( 'tabIndex', 0 );
+ });
+
+ _.first( widgetControls ).container
.addClass( 'first-widget' )
.find( '.move-widget-up' ).prop( 'tabIndex', -1 );
- this.$sectionContent.find( '.customize-control-widget_form:last' )
+ _.last( widgetControls ).container
.addClass( 'last-widget' )
.find( '.move-widget-down' ).prop( 'tabIndex', -1 );
},
* Enable/disable the reordering UI
*
* @param {Boolean} showOrHide to enable/disable reordering
+ *
+ * @todo We should have a reordering state instead and rename this to onChangeReordering
*/
toggleReordering: function( showOrHide ) {
showOrHide = Boolean( showOrHide );
if ( showOrHide ) {
_( this.getWidgetFormControls() ).each( function( formControl ) {
- formControl.collapseForm();
+ formControl.collapse();
} );
+
+ this.$sectionContent.find( '.first-widget .move-widget' ).focus();
+ this.$sectionContent.find( '.add-new-widget' ).prop( 'tabIndex', -1 );
+ } else {
+ this.$sectionContent.find( '.add-new-widget' ).prop( 'tabIndex', 0 );
}
},
/**
+ * Get the widget_form Customize controls associated with the current sidebar.
+ *
+ * @since 3.9
* @return {wp.customize.controlConstructor.widget_form[]}
*/
getWidgetFormControls: function() {
- var formControls;
+ var formControls = [];
- formControls = _( this.setting() ).map( function( widgetId ) {
+ _( this.setting() ).each( function( widgetId ) {
var settingId = widgetIdToSettingId( widgetId ),
formControl = api.control( settingId );
-
- if ( ! formControl ) {
- return;
+ if ( formControl ) {
+ formControls.push( formControl );
}
-
- return formControl;
} );
return formControls;
* @returns {object|false} widget_form control instance, or false on error
*/
addWidget: function( widgetId ) {
- var self = this, controlHtml, $widget, controlType = 'widget_form', $control, controlConstructor,
+ var self = this, controlHtml, $widget, controlType = 'widget_form', controlContainer, controlConstructor,
parsedWidgetId = parseWidgetId( widgetId ),
widgetNumber = parsedWidgetId.number,
widgetIdBase = parsedWidgetId.id_base,
widget = api.Widgets.availableWidgets.findWhere( {id_base: widgetIdBase} ),
- settingId, isExistingWidget, widgetFormControl, sidebarWidgets, settingArgs;
+ settingId, isExistingWidget, widgetFormControl, sidebarWidgets, settingArgs, setting;
if ( ! widget ) {
return false;
$widget = $( controlHtml );
- $control = $( '<li/>' )
+ controlContainer = $( '<li/>' )
.addClass( 'customize-control' )
.addClass( 'customize-control-' + controlType )
.append( $widget );
// Remove icon which is visible inside the panel
- $control.find( '> .widget-icon' ).remove();
+ controlContainer.find( '> .widget-icon' ).remove();
if ( widget.get( 'is_multi' ) ) {
- $control.find( 'input[name="widget_number"]' ).val( widgetNumber );
- $control.find( 'input[name="multi_number"]' ).val( widgetNumber );
+ controlContainer.find( 'input[name="widget_number"]' ).val( widgetNumber );
+ controlContainer.find( 'input[name="multi_number"]' ).val( widgetNumber );
}
- widgetId = $control.find( '[name="widget-id"]' ).val();
+ widgetId = controlContainer.find( '[name="widget-id"]' ).val();
- $control.hide(); // to be slid-down below
+ controlContainer.hide(); // to be slid-down below
settingId = 'widget_' + widget.get( 'id_base' );
if ( widget.get( 'is_multi' ) ) {
settingId += '[' + widgetNumber + ']';
}
- $control.attr( 'id', 'customize-control-' + settingId.replace( /\]/g, '' ).replace( /\[/g, '-' ) );
-
- this.container.after( $control );
+ controlContainer.attr( 'id', 'customize-control-' + settingId.replace( /\]/g, '' ).replace( /\[/g, '-' ) );
// Only create setting if it doesn't already exist (if we're adding a pre-existing inactive widget)
isExistingWidget = api.has( settingId );
transport: 'refresh',
previewer: this.setting.previewer
};
- api.create( settingId, settingId, {}, settingArgs );
+ setting = api.create( settingId, settingId, '', settingArgs );
+ setting.set( {} ); // mark dirty, changing from '' to {}
}
controlConstructor = api.controlConstructor[controlType];
settings: {
'default': settingId
},
+ content: controlContainer,
sidebar_id: self.params.sidebar_id,
widget_id: widgetId,
widget_id_base: widget.get( 'id_base' ),
this.setting( sidebarWidgets );
}
- $control.slideDown( function() {
+ controlContainer.slideDown( function() {
if ( isExistingWidget ) {
- widgetFormControl.expandForm();
widgetFormControl.updateWidget( {
- instance: widgetFormControl.setting(),
- complete: function( error ) {
- if ( error ) {
- throw error;
- }
- widgetFormControl.focus();
- }
+ instance: widgetFormControl.setting()
} );
- } else {
- widgetFormControl.focus();
}
} );
- $( document ).trigger( 'widget-added', [ $widget ] );
-
return widgetFormControl;
}
} );
- /**
- * Extends wp.customizer.controlConstructor with control constructor for
- * widget_form and sidebar_widgets.
- */
+ // Register models for custom section and control types
+ $.extend( api.sectionConstructor, {
+ sidebar: api.Widgets.SidebarSection
+ });
$.extend( api.controlConstructor, {
widget_form: api.Widgets.WidgetControl,
sidebar_widgets: api.Widgets.SidebarControl
});
- /**
- * Capture the instance of the Previewer since it is private
- */
- OldPreviewer = api.Previewer;
- api.Previewer = OldPreviewer.extend({
- initialize: function( params, options ) {
- api.Widgets.Previewer = this;
- OldPreviewer.prototype.initialize.call( this, params, options );
- this.bind( 'refresh', this.refresh );
- }
- } );
+ // Refresh the nonce if login sends updated nonces over.
+ api.bind( 'nonce-refresh', function( nonces ) {
+ api.Widgets.data.nonce = nonces['update-widget'];
+ });
/**
* Init Customizer for widgets.
});
// Highlight widget control
- api.Widgets.Previewer.bind( 'highlight-widget-control', api.Widgets.highlightWidgetFormControl );
+ api.previewer.bind( 'highlight-widget-control', api.Widgets.highlightWidgetFormControl );
// Open and focus widget control
- api.Widgets.Previewer.bind( 'focus-widget-control', api.Widgets.focusWidgetFormControl );
+ api.previewer.bind( 'focus-widget-control', api.Widgets.focusWidgetFormControl );
} );
/**