X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/dc1231b7312fbdca99e9e887cc2bb35a28f85cdc..refs/tags/wordpress-4.4:/wp-admin/js/customize-widgets.js diff --git a/wp-admin/js/customize-widgets.js b/wp-admin/js/customize-widgets.js index 6a640e0e..820e8e3c 100644 --- a/wp-admin/js/customize-widgets.js +++ b/wp-admin/js/customize-widgets.js @@ -8,6 +8,7 @@ l10n; api.Widgets = api.Widgets || {}; + api.Widgets.savedWidgetIds = {}; // Link settings api.Widgets.data = _wpCustomizeWidgetsSettings || {}; @@ -417,37 +418,104 @@ /** * @since 4.1.0 */ - initialize: function ( id, options ) { + initialize: function( id, options ) { var control = this; - api.Control.prototype.initialize.call( control, id, options ); - control.expanded = new api.Value(); + + control.widgetControlEmbedded = false; + control.widgetContentEmbedded = false; + control.expanded = new api.Value( false ); control.expandedArgumentsQueue = []; - control.expanded.bind( function ( expanded ) { + control.expanded.bind( function( expanded ) { var args = control.expandedArgumentsQueue.shift(); args = $.extend( {}, control.defaultExpandedArguments, args ); control.onChangeExpanded( expanded, args ); }); - control.expanded.set( false ); + + api.Control.prototype.initialize.call( control, id, options ); }, /** - * Set up the control + * Set up the control. + * + * @since 3.9.0 */ ready: function() { - this._setupModel(); - this._setupWideWidget(); - this._setupControlToggle(); - this._setupWidgetTitle(); - this._setupReorderUI(); - this._setupHighlightEffects(); - this._setupUpdateUI(); - this._setupRemoveUI(); + var control = this; + + /* + * Embed a placeholder once the section is expanded. The full widget + * form content will be embedded once the control itself is expanded, + * and at this point the widget-added event will be triggered. + */ + if ( ! control.section() ) { + control.embedWidgetControl(); + } else { + api.section( control.section(), function( section ) { + var onExpanded = function( isExpanded ) { + if ( isExpanded ) { + control.embedWidgetControl(); + section.expanded.unbind( onExpanded ); + } + }; + if ( section.expanded() ) { + onExpanded( true ); + } else { + section.expanded.bind( onExpanded ); + } + } ); + } + }, + + /** + * Embed the .widget element inside the li container. + * + * @since 4.4.0 + */ + embedWidgetControl: function() { + var control = this, widgetControl; + + if ( control.widgetControlEmbedded ) { + return; + } + control.widgetControlEmbedded = true; + + widgetControl = $( control.params.widget_control ); + control.container.append( widgetControl ); + + control._setupModel(); + control._setupWideWidget(); + control._setupControlToggle(); + + control._setupWidgetTitle(); + control._setupReorderUI(); + control._setupHighlightEffects(); + control._setupUpdateUI(); + control._setupRemoveUI(); + }, + + /** + * Embed the actual widget form inside of .widget-content and finally trigger the widget-added event. + * + * @since 4.4.0 + */ + embedWidgetContent: function() { + var control = this, widgetContent; + + control.embedWidgetControl(); + if ( control.widgetContentEmbedded ) { + return; + } + control.widgetContentEmbedded = true; + + widgetContent = $( control.params.widget_content ); + control.container.find( '.widget-content:first' ).append( widgetContent ); /* * 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' ) ] ); + $( document ).trigger( 'widget-added', [ control.container.find( '.widget:first' ) ] ); + }, /** @@ -456,8 +524,6 @@ _setupModel: function() { var self = this, rememberSavedWidgetId; - api.Widgets.savedWidgetIds = api.Widgets.savedWidgetIds || []; - // Remember saved widgets so we know which to trash (move to inactive widgets sidebar) rememberSavedWidgetId = function() { api.Widgets.savedWidgetIds[self.params.widget_id] = true; @@ -1008,6 +1074,9 @@ var self = this, instanceOverride, completeCallback, $widgetRoot, $widgetContent, updateNumber, params, data, $inputs, processing, jqxhr, isChanged; + // The updateWidget logic requires that the form fields to be fully present. + self.embedWidgetContent(); + args = $.extend( { instance: null, complete: null, @@ -1255,6 +1324,11 @@ onChangeExpanded: function ( expanded, args ) { var self = this, $widget, $inside, complete, prevComplete; + self.embedWidgetControl(); // Make sure the outer form is embedded so that the expanded state can be set in the UI. + if ( expanded ) { + self.embedWidgetContent(); + } + // If the expanded state is unchanged only manipulate container expanded states if ( args.unchanged ) { if ( expanded ) { @@ -1431,6 +1505,77 @@ } } ); + /** + * wp.customize.Widgets.WidgetsPanel + * + * Customizer panel containing the widget area sections. + * + * @since 4.4.0 + */ + api.Widgets.WidgetsPanel = api.Panel.extend({ + + /** + * Add and manage the display of the no-rendered-areas notice. + * + * @since 4.4.0 + */ + ready: function () { + var panel = this; + + api.Panel.prototype.ready.call( panel ); + + panel.deferred.embedded.done(function() { + var panelMetaContainer, noRenderedAreasNotice, shouldShowNotice; + panelMetaContainer = panel.container.find( '.panel-meta' ); + noRenderedAreasNotice = $( '
', { + 'class': 'no-widget-areas-rendered-notice' + }); + noRenderedAreasNotice.append( $( '', { + text: l10n.noAreasRendered + } ) ); + panelMetaContainer.append( noRenderedAreasNotice ); + + shouldShowNotice = function() { + return ( 0 === _.filter( panel.sections(), function( section ) { + return section.active(); + } ).length ); + }; + + /* + * Set the initial visibility state for rendered notice. + * Update the visibility of the notice whenever a reflow happens. + */ + noRenderedAreasNotice.toggle( shouldShowNotice() ); + api.previewer.deferred.active.done( function () { + noRenderedAreasNotice.toggle( shouldShowNotice() ); + }); + api.bind( 'pane-contents-reflowed', function() { + var duration = ( 'resolved' === api.previewer.deferred.active.state() ) ? 'fast' : 0; + if ( shouldShowNotice() ) { + noRenderedAreasNotice.slideDown( duration ); + } else { + noRenderedAreasNotice.slideUp( duration ); + } + }); + }); + }, + + /** + * Allow an active widgets panel to be contextually active even when it has no active sections (widget areas). + * + * This ensures that the widgets panel appears even when there are no + * sidebars displayed on the URL currently being previewed. + * + * @since 4.4.0 + * + * @returns {boolean} + */ + isContextuallyActive: function() { + var panel = this; + return panel.active(); + } + }); + /** * wp.customize.Widgets.SidebarSection * @@ -1644,11 +1789,7 @@ /** * Keyboard-accessible reordering */ - this.container.find( '.reorder-toggle' ).on( 'click keydown', function( event ) { - if ( event.type === 'keydown' && ! ( event.which === 13 || event.which === 32 ) ) { // Enter or Spacebar - return; - } - + this.container.find( '.reorder-toggle' ).on( 'click', function() { self.toggleReordering( ! self.isReordering ); } ); }, @@ -1659,18 +1800,18 @@ _setupAddition: function() { var self = this; - this.container.find( '.add-new-widget' ).on( 'click keydown', function( event ) { - if ( event.type === 'keydown' && ! ( event.which === 13 || event.which === 32 ) ) { // Enter or Spacebar - return; - } + this.container.find( '.add-new-widget' ).on( 'click', function() { + var addNewWidgetBtn = $( this ); if ( self.$sectionContent.hasClass( 'reordering' ) ) { return; } if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) { + addNewWidgetBtn.attr( 'aria-expanded', 'true' ); api.Widgets.availableWidgetsPanel.open( self ); } else { + addNewWidgetBtn.attr( 'aria-expanded', 'false' ); api.Widgets.availableWidgetsPanel.close(); } } ); @@ -1724,6 +1865,10 @@ * @todo We should have a reordering state instead and rename this to onChangeReordering */ toggleReordering: function( showOrHide ) { + var addNewWidgetBtn = this.$sectionContent.find( '.add-new-widget' ), + reorderBtn = this.container.find( '.reorder-toggle' ), + widgetsTitle = this.$sectionContent.find( '.widget-title' ); + showOrHide = Boolean( showOrHide ); if ( showOrHide === this.$sectionContent.hasClass( 'reordering' ) ) { @@ -1738,10 +1883,16 @@ formControl.collapse(); } ); - this.$sectionContent.find( '.first-widget .move-widget' ).focus(); - this.$sectionContent.find( '.add-new-widget' ).prop( 'tabIndex', -1 ); + addNewWidgetBtn.attr({ 'tabindex': '-1', 'aria-hidden': 'true' }); + reorderBtn.attr( 'aria-label', l10n.reorderLabelOff ); + wp.a11y.speak( l10n.reorderModeOn ); + // Hide widget titles while reordering: title is already in the reorder controls. + widgetsTitle.attr( 'aria-hidden', 'true' ); } else { - this.$sectionContent.find( '.add-new-widget' ).prop( 'tabIndex', 0 ); + addNewWidgetBtn.removeAttr( 'tabindex aria-hidden' ); + reorderBtn.attr( 'aria-label', l10n.reorderLabelOn ); + wp.a11y.speak( l10n.reorderModeOff ); + widgetsTitle.attr( 'aria-hidden', 'false' ); } }, @@ -1850,7 +2001,8 @@ is_new: ! isExistingWidget, width: widget.get( 'width' ), height: widget.get( 'height' ), - is_wide: widget.get( 'is_wide' ) + is_wide: widget.get( 'is_wide' ), + active: true }, previewer: self.setting.previewer } ); @@ -1894,7 +2046,10 @@ } } ); - // Register models for custom section and control types + // Register models for custom panel, section, and control types + $.extend( api.panelConstructor, { + widgets: api.Widgets.WidgetsPanel + }); $.extend( api.sectionConstructor, { sidebar: api.Widgets.SidebarSection });