]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-admin/js/customize-widgets.js
WordPress 4.4
[autoinstalls/wordpress.git] / wp-admin / js / customize-widgets.js
index 6a640e0efa544b1a0701b2a40f331d87007c4858..820e8e3cab6abac50f780651a4023bdecbd04011 100644 (file)
@@ -8,6 +8,7 @@
                l10n;
 
        api.Widgets = api.Widgets || {};
                l10n;
 
        api.Widgets = api.Widgets || {};
+       api.Widgets.savedWidgetIds = {};
 
        // Link settings
        api.Widgets.data = _wpCustomizeWidgetsSettings || {};
 
        // Link settings
        api.Widgets.data = _wpCustomizeWidgetsSettings || {};
                /**
                 * @since 4.1.0
                 */
                /**
                 * @since 4.1.0
                 */
-               initialize: function ( id, options ) {
+               initialize: function( id, options ) {
                        var control = this;
                        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.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 );
                        });
                                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() {
                 */
                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.
                         */
 
                        /*
                         * 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' ) ] );
+
                },
 
                /**
                },
 
                /**
                _setupModel: function() {
                        var self = this, rememberSavedWidgetId;
 
                _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;
                        // Remember saved widgets so we know which to trash (move to inactive widgets sidebar)
                        rememberSavedWidgetId = function() {
                                api.Widgets.savedWidgetIds[self.params.widget_id] = true;
                        var self = this, instanceOverride, completeCallback, $widgetRoot, $widgetContent,
                                updateNumber, params, data, $inputs, processing, jqxhr, isChanged;
 
                        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,
                        args = $.extend( {
                                instance: null,
                                complete: null,
                onChangeExpanded: function ( expanded, args ) {
                        var self = this, $widget, $inside, complete, prevComplete;
 
                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 ) {
                        // If the expanded state is unchanged only manipulate container expanded states
                        if ( args.unchanged ) {
                                if ( expanded ) {
                }
        } );
 
                }
        } );
 
+       /**
+        * 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 = $( '<div></div>', {
+                                       'class': 'no-widget-areas-rendered-notice'
+                               });
+                               noRenderedAreasNotice.append( $( '<em></em>', {
+                                       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
         *
        /**
         * wp.customize.Widgets.SidebarSection
         *
                        /**
                         * Keyboard-accessible reordering
                         */
                        /**
                         * 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 );
                        } );
                },
                                self.toggleReordering( ! self.isReordering );
                        } );
                },
                _setupAddition: function() {
                        var self = this;
 
                _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' ) ) {
 
                                if ( self.$sectionContent.hasClass( 'reordering' ) ) {
                                        return;
                                }
 
                                if ( ! $( 'body' ).hasClass( 'adding-widget' ) ) {
+                                       addNewWidgetBtn.attr( 'aria-expanded', 'true' );
                                        api.Widgets.availableWidgetsPanel.open( self );
                                } else {
                                        api.Widgets.availableWidgetsPanel.open( self );
                                } else {
+                                       addNewWidgetBtn.attr( 'aria-expanded', 'false' );
                                        api.Widgets.availableWidgetsPanel.close();
                                }
                        } );
                                        api.Widgets.availableWidgetsPanel.close();
                                }
                        } );
                 * @todo We should have a reordering state instead and rename this to onChangeReordering
                 */
                toggleReordering: function( showOrHide ) {
                 * @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' ) ) {
                        showOrHide = Boolean( showOrHide );
 
                        if ( showOrHide === this.$sectionContent.hasClass( 'reordering' ) ) {
                                        formControl.collapse();
                                } );
 
                                        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 {
                        } 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' );
                        }
                },
 
                        }
                },
 
                                        is_new: ! isExistingWidget,
                                        width: widget.get( 'width' ),
                                        height: widget.get( 'height' ),
                                        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
                        } );
                                },
                                previewer: self.setting.previewer
                        } );
                }
        } );
 
                }
        } );
 
-       // 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
        });
        $.extend( api.sectionConstructor, {
                sidebar: api.Widgets.SidebarSection
        });