Changeset 37040
- Timestamp:
- 03/21/2016 09:58:02 PM (9 years ago)
- Location:
- trunk
- Files:
-
- 34 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/js/customize-widgets.js
r36586 r37040 35 35 name: null, 36 36 id_base: null, 37 transport: api.Widgets.data.selectiveRefresh ? 'postMessage' : 'refresh',37 transport: null, 38 38 params: [], 39 39 width: null, … … 1983 1983 if ( ! isExistingWidget ) { 1984 1984 settingArgs = { 1985 transport: api.Widgets.data.selectiveRefresh ? 'postMessage' : 'refresh',1985 transport: api.Widgets.data.selectiveRefreshableWidgets[ widget.get( 'id_base' ) ] ? 'postMessage' : 'refresh', 1986 1986 previewer: this.setting.previewer 1987 1987 }; -
trunk/src/wp-content/themes/twentyeleven/functions.php
r32116 r37040 227 227 ) 228 228 ) ); 229 230 // Indicate widget sidebars can use selective refresh in the Customizer. 231 add_theme_support( 'customize-selective-refresh-widgets' ); 229 232 } 230 233 endif; // twentyeleven_setup -
trunk/src/wp-content/themes/twentyeleven/inc/widgets.php
r33086 r37040 22 22 'classname' => 'widget_twentyeleven_ephemera', 23 23 'description' => __( 'Use this widget to list your recent Aside, Status, Quote, and Link posts', 'twentyeleven' ), 24 'customize_selective_refresh' => true, 24 25 ) ); 25 26 $this->alt_option_name = 'widget_twentyeleven_ephemera'; -
trunk/src/wp-content/themes/twentyfifteen/functions.php
r36913 r37040 126 126 */ 127 127 add_editor_style( array( 'css/editor-style.css', 'genericons/genericons.css', twentyfifteen_fonts_url() ) ); 128 129 // Indicate widget sidebars can use selective refresh in the Customizer. 130 add_theme_support( 'customize-selective-refresh-widgets' ); 128 131 } 129 132 endif; // twentyfifteen_setup -
trunk/src/wp-content/themes/twentyfourteen/functions.php
r32843 r37040 114 114 // This theme uses its own gallery styles. 115 115 add_filter( 'use_default_gallery_style', '__return_false' ); 116 117 // Indicate widget sidebars can use selective refresh in the Customizer. 118 add_theme_support( 'customize-selective-refresh-widgets' ); 116 119 } 117 120 endif; // twentyfourteen_setup -
trunk/src/wp-content/themes/twentyfourteen/inc/widgets.php
r32116 r37040 35 35 'classname' => 'widget_twentyfourteen_ephemera', 36 36 'description' => __( 'Use this widget to list your recent Aside, Quote, Video, Audio, Image, Gallery, and Link posts.', 'twentyfourteen' ), 37 'customize_selective_refresh' => true, 37 38 ) ); 39 40 if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) { 41 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); 42 } 43 } 44 45 /** 46 * Enqueue scripts. 47 * 48 * @since Twenty Fourteen 1.7 49 */ 50 public function enqueue_scripts() { 51 /** This filter is documented in wp-includes/media.php */ 52 $audio_library = apply_filters( 'wp_audio_shortcode_library', 'mediaelement' ); 53 /** This filter is documented in wp-includes/media.php */ 54 $video_library = apply_filters( 'wp_video_shortcode_library', 'mediaelement' ); 55 if ( in_array( 'mediaelement', array( $video_library, $audio_library ), true ) ) { 56 wp_enqueue_style( 'wp-mediaelement' ); 57 wp_enqueue_script( 'wp-mediaelement' ); 58 } 38 59 } 39 60 -
trunk/src/wp-content/themes/twentyfourteen/js/functions.js
r36999 r37040 147 147 148 148 _window.load( function() { 149 var footerSidebar, 150 isCustomizeSelectiveRefresh = ( 'undefined' !== typeof wp && wp.customize && wp.customize.selectiveRefresh ); 151 149 152 // Arrange footer widgets vertically. 150 153 if ( $.isFunction( $.fn.masonry ) ) { 151 $( '#footer-sidebar' ).masonry( { 154 footerSidebar = $( '#footer-sidebar' ); 155 footerSidebar.masonry( { 152 156 itemSelector: '.widget', 153 157 columnWidth: function( containerWidth ) { … … 158 162 isRTL: $( 'body' ).is( '.rtl' ) 159 163 } ); 164 165 if ( isCustomizeSelectiveRefresh ) { 166 167 // Retain previous masonry-brick initial position. 168 wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) { 169 var copyPosition = ( 170 placement.partial.extended( wp.customize.widgetsPreview.WidgetPartial ) && 171 placement.removedNodes instanceof jQuery && 172 placement.removedNodes.is( '.masonry-brick' ) && 173 placement.container instanceof jQuery 174 ); 175 if ( copyPosition ) { 176 placement.container.css( { 177 position: placement.removedNodes.css( 'position' ), 178 top: placement.removedNodes.css( 'top' ), 179 left: placement.removedNodes.css( 'left' ) 180 } ); 181 } 182 } ); 183 184 // Re-arrange footer widgets after selective refresh event. 185 wp.customize.selectiveRefresh.bind( 'sidebar-updated', function( sidebarPartial ) { 186 if ( 'sidebar-3' === sidebarPartial.sidebarId ) { 187 footerSidebar.masonry( 'reloadItems' ); 188 footerSidebar.masonry( 'layout' ); 189 } 190 } ); 191 } 192 } 193 194 // Initialize audio and video players in Twenty_Fourteen_Ephemera_Widget widget when selectively refreshed in Customizer. 195 if ( isCustomizeSelectiveRefresh && wp.mediaelement ) { 196 wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function() { 197 wp.mediaelement.initialize(); 198 } ); 160 199 } 161 200 -
trunk/src/wp-content/themes/twentythirteen/functions.php
r36797 r37040 106 106 // This theme uses its own gallery styles. 107 107 add_filter( 'use_default_gallery_style', '__return_false' ); 108 109 // Indicate widget sidebars can use selective refresh in the Customizer. 110 add_theme_support( 'customize-selective-refresh-widgets' ); 108 111 } 109 112 add_action( 'after_setup_theme', 'twentythirteen_setup' ); -
trunk/src/wp-content/themes/twentythirteen/js/functions.js
r36999 r37040 121 121 */ 122 122 if ( $.isFunction( $.fn.masonry ) ) { 123 var columnWidth = body.is( '.sidebar' ) ? 228 : 245; 123 var columnWidth = body.is( '.sidebar' ) ? 228 : 245, 124 widgetArea = $( '#secondary .widget-area' ); 124 125 125 $( '#secondary .widget-area' ).masonry( {126 widgetArea.masonry( { 126 127 itemSelector: '.widget', 127 128 columnWidth: columnWidth, … … 129 130 isRTL: body.is( '.rtl' ) 130 131 } ); 132 133 if ( 'undefined' !== typeof wp && wp.customize && wp.customize.selectiveRefresh ) { 134 135 // Retain previous masonry-brick initial position. 136 wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) { 137 var copyPosition = ( 138 placement.partial.extended( wp.customize.widgetsPreview.WidgetPartial ) && 139 placement.removedNodes instanceof jQuery && 140 placement.removedNodes.is( '.masonry-brick' ) && 141 placement.container instanceof jQuery 142 ); 143 if ( copyPosition ) { 144 placement.container.css( { 145 position: placement.removedNodes.css( 'position' ), 146 top: placement.removedNodes.css( 'top' ), 147 left: placement.removedNodes.css( 'left' ) 148 } ); 149 } 150 } ); 151 152 // Re-arrange footer widgets when sidebar is updated via selective refresh in the Customizer. 153 wp.customize.selectiveRefresh.bind( 'sidebar-updated', function( sidebarPartial ) { 154 if ( 'sidebar-1' === sidebarPartial.sidebarId ) { 155 widgetArea.masonry( 'reloadItems' ); 156 widgetArea.masonry( 'layout' ); 157 } 158 } ); 159 } 131 160 } 132 161 } )( jQuery ); -
trunk/src/wp-content/themes/twentythirteen/js/theme-customizer.js
r36586 r37040 39 39 } ); 40 40 } ); 41 42 if ( wp.customize.selectiveRefresh ) {43 wp.customize.selectiveRefresh.bind( 'sidebar-updated', function( sidebarPartial ) {44 var widgetArea;45 if ( 'sidebar-1' === sidebarPartial.sidebarId && $.isFunction( $.fn.masonry ) ) {46 widgetArea = $( '#secondary .widget-area' );47 widgetArea.masonry( 'destroy' );48 widgetArea.masonry();49 }50 } );51 }52 53 41 } )( jQuery ); -
trunk/src/wp-content/themes/twentytwelve/functions.php
r36797 r37040 75 75 add_theme_support( 'post-thumbnails' ); 76 76 set_post_thumbnail_size( 624, 9999 ); // Unlimited height, soft crop 77 78 // Indicate widget sidebars can use selective refresh in the Customizer. 79 add_theme_support( 'customize-selective-refresh-widgets' ); 77 80 } 78 81 add_action( 'after_setup_theme', 'twentytwelve_setup' ); -
trunk/src/wp-includes/class-wp-customize-manager.php
r36915 r37040 110 110 * @var array 111 111 */ 112 protected $components = array( 'widgets', 'nav_menus' , 'selective_refresh');112 protected $components = array( 'widgets', 'nav_menus' ); 113 113 114 114 /** … … 259 259 $components = apply_filters( 'customize_loaded_components', $this->components, $this ); 260 260 261 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-selective-refresh.php' ); 262 $this->selective_refresh = new WP_Customize_Selective_Refresh( $this ); 263 261 264 if ( in_array( 'widgets', $components, true ) ) { 262 265 require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); … … 267 270 require_once( ABSPATH . WPINC . '/class-wp-customize-nav-menus.php' ); 268 271 $this->nav_menus = new WP_Customize_Nav_Menus( $this ); 269 }270 271 if ( in_array( 'selective_refresh', $components, true ) ) {272 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-selective-refresh.php' );273 $this->selective_refresh = new WP_Customize_Selective_Refresh( $this );274 272 } 275 273 … … 1731 1729 'documentTitleTmpl' => $this->get_document_title_template(), 1732 1730 'previewableDevices' => $this->get_previewable_devices(), 1733 'selectiveRefreshEnabled' => isset( $this->selective_refresh ),1734 1731 ); 1735 1732 … … 1979 1976 ) ) ); 1980 1977 1981 if ( isset( $this->selective_refresh ) ) { 1982 $this->selective_refresh->add_partial( 'custom_logo', array( 1983 'settings' => array( 'custom_logo' ), 1984 'selector' => '.custom-logo-link', 1985 'render_callback' => array( $this, '_render_custom_logo_partial' ), 1986 'container_inclusive' => true, 1987 ) ); 1988 } 1978 $this->selective_refresh->add_partial( 'site_logo', array( 1979 'settings' => array( 'site_logo' ), 1980 'selector' => '.site-logo-link', 1981 'render_callback' => array( $this, '_render_site_logo_partial' ), 1982 'container_inclusive' => true, 1983 ) ); 1989 1984 1990 1985 /* Colors */ -
trunk/src/wp-includes/class-wp-customize-nav-menus.php
r36889 r37040 394 394 'reorderLabelOff' => esc_attr__( 'Close reorder mode' ), 395 395 ), 396 'settingTransport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',396 'settingTransport' => 'postMessage', 397 397 'phpIntMax' => PHP_INT_MAX, 398 398 'defaultSettingValues' => array( … … 446 446 $setting_args = array( 447 447 'type' => WP_Customize_Nav_Menu_Setting::TYPE, 448 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',448 'transport' => 'postMessage', 449 449 ); 450 450 } elseif ( preg_match( WP_Customize_Nav_Menu_Item_Setting::ID_PATTERN, $setting_id ) ) { 451 451 $setting_args = array( 452 452 'type' => WP_Customize_Nav_Menu_Item_Setting::TYPE, 453 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',453 'transport' => 'postMessage', 454 454 ); 455 455 } … … 536 536 $setting = $this->manager->get_setting( $setting_id ); 537 537 if ( $setting ) { 538 $setting->transport = isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh';538 $setting->transport = 'postMessage'; 539 539 remove_filter( "customize_sanitize_{$setting_id}", 'absint' ); 540 540 add_filter( "customize_sanitize_{$setting_id}", array( $this, 'intval_base10' ) ); … … 544 544 'theme_supports' => 'menus', 545 545 'type' => 'theme_mod', 546 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',546 'transport' => 'postMessage', 547 547 'default' => 0, 548 548 ) ); … … 571 571 $nav_menu_setting_id = 'nav_menu[' . $menu_id . ']'; 572 572 $this->manager->add_setting( new WP_Customize_Nav_Menu_Setting( $this->manager, $nav_menu_setting_id, array( 573 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',573 'transport' => 'postMessage', 574 574 ) ) ); 575 575 … … 586 586 $this->manager->add_setting( new WP_Customize_Nav_Menu_Item_Setting( $this->manager, $menu_item_setting_id, array( 587 587 'value' => $value, 588 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',588 'transport' => 'postMessage', 589 589 ) ) ); 590 590 … … 989 989 */ 990 990 public function customize_preview_enqueue_deps() { 991 if ( isset( $this->manager->selective_refresh ) ) {992 $script = wp_scripts()->registered['customize-preview-nav-menus'];993 $script->deps[] = 'customize-selective-refresh';994 }995 996 991 wp_enqueue_script( 'customize-preview-nav-menus' ); // Note that we have overridden this. 997 992 wp_enqueue_style( 'customize-preview' ); -
trunk/src/wp-includes/class-wp-customize-widgets.php
r36842 r37040 63 63 64 64 /** 65 * Mapping of widget ID base to whether it supports selective refresh. 66 * 67 * @since 4.5.0 68 * @access protected 69 * @var array 70 */ 71 protected $selective_refreshable_widgets; 72 73 /** 65 74 * Mapping of setting type to setting ID pattern. 66 75 * … … 70 79 */ 71 80 protected $setting_id_patterns = array( 72 'widget_instance' => '/^ (widget_.+?)(?:\[(\d+)\])?$/',73 'sidebar_widgets' => '/^sidebars_widgets\[( .+?)\]$/',81 'widget_instance' => '/^widget_(?P<id_base>.+?)(?:\[(?P<widget_number>\d+)\])?$/', 82 'sidebar_widgets' => '/^sidebars_widgets\[(?P<sidebar_id>.+?)\]$/', 74 83 ); 75 84 … … 113 122 114 123 /** 124 * List whether each registered widget can be use selective refresh. 125 * 126 * If the theme does not support the customize-selective-refresh-widgets feature, 127 * then this will always return an empty array. 128 * 129 * @since 4.5.0 130 * @access public 131 * 132 * @return array Mapping of id_base to support. If theme doesn't support 133 * selective refresh, an empty array is returned. 134 */ 135 public function get_selective_refreshable_widgets() { 136 global $wp_widget_factory; 137 if ( ! current_theme_supports( 'customize-selective-refresh-widgets' ) ) { 138 return array(); 139 } 140 if ( ! isset( $this->selective_refreshable_widgets ) ) { 141 $this->selective_refreshable_widgets = array(); 142 foreach ( $wp_widget_factory->widgets as $wp_widget ) { 143 $this->selective_refreshable_widgets[ $wp_widget->id_base ] = ! empty( $wp_widget->widget_options['customize_selective_refresh'] ); 144 } 145 } 146 return $this->selective_refreshable_widgets; 147 } 148 149 /** 150 * Determines if a widget supports selective refresh. 151 * 152 * @since 4.5.0 153 * @access public 154 * 155 * @param string $id_base Widget ID Base. 156 * @return bool Whether the widget can be selective refreshed. 157 */ 158 public function is_widget_selective_refreshable( $id_base ) { 159 $selective_refreshable_widgets = $this->get_selective_refreshable_widgets(); 160 return ! empty( $selective_refreshable_widgets[ $id_base ] ); 161 } 162 163 /** 115 164 * Retrieves the widget setting type given a setting ID. 116 165 * … … 120 169 * @staticvar array $cache 121 170 * 122 * @param $setting_id Setting ID.171 * @param string $setting_id Setting ID. 123 172 * @return string|void Setting type. 124 173 */ … … 691 740 'moveWidgetArea' => $move_widget_area_tpl, 692 741 ), 693 'selectiveRefresh ' => isset( $this->manager->selective_refresh),742 'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(), 694 743 ); 695 744 … … 772 821 'type' => 'option', 773 822 'capability' => 'edit_theme_options', 774 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',775 823 'default' => array(), 776 824 ); … … 779 827 $args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' ); 780 828 $args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' ); 829 $args['transport'] = current_theme_supports( 'customize-selective-refresh-widgets' ) ? 'postMessage' : 'refresh'; 781 830 } elseif ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) { 782 831 $args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' ); 783 832 $args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' ); 833 $args['transport'] = $this->is_widget_selective_refreshable( $matches['id_base'] ) ? 'postMessage' : 'refresh'; 784 834 } 785 835 … … 894 944 'is_disabled' => $is_disabled, 895 945 'id_base' => $id_base, 896 'transport' => isset( $this->manager->selective_refresh) ? 'postMessage' : 'refresh',946 'transport' => $this->is_widget_selective_refreshable( $id_base ) ? 'postMessage' : 'refresh', 897 947 'width' => $wp_registered_widget_controls[$widget['id']]['width'], 898 948 'height' => $wp_registered_widget_controls[$widget['id']]['height'], … … 1026 1076 public function customize_preview_enqueue() { 1027 1077 wp_enqueue_script( 'customize-preview-widgets' ); 1078 wp_enqueue_style( 'customize-preview' ); 1028 1079 } 1029 1080 … … 1061 1112 public function export_preview_data() { 1062 1113 global $wp_registered_sidebars, $wp_registered_widgets; 1114 1063 1115 // Prepare Customizer settings to pass to JavaScript. 1064 1116 $settings = array( … … 1070 1122 'widgetTooltip' => __( 'Shift-click to edit this widget.' ), 1071 1123 ), 1072 'selectiveRefresh ' => isset( $this->manager->selective_refresh),1124 'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(), 1073 1125 ); 1074 1126 foreach ( $settings['registeredWidgets'] as &$registered_widget ) { … … 1480 1532 */ 1481 1533 public function customize_dynamic_partial_args( $partial_args, $partial_id ) { 1534 if ( ! current_theme_supports( 'customize-selective-refresh-widgets' ) ) { 1535 return $partial_args; 1536 } 1482 1537 1483 1538 if ( preg_match( '/^widget\[(?P<widget_id>.+)\]$/', $partial_id, $matches ) ) { … … 1507 1562 */ 1508 1563 public function selective_refresh_init() { 1509 if ( ! isset( $this->manager->selective_refresh) ) {1564 if ( ! current_theme_supports( 'customize-selective-refresh-widgets' ) ) { 1510 1565 return; 1511 1566 } 1512 1513 add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue_deps' ) );1514 1567 add_filter( 'dynamic_sidebar_params', array( $this, 'filter_dynamic_sidebar_params' ) ); 1515 1568 add_filter( 'wp_kses_allowed_html', array( $this, 'filter_wp_kses_allowed_data_attributes' ) ); 1516 1569 add_action( 'dynamic_sidebar_before', array( $this, 'start_dynamic_sidebar' ) ); 1517 1570 add_action( 'dynamic_sidebar_after', array( $this, 'end_dynamic_sidebar' ) ); 1518 }1519 1520 /**1521 * Enqueues scripts for the Customizer preview.1522 *1523 * @since 4.5.01524 * @access public1525 */1526 public function customize_preview_enqueue_deps() {1527 if ( isset( $this->manager->selective_refresh ) ) {1528 $script = wp_scripts()->registered['customize-preview-widgets'];1529 $script->deps[] = 'customize-selective-refresh';1530 }1531 1532 wp_enqueue_script( 'customize-preview-widgets' );1533 wp_enqueue_style( 'customize-preview' );1534 1571 } 1535 1572 -
trunk/src/wp-includes/class-wp-widget.php
r36541 r37040 156 156 $this->name = $name; 157 157 $this->option_name = 'widget_' . $this->id_base; 158 $this->widget_options = wp_parse_args( $widget_options, array( 'classname' => $this->option_name) );159 $this->control_options = wp_parse_args( $control_options, array( 'id_base' => $this->id_base) );158 $this->widget_options = wp_parse_args( $widget_options, array( 'classname' => $this->option_name, 'customize_selective_refresh' => false ) ); 159 $this->control_options = wp_parse_args( $control_options, array( 'id_base' => $this->id_base ) ); 160 160 } 161 161 -
trunk/src/wp-includes/js/customize-preview-widgets.js
r36586 r37040 13 13 l10n: { 14 14 widgetTooltip: '' 15 } 15 }, 16 selectiveRefreshableWidgets: {} 16 17 }; 17 18 … … 25 26 26 27 self.preview = api.preview; 27 if ( api.selectiveRefresh) {28 if ( ! _.isEmpty( self.selectiveRefreshableWidgets ) ) { 28 29 self.addPartials(); 29 30 } … … 39 40 }; 40 41 41 if ( api.selectiveRefresh ) { 42 43 /** 44 * Partial representing a widget instance. 45 * 46 * @class 47 * @augments wp.customize.selectiveRefresh.Partial 48 * @since 4.5.0 49 */ 50 self.WidgetPartial = api.selectiveRefresh.Partial.extend({ 51 52 /** 53 * Constructor. 54 * 55 * @since 4.5.0 56 * @param {string} id - Partial ID. 57 * @param {Object} options 58 * @param {Object} options.params 59 */ 60 initialize: function( id, options ) { 61 var partial = this, matches; 62 matches = id.match( /^widget\[(.+)]$/ ); 63 if ( ! matches ) { 64 throw new Error( 'Illegal id for widget partial.' ); 65 } 66 67 partial.widgetId = matches[1]; 68 options = options || {}; 69 options.params = _.extend( 70 { 71 /* Note that a selector of ('#' + partial.widgetId) is faster, but jQuery will only return the one result. */ 72 selector: '[id="' + partial.widgetId + '"]', // Alternatively, '[data-customize-widget-id="' + partial.widgetId + '"]' 73 settings: [ self.getWidgetSettingId( partial.widgetId ) ], 74 containerInclusive: true 75 }, 76 options.params || {} 42 /** 43 * Partial representing a widget instance. 44 * 45 * @class 46 * @augments wp.customize.selectiveRefresh.Partial 47 * @since 4.5.0 48 */ 49 self.WidgetPartial = api.selectiveRefresh.Partial.extend({ 50 51 /** 52 * Constructor. 53 * 54 * @since 4.5.0 55 * @param {string} id - Partial ID. 56 * @param {Object} options 57 * @param {Object} options.params 58 */ 59 initialize: function( id, options ) { 60 var partial = this, matches; 61 matches = id.match( /^widget\[(.+)]$/ ); 62 if ( ! matches ) { 63 throw new Error( 'Illegal id for widget partial.' ); 64 } 65 66 partial.widgetId = matches[1]; 67 partial.widgetIdParts = self.parseWidgetId( partial.widgetId ); 68 options = options || {}; 69 options.params = _.extend( 70 { 71 settings: [ self.getWidgetSettingId( partial.widgetId ) ], 72 containerInclusive: true 73 }, 74 options.params || {} 75 ); 76 77 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options ); 78 }, 79 80 /** 81 * Refresh widget partial. 82 * 83 * @returns {Promise} 84 */ 85 refresh: function() { 86 var partial = this, refreshDeferred; 87 if ( ! self.selectiveRefreshableWidgets[ partial.widgetIdParts.idBase ] ) { 88 refreshDeferred = $.Deferred(); 89 refreshDeferred.reject(); 90 partial.fallback(); 91 return refreshDeferred.promise(); 92 } else { 93 return api.selectiveRefresh.Partial.prototype.refresh.call( partial ); 94 } 95 }, 96 97 /** 98 * Send widget-updated message to parent so spinner will get removed from widget control. 99 * 100 * @inheritdoc 101 * @param {wp.customize.selectiveRefresh.Placement} placement 102 */ 103 renderContent: function( placement ) { 104 var partial = this; 105 if ( api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement ) ) { 106 api.preview.send( 'widget-updated', partial.widgetId ); 107 api.selectiveRefresh.trigger( 'widget-updated', partial ); 108 } 109 } 110 }); 111 112 /** 113 * Partial representing a widget area. 114 * 115 * @class 116 * @augments wp.customize.selectiveRefresh.Partial 117 * @since 4.5.0 118 */ 119 self.SidebarPartial = api.selectiveRefresh.Partial.extend({ 120 121 /** 122 * Constructor. 123 * 124 * @since 4.5.0 125 * @param {string} id - Partial ID. 126 * @param {Object} options 127 * @param {Object} options.params 128 */ 129 initialize: function( id, options ) { 130 var partial = this, matches; 131 matches = id.match( /^sidebar\[(.+)]$/ ); 132 if ( ! matches ) { 133 throw new Error( 'Illegal id for sidebar partial.' ); 134 } 135 partial.sidebarId = matches[1]; 136 137 options = options || {}; 138 options.params = _.extend( 139 { 140 settings: [ 'sidebars_widgets[' + partial.sidebarId + ']' ] 141 }, 142 options.params || {} 143 ); 144 145 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options ); 146 147 if ( ! partial.params.sidebarArgs ) { 148 throw new Error( 'The sidebarArgs param was not provided.' ); 149 } 150 if ( partial.params.settings.length > 1 ) { 151 throw new Error( 'Expected SidebarPartial to only have one associated setting' ); 152 } 153 }, 154 155 /** 156 * Set up the partial. 157 * 158 * @since 4.5.0 159 */ 160 ready: function() { 161 var sidebarPartial = this; 162 163 // Watch for changes to the sidebar_widgets setting. 164 _.each( sidebarPartial.settings(), function( settingId ) { 165 api( settingId ).bind( _.bind( sidebarPartial.handleSettingChange, sidebarPartial ) ); 166 } ); 167 168 // Trigger an event for this sidebar being updated whenever a widget inside is rendered. 169 api.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) { 170 var isAssignedWidgetPartial = ( 171 placement.partial.extended( self.WidgetPartial ) && 172 ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), placement.partial.widgetId ) ) 77 173 ); 78 79 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options ); 80 }, 81 82 /** 83 * Send widget-updated message to parent so spinner will get removed from widget control. 84 * 85 * @inheritdoc 86 * @param {wp.customize.selectiveRefresh.Placement} placement 87 */ 88 renderContent: function( placement ) { 89 var partial = this; 90 if ( api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement ) ) { 91 api.preview.send( 'widget-updated', partial.widgetId ); 92 api.selectiveRefresh.trigger( 'widget-updated', partial ); 93 } 94 } 95 }); 96 97 /** 98 * Partial representing a widget area. 99 * 100 * @class 101 * @augments wp.customize.selectiveRefresh.Partial 102 * @since 4.5.0 103 */ 104 self.SidebarPartial = api.selectiveRefresh.Partial.extend({ 105 106 /** 107 * Constructor. 108 * 109 * @since 4.5.0 110 * @param {string} id - Partial ID. 111 * @param {Object} options 112 * @param {Object} options.params 113 */ 114 initialize: function( id, options ) { 115 var partial = this, matches; 116 matches = id.match( /^sidebar\[(.+)]$/ ); 117 if ( ! matches ) { 118 throw new Error( 'Illegal id for sidebar partial.' ); 119 } 120 partial.sidebarId = matches[1]; 121 122 options = options || {}; 123 options.params = _.extend( 124 { 125 settings: [ 'sidebars_widgets[' + partial.sidebarId + ']' ] 126 }, 127 options.params || {} 128 ); 129 130 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options ); 131 132 if ( ! partial.params.sidebarArgs ) { 133 throw new Error( 'The sidebarArgs param was not provided.' ); 134 } 135 if ( partial.params.settings.length > 1 ) { 136 throw new Error( 'Expected SidebarPartial to only have one associated setting' ); 137 } 138 }, 139 140 /** 141 * Set up the partial. 142 * 143 * @since 4.5.0 144 */ 145 ready: function() { 146 var sidebarPartial = this; 147 148 // Watch for changes to the sidebar_widgets setting. 149 _.each( sidebarPartial.settings(), function( settingId ) { 150 api( settingId ).bind( _.bind( sidebarPartial.handleSettingChange, sidebarPartial ) ); 151 } ); 152 153 // Trigger an event for this sidebar being updated whenever a widget inside is rendered. 154 api.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) { 155 var isAssignedWidgetPartial = ( 156 placement.partial.extended( self.WidgetPartial ) && 157 ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), placement.partial.widgetId ) ) 158 ); 159 if ( isAssignedWidgetPartial ) { 160 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 174 if ( isAssignedWidgetPartial ) { 175 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 176 } 177 } ); 178 179 // Make sure that a widget partial has a container in the DOM prior to a refresh. 180 api.bind( 'change', function( widgetSetting ) { 181 var widgetId, parsedId; 182 parsedId = self.parseWidgetSettingId( widgetSetting.id ); 183 if ( ! parsedId ) { 184 return; 185 } 186 widgetId = parsedId.idBase; 187 if ( parsedId.number ) { 188 widgetId += '-' + String( parsedId.number ); 189 } 190 if ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), widgetId ) ) { 191 sidebarPartial.ensureWidgetPlacementContainers( widgetId ); 192 } 193 } ); 194 }, 195 196 /** 197 * Get the before/after boundary nodes for all instances of this sidebar (usually one). 198 * 199 * Note that TreeWalker is not implemented in IE8. 200 * 201 * @since 4.5.0 202 * @returns {Array.<{before: Comment, after: Comment, instanceNumber: number}>} 203 */ 204 findDynamicSidebarBoundaryNodes: function() { 205 var partial = this, regExp, boundaryNodes = {}, recursiveCommentTraversal; 206 regExp = /^(dynamic_sidebar_before|dynamic_sidebar_after):(.+):(\d+)$/; 207 recursiveCommentTraversal = function( childNodes ) { 208 _.each( childNodes, function( node ) { 209 var matches; 210 if ( 8 === node.nodeType ) { 211 matches = node.nodeValue.match( regExp ); 212 if ( ! matches || matches[2] !== partial.sidebarId ) { 213 return; 214 } 215 if ( _.isUndefined( boundaryNodes[ matches[3] ] ) ) { 216 boundaryNodes[ matches[3] ] = { 217 before: null, 218 after: null, 219 instanceNumber: parseInt( matches[3], 10 ) 220 }; 221 } 222 if ( 'dynamic_sidebar_before' === matches[1] ) { 223 boundaryNodes[ matches[3] ].before = node; 224 } else { 225 boundaryNodes[ matches[3] ].after = node; 226 } 227 } else if ( 1 === node.nodeType ) { 228 recursiveCommentTraversal( node.childNodes ); 161 229 } 162 230 } ); 163 164 // Make sure that a widget partial has a container in the DOM prior to a refresh. 165 api.bind( 'change', function( widgetSetting ) { 166 var widgetId, parsedId; 167 parsedId = self.parseWidgetSettingId( widgetSetting.id ); 168 if ( ! parsedId ) { 169 return; 231 }; 232 233 recursiveCommentTraversal( document.body.childNodes ); 234 return _.values( boundaryNodes ); 235 }, 236 237 /** 238 * Get the placements for this partial. 239 * 240 * @since 4.5.0 241 * @returns {Array} 242 */ 243 placements: function() { 244 var partial = this; 245 return _.map( partial.findDynamicSidebarBoundaryNodes(), function( boundaryNodes ) { 246 return new api.selectiveRefresh.Placement( { 247 partial: partial, 248 container: null, 249 startNode: boundaryNodes.before, 250 endNode: boundaryNodes.after, 251 context: { 252 instanceNumber: boundaryNodes.instanceNumber 170 253 } 171 widgetId = parsedId.idBase; 172 if ( parsedId.number ) { 173 widgetId += '-' + String( parsedId.number ); 174 } 175 if ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), widgetId ) ) { 176 sidebarPartial.ensureWidgetPlacementContainers( widgetId ); 177 } 178 } ); 179 }, 180 181 /** 182 * Get the before/after boundary nodes for all instances of this sidebar (usually one). 183 * 184 * Note that TreeWalker is not implemented in IE8. 185 * 186 * @since 4.5.0 187 * @returns {Array.<{before: Comment, after: Comment, instanceNumber: number}>} 188 */ 189 findDynamicSidebarBoundaryNodes: function() { 190 var partial = this, regExp, boundaryNodes = {}, recursiveCommentTraversal; 191 regExp = /^(dynamic_sidebar_before|dynamic_sidebar_after):(.+):(\d+)$/; 192 recursiveCommentTraversal = function( childNodes ) { 193 _.each( childNodes, function( node ) { 194 var matches; 195 if ( 8 === node.nodeType ) { 196 matches = node.nodeValue.match( regExp ); 197 if ( ! matches || matches[2] !== partial.sidebarId ) { 198 return; 254 } ); 255 } ); 256 }, 257 258 /** 259 * Get the list of widget IDs associated with this widget area. 260 * 261 * @since 4.5.0 262 * 263 * @returns {Array} 264 */ 265 getWidgetIds: function() { 266 var sidebarPartial = this, settingId, widgetIds; 267 settingId = sidebarPartial.settings()[0]; 268 if ( ! settingId ) { 269 throw new Error( 'Missing associated setting.' ); 270 } 271 if ( ! api.has( settingId ) ) { 272 throw new Error( 'Setting does not exist.' ); 273 } 274 widgetIds = api( settingId ).get(); 275 if ( ! _.isArray( widgetIds ) ) { 276 throw new Error( 'Expected setting to be array of widget IDs' ); 277 } 278 return widgetIds.slice( 0 ); 279 }, 280 281 /** 282 * Reflow widgets in the sidebar, ensuring they have the proper position in the DOM. 283 * 284 * @since 4.5.0 285 * 286 * @return {Array.<wp.customize.selectiveRefresh.Placement>} List of placements that were reflowed. 287 */ 288 reflowWidgets: function() { 289 var sidebarPartial = this, sidebarPlacements, widgetIds, widgetPartials, sortedSidebarContainers = []; 290 widgetIds = sidebarPartial.getWidgetIds(); 291 sidebarPlacements = sidebarPartial.placements(); 292 293 widgetPartials = {}; 294 _.each( widgetIds, function( widgetId ) { 295 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + widgetId + ']' ); 296 if ( widgetPartial ) { 297 widgetPartials[ widgetId ] = widgetPartial; 298 } 299 } ); 300 301 _.each( sidebarPlacements, function( sidebarPlacement ) { 302 var sidebarWidgets = [], needsSort = false, thisPosition, lastPosition = -1; 303 304 // Gather list of widget partial containers in this sidebar, and determine if a sort is needed. 305 _.each( widgetPartials, function( widgetPartial ) { 306 _.each( widgetPartial.placements(), function( widgetPlacement ) { 307 308 if ( sidebarPlacement.context.instanceNumber === widgetPlacement.context.sidebar_instance_number ) { 309 thisPosition = widgetPlacement.container.index(); 310 sidebarWidgets.push( { 311 partial: widgetPartial, 312 placement: widgetPlacement, 313 position: thisPosition 314 } ); 315 if ( thisPosition < lastPosition ) { 316 needsSort = true; 199 317 } 200 if ( _.isUndefined( boundaryNodes[ matches[3] ] ) ) { 201 boundaryNodes[ matches[3] ] = { 202 before: null, 203 after: null, 204 instanceNumber: parseInt( matches[3], 10 ) 205 }; 206 } 207 if ( 'dynamic_sidebar_before' === matches[1] ) { 208 boundaryNodes[ matches[3] ].before = node; 209 } else { 210 boundaryNodes[ matches[3] ].after = node; 211 } 212 } else if ( 1 === node.nodeType ) { 213 recursiveCommentTraversal( node.childNodes ); 318 lastPosition = thisPosition; 214 319 } 215 320 } ); 216 }; 217 218 recursiveCommentTraversal( document.body.childNodes ); 219 return _.values( boundaryNodes ); 220 }, 221 222 /** 223 * Get the placements for this partial. 224 * 225 * @since 4.5.0 226 * @returns {Array} 227 */ 228 placements: function() { 229 var partial = this; 230 return _.map( partial.findDynamicSidebarBoundaryNodes(), function( boundaryNodes ) { 231 return new api.selectiveRefresh.Placement( { 232 partial: partial, 233 container: null, 234 startNode: boundaryNodes.before, 235 endNode: boundaryNodes.after, 236 context: { 237 instanceNumber: boundaryNodes.instanceNumber 321 } ); 322 323 if ( needsSort ) { 324 _.each( sidebarWidgets, function( sidebarWidget ) { 325 sidebarPlacement.endNode.parentNode.insertBefore( 326 sidebarWidget.placement.container[0], 327 sidebarPlacement.endNode 328 ); 329 330 // @todo Rename partial-placement-moved? 331 api.selectiveRefresh.trigger( 'partial-content-moved', sidebarWidget.placement ); 332 } ); 333 334 sortedSidebarContainers.push( sidebarPlacement ); 335 } 336 } ); 337 338 if ( sortedSidebarContainers.length > 0 ) { 339 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 340 } 341 342 return sortedSidebarContainers; 343 }, 344 345 /** 346 * Make sure there is a widget instance container in this sidebar for the given widget ID. 347 * 348 * @since 4.5.0 349 * 350 * @param {string} widgetId 351 * @returns {wp.customize.selectiveRefresh.Partial} Widget instance partial. 352 */ 353 ensureWidgetPlacementContainers: function( widgetId ) { 354 var sidebarPartial = this, widgetPartial, wasInserted = false, partialId = 'widget[' + widgetId + ']'; 355 widgetPartial = api.selectiveRefresh.partial( partialId ); 356 if ( ! widgetPartial ) { 357 widgetPartial = new self.WidgetPartial( partialId, { 358 params: {} 359 } ); 360 api.selectiveRefresh.partial.add( widgetPartial.id, widgetPartial ); 361 } 362 363 // Make sure that there is a container element for the widget in the sidebar, if at least a placeholder. 364 _.each( sidebarPartial.placements(), function( sidebarPlacement ) { 365 var foundWidgetPlacement, widgetContainerElement; 366 367 foundWidgetPlacement = _.find( widgetPartial.placements(), function( widgetPlacement ) { 368 return ( widgetPlacement.context.sidebar_instance_number === sidebarPlacement.context.instanceNumber ); 369 } ); 370 if ( foundWidgetPlacement ) { 371 return; 372 } 373 374 widgetContainerElement = $( 375 sidebarPartial.params.sidebarArgs.before_widget.replace( '%1$s', widgetId ).replace( '%2$s', 'widget' ) + 376 sidebarPartial.params.sidebarArgs.after_widget 377 ); 378 379 widgetContainerElement.attr( 'data-customize-partial-id', widgetPartial.id ); 380 widgetContainerElement.attr( 'data-customize-partial-type', 'widget' ); 381 widgetContainerElement.attr( 'data-customize-widget-id', widgetId ); 382 383 /* 384 * Make sure the widget container element has the customize-container context data. 385 * The sidebar_instance_number is used to disambiguate multiple instances of the 386 * same sidebar are rendered onto the template, and so the same widget is embedded 387 * multiple times. 388 */ 389 widgetContainerElement.data( 'customize-partial-placement-context', { 390 'sidebar_id': sidebarPartial.sidebarId, 391 'sidebar_instance_number': sidebarPlacement.context.instanceNumber 392 } ); 393 394 sidebarPlacement.endNode.parentNode.insertBefore( widgetContainerElement[0], sidebarPlacement.endNode ); 395 wasInserted = true; 396 } ); 397 398 if ( wasInserted ) { 399 sidebarPartial.reflowWidgets(); 400 } 401 402 return widgetPartial; 403 }, 404 405 /** 406 * Handle change to the sidebars_widgets[] setting. 407 * 408 * @since 4.5.0 409 * 410 * @param {Array} newWidgetIds New widget ids. 411 * @param {Array} oldWidgetIds Old widget ids. 412 */ 413 handleSettingChange: function( newWidgetIds, oldWidgetIds ) { 414 var sidebarPartial = this, needsRefresh, widgetsRemoved, widgetsAdded, addedWidgetPartials = []; 415 416 needsRefresh = ( 417 ( oldWidgetIds.length > 0 && 0 === newWidgetIds.length ) || 418 ( newWidgetIds.length > 0 && 0 === oldWidgetIds.length ) 419 ); 420 if ( needsRefresh ) { 421 sidebarPartial.fallback(); 422 return; 423 } 424 425 // Handle removal of widgets. 426 widgetsRemoved = _.difference( oldWidgetIds, newWidgetIds ); 427 _.each( widgetsRemoved, function( removedWidgetId ) { 428 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + removedWidgetId + ']' ); 429 if ( widgetPartial ) { 430 _.each( widgetPartial.placements(), function( placement ) { 431 var isRemoved = ( 432 placement.context.sidebar_id === sidebarPartial.sidebarId || 433 ( placement.context.sidebar_args && placement.context.sidebar_args.id === sidebarPartial.sidebarId ) 434 ); 435 if ( isRemoved ) { 436 placement.container.remove(); 238 437 } 239 438 } ); 240 } ); 241 }, 242 243 /** 244 * Get the list of widget IDs associated with this widget area. 245 * 246 * @since 4.5.0 247 * 248 * @returns {Array} 249 */ 250 getWidgetIds: function() { 251 var sidebarPartial = this, settingId, widgetIds; 252 settingId = sidebarPartial.settings()[0]; 253 if ( ! settingId ) { 254 throw new Error( 'Missing associated setting.' ); 255 } 256 if ( ! api.has( settingId ) ) { 257 throw new Error( 'Setting does not exist.' ); 258 } 259 widgetIds = api( settingId ).get(); 260 if ( ! _.isArray( widgetIds ) ) { 261 throw new Error( 'Expected setting to be array of widget IDs' ); 262 } 263 return widgetIds.slice( 0 ); 264 }, 265 266 /** 267 * Reflow widgets in the sidebar, ensuring they have the proper position in the DOM. 268 * 269 * @since 4.5.0 270 * 271 * @return {Array.<wp.customize.selectiveRefresh.Placement>} List of placements that were reflowed. 272 */ 273 reflowWidgets: function() { 274 var sidebarPartial = this, sidebarPlacements, widgetIds, widgetPartials, sortedSidebarContainers = []; 275 widgetIds = sidebarPartial.getWidgetIds(); 276 sidebarPlacements = sidebarPartial.placements(); 277 278 widgetPartials = {}; 279 _.each( widgetIds, function( widgetId ) { 280 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + widgetId + ']' ); 281 if ( widgetPartial ) { 282 widgetPartials[ widgetId ] = widgetPartial; 439 } 440 } ); 441 442 // Handle insertion of widgets. 443 widgetsAdded = _.difference( newWidgetIds, oldWidgetIds ); 444 _.each( widgetsAdded, function( addedWidgetId ) { 445 var widgetPartial = sidebarPartial.ensureWidgetPlacementContainers( addedWidgetId ); 446 addedWidgetPartials.push( widgetPartial ); 447 } ); 448 449 _.each( addedWidgetPartials, function( widgetPartial ) { 450 widgetPartial.refresh(); 451 } ); 452 453 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 454 }, 455 456 /** 457 * Note that the meat is handled in handleSettingChange because it has the context of which widgets were removed. 458 * 459 * @since 4.5.0 460 */ 461 refresh: function() { 462 var partial = this, deferred = $.Deferred(); 463 464 deferred.fail( function() { 465 partial.fallback(); 466 } ); 467 468 if ( 0 === partial.placements().length ) { 469 deferred.reject(); 470 } else { 471 _.each( partial.reflowWidgets(), function( sidebarPlacement ) { 472 api.selectiveRefresh.trigger( 'partial-content-rendered', sidebarPlacement ); 473 } ); 474 deferred.resolve(); 475 } 476 477 return deferred.promise(); 478 } 479 }); 480 481 api.selectiveRefresh.partialConstructor.sidebar = self.SidebarPartial; 482 api.selectiveRefresh.partialConstructor.widget = self.WidgetPartial; 483 484 /** 485 * Add partials for the registered widget areas (sidebars). 486 * 487 * @since 4.5.0 488 */ 489 self.addPartials = function() { 490 _.each( self.registeredSidebars, function( registeredSidebar ) { 491 var partial, partialId = 'sidebar[' + registeredSidebar.id + ']'; 492 partial = api.selectiveRefresh.partial( partialId ); 493 if ( ! partial ) { 494 partial = new self.SidebarPartial( partialId, { 495 params: { 496 sidebarArgs: registeredSidebar 283 497 } 284 498 } ); 285 286 _.each( sidebarPlacements, function( sidebarPlacement ) { 287 var sidebarWidgets = [], needsSort = false, thisPosition, lastPosition = -1; 288 289 // Gather list of widget partial containers in this sidebar, and determine if a sort is needed. 290 _.each( widgetPartials, function( widgetPartial ) { 291 _.each( widgetPartial.placements(), function( widgetPlacement ) { 292 293 if ( sidebarPlacement.context.instanceNumber === widgetPlacement.context.sidebar_instance_number ) { 294 thisPosition = widgetPlacement.container.index(); 295 sidebarWidgets.push( { 296 partial: widgetPartial, 297 placement: widgetPlacement, 298 position: thisPosition 299 } ); 300 if ( thisPosition < lastPosition ) { 301 needsSort = true; 302 } 303 lastPosition = thisPosition; 304 } 305 } ); 306 } ); 307 308 if ( needsSort ) { 309 _.each( sidebarWidgets, function( sidebarWidget ) { 310 sidebarPlacement.endNode.parentNode.insertBefore( 311 sidebarWidget.placement.container[0], 312 sidebarPlacement.endNode 313 ); 314 315 // @todo Rename partial-placement-moved? 316 api.selectiveRefresh.trigger( 'partial-content-moved', sidebarWidget.placement ); 317 } ); 318 319 sortedSidebarContainers.push( sidebarPlacement ); 320 } 321 } ); 322 323 if ( sortedSidebarContainers.length > 0 ) { 324 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 325 } 326 327 return sortedSidebarContainers; 328 }, 329 330 /** 331 * Make sure there is a widget instance container in this sidebar for the given widget ID. 332 * 333 * @since 4.5.0 334 * 335 * @param {string} widgetId 336 * @returns {wp.customize.selectiveRefresh.Partial} Widget instance partial. 337 */ 338 ensureWidgetPlacementContainers: function( widgetId ) { 339 var sidebarPartial = this, widgetPartial, wasInserted = false, partialId = 'widget[' + widgetId + ']'; 340 widgetPartial = api.selectiveRefresh.partial( partialId ); 341 if ( ! widgetPartial ) { 342 widgetPartial = new self.WidgetPartial( partialId, { 343 params: {} 344 } ); 345 api.selectiveRefresh.partial.add( widgetPartial.id, widgetPartial ); 346 } 347 348 // Make sure that there is a container element for the widget in the sidebar, if at least a placeholder. 349 _.each( sidebarPartial.placements(), function( sidebarPlacement ) { 350 var foundWidgetPlacement, widgetContainerElement; 351 352 foundWidgetPlacement = _.find( widgetPartial.placements(), function( widgetPlacement ) { 353 return ( widgetPlacement.context.sidebar_instance_number === sidebarPlacement.context.instanceNumber ); 354 } ); 355 if ( foundWidgetPlacement ) { 356 return; 357 } 358 359 widgetContainerElement = $( 360 sidebarPartial.params.sidebarArgs.before_widget.replace( '%1$s', widgetId ).replace( '%2$s', 'widget' ) + 361 sidebarPartial.params.sidebarArgs.after_widget 362 ); 363 364 widgetContainerElement.attr( 'data-customize-partial-id', widgetPartial.id ); 365 widgetContainerElement.attr( 'data-customize-partial-type', 'widget' ); 366 widgetContainerElement.attr( 'data-customize-widget-id', widgetId ); 367 368 /* 369 * Make sure the widget container element has the customize-container context data. 370 * The sidebar_instance_number is used to disambiguate multiple instances of the 371 * same sidebar are rendered onto the template, and so the same widget is embedded 372 * multiple times. 373 */ 374 widgetContainerElement.data( 'customize-partial-placement-context', { 375 'sidebar_id': sidebarPartial.sidebarId, 376 'sidebar_instance_number': sidebarPlacement.context.instanceNumber 377 } ); 378 379 sidebarPlacement.endNode.parentNode.insertBefore( widgetContainerElement[0], sidebarPlacement.endNode ); 380 wasInserted = true; 381 } ); 382 383 if ( wasInserted ) { 384 sidebarPartial.reflowWidgets(); 385 } 386 387 return widgetPartial; 388 }, 389 390 /** 391 * Handle change to the sidebars_widgets[] setting. 392 * 393 * @since 4.5.0 394 * 395 * @param {Array} newWidgetIds New widget ids. 396 * @param {Array} oldWidgetIds Old widget ids. 397 */ 398 handleSettingChange: function( newWidgetIds, oldWidgetIds ) { 399 var sidebarPartial = this, needsRefresh, widgetsRemoved, widgetsAdded, addedWidgetPartials = []; 400 401 needsRefresh = ( 402 ( oldWidgetIds.length > 0 && 0 === newWidgetIds.length ) || 403 ( newWidgetIds.length > 0 && 0 === oldWidgetIds.length ) 404 ); 405 if ( needsRefresh ) { 406 sidebarPartial.fallback(); 407 return; 408 } 409 410 // Handle removal of widgets. 411 widgetsRemoved = _.difference( oldWidgetIds, newWidgetIds ); 412 _.each( widgetsRemoved, function( removedWidgetId ) { 413 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + removedWidgetId + ']' ); 414 if ( widgetPartial ) { 415 _.each( widgetPartial.placements(), function( placement ) { 416 var isRemoved = ( 417 placement.context.sidebar_id === sidebarPartial.sidebarId || 418 ( placement.context.sidebar_args && placement.context.sidebar_args.id === sidebarPartial.sidebarId ) 419 ); 420 if ( isRemoved ) { 421 placement.container.remove(); 422 } 423 } ); 424 } 425 } ); 426 427 // Handle insertion of widgets. 428 widgetsAdded = _.difference( newWidgetIds, oldWidgetIds ); 429 _.each( widgetsAdded, function( addedWidgetId ) { 430 var widgetPartial = sidebarPartial.ensureWidgetPlacementContainers( addedWidgetId ); 431 addedWidgetPartials.push( widgetPartial ); 432 } ); 433 434 _.each( addedWidgetPartials, function( widgetPartial ) { 435 widgetPartial.refresh(); 436 } ); 437 438 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 439 }, 440 441 /** 442 * Note that the meat is handled in handleSettingChange because it has the context of which widgets were removed. 443 * 444 * @since 4.5.0 445 */ 446 refresh: function() { 447 var partial = this, deferred = $.Deferred(); 448 449 deferred.fail( function() { 450 partial.fallback(); 451 } ); 452 453 if ( 0 === partial.placements().length ) { 454 deferred.reject(); 455 } else { 456 _.each( partial.reflowWidgets(), function( sidebarPlacement ) { 457 api.selectiveRefresh.trigger( 'partial-content-rendered', sidebarPlacement ); 458 } ); 459 deferred.resolve(); 460 } 461 462 return deferred.promise(); 463 } 464 }); 465 466 api.selectiveRefresh.partialConstructor.sidebar = self.SidebarPartial; 467 api.selectiveRefresh.partialConstructor.widget = self.WidgetPartial; 468 469 /** 470 * Add partials for the registered widget areas (sidebars). 471 * 472 * @since 4.5.0 473 */ 474 self.addPartials = function() { 475 _.each( self.registeredSidebars, function( registeredSidebar ) { 476 var partial, partialId = 'sidebar[' + registeredSidebar.id + ']'; 477 partial = api.selectiveRefresh.partial( partialId ); 478 if ( ! partial ) { 479 partial = new self.SidebarPartial( partialId, { 480 params: { 481 sidebarArgs: registeredSidebar 482 } 483 } ); 484 api.selectiveRefresh.partial.add( partial.id, partial ); 485 } 486 } ); 487 }; 488 489 } 499 api.selectiveRefresh.partial.add( partial.id, partial ); 500 } 501 } ); 502 }; 490 503 491 504 /** -
trunk/src/wp-includes/js/customize-selective-refresh.js
r36892 r37040 110 110 var partial = this, selector; 111 111 112 selector = partial.params.selector ;112 selector = partial.params.selector || ''; 113 113 if ( selector ) { 114 114 selector += ', '; -
trunk/src/wp-includes/script-loader.php
r36984 r37040 456 456 457 457 $scripts->add( 'customize-widgets', "/wp-admin/js/customize-widgets$suffix.js", array( 'jquery', 'jquery-ui-sortable', 'jquery-ui-droppable', 'wp-backbone', 'customize-controls' ), false, 1 ); 458 $scripts->add( 'customize-preview-widgets', "/wp-includes/js/customize-preview-widgets$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 );458 $scripts->add( 'customize-preview-widgets', "/wp-includes/js/customize-preview-widgets$suffix.js", array( 'jquery', 'wp-util', 'customize-preview', 'customize-selective-refresh' ), false, 1 ); 459 459 460 460 $scripts->add( 'customize-nav-menus', "/wp-admin/js/customize-nav-menus$suffix.js", array( 'jquery', 'wp-backbone', 'customize-controls', 'accordion', 'nav-menu' ), false, 1 ); 461 $scripts->add( 'customize-preview-nav-menus', "/wp-includes/js/customize-preview-nav-menus$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 );461 $scripts->add( 'customize-preview-nav-menus', "/wp-includes/js/customize-preview-nav-menus$suffix.js", array( 'jquery', 'wp-util', 'customize-preview', 'customize-selective-refresh' ), false, 1 ); 462 462 463 463 $scripts->add( 'accordion', "/wp-admin/js/accordion$suffix.js", array( 'jquery' ), false, 1 ); -
trunk/src/wp-includes/theme.php
r36915 r37040 1915 1915 * The dynamic portion of the hook name, `$feature`, refers to the specific theme 1916 1916 * feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background', 1917 * 'custom-header', 'menus', 'automatic-feed-links', and 'html5'.1917 * 'custom-header', 'menus', 'automatic-feed-links', 'html5', and `customize-selective-refresh-widgets`. 1918 1918 * 1919 1919 * @since 3.4.0 -
trunk/src/wp-includes/widgets/class-wp-nav-menu-widget.php
r36622 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array( 'description' => __('Add a custom menu to your sidebar.') ); 26 $widget_ops = array( 27 'description' => __( 'Add a custom menu to your sidebar.' ), 28 'customize_selective_refresh' => true, 29 ); 27 30 parent::__construct( 'nav_menu', __('Custom Menu'), $widget_ops ); 28 31 } -
trunk/src/wp-includes/widgets/class-wp-widget-archives.php
r34620 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array('classname' => 'widget_archive', 'description' => __( 'A monthly archive of your site’s Posts.') ); 26 $widget_ops = array( 27 'classname' => 'widget_archive', 28 'description' => __( 'A monthly archive of your site’s Posts.' ), 29 'customize_selective_refresh' => true, 30 ); 27 31 parent::__construct('archives', __('Archives'), $widget_ops); 28 32 } -
trunk/src/wp-includes/widgets/class-wp-widget-calendar.php
r34619 r37040 34 34 */ 35 35 public function __construct() { 36 $widget_ops = array('classname' => 'widget_calendar', 'description' => __( 'A calendar of your site’s Posts.') ); 37 parent::__construct('calendar', __('Calendar'), $widget_ops); 36 $widget_ops = array( 37 'classname' => 'widget_calendar', 38 'description' => __( 'A calendar of your site’s Posts.' ), 39 'customize_selective_refresh' => true, 40 ); 41 parent::__construct( 'calendar', __( 'Calendar' ), $widget_ops ); 38 42 } 39 43 -
trunk/src/wp-includes/widgets/class-wp-widget-categories.php
r35278 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array( 'classname' => 'widget_categories', 'description' => __( "A list or dropdown of categories." ) ); 27 parent::__construct('categories', __('Categories'), $widget_ops); 26 $widget_ops = array( 27 'classname' => 'widget_categories', 28 'description' => __( 'A list or dropdown of categories.' ), 29 'customize_selective_refresh' => true, 30 ); 31 parent::__construct( 'categories', __( 'Categories' ), $widget_ops ); 28 32 } 29 33 -
trunk/src/wp-includes/widgets/class-wp-widget-links.php
r34617 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array('description' => __( "Your blogroll" ) ); 27 parent::__construct('links', __('Links'), $widget_ops); 26 $widget_ops = array( 27 'description' => __( 'Your blogroll' ), 28 'customize_selective_refresh' => true, 29 ); 30 parent::__construct( 'links', __( 'Links' ), $widget_ops ); 28 31 } 29 32 -
trunk/src/wp-includes/widgets/class-wp-widget-meta.php
r34616 r37040 26 26 */ 27 27 public function __construct() { 28 $widget_ops = array('classname' => 'widget_meta', 'description' => __( "Login, RSS, & WordPress.org links.") ); 29 parent::__construct('meta', __('Meta'), $widget_ops); 28 $widget_ops = array( 29 'classname' => 'widget_meta', 30 'description' => __( 'Login, RSS, & WordPress.org links.' ), 31 'customize_selective_refresh' => true, 32 ); 33 parent::__construct( 'meta', __( 'Meta' ), $widget_ops ); 30 34 } 31 35 -
trunk/src/wp-includes/widgets/class-wp-widget-pages.php
r34615 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array('classname' => 'widget_pages', 'description' => __( 'A list of your site’s Pages.') ); 27 parent::__construct('pages', __('Pages'), $widget_ops); 26 $widget_ops = array( 27 'classname' => 'widget_pages', 28 'description' => __( 'A list of your site’s Pages.' ), 29 'customize_selective_refresh' => true, 30 ); 31 parent::__construct( 'pages', __( 'Pages' ), $widget_ops ); 28 32 } 29 33 -
trunk/src/wp-includes/widgets/class-wp-widget-recent-comments.php
r34622 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array('classname' => 'widget_recent_comments', 'description' => __( 'Your site’s most recent comments.' ) ); 27 parent::__construct('recent-comments', __('Recent Comments'), $widget_ops); 26 $widget_ops = array( 27 'classname' => 'widget_recent_comments', 28 'description' => __( 'Your site’s most recent comments.' ), 29 'customize_selective_refresh' => true, 30 ); 31 parent::__construct( 'recent-comments', __( 'Recent Comments' ), $widget_ops ); 28 32 $this->alt_option_name = 'widget_recent_comments'; 29 33 30 if ( is_active_widget(false, false, $this->id_base) ) 31 add_action( 'wp_head', array($this, 'recent_comments_style') ); 34 if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) { 35 add_action( 'wp_head', array( $this, 'recent_comments_style' ) ); 36 } 32 37 } 33 38 -
trunk/src/wp-includes/widgets/class-wp-widget-recent-posts.php
r34613 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array('classname' => 'widget_recent_entries', 'description' => __( "Your site’s most recent Posts.") ); 27 parent::__construct('recent-posts', __('Recent Posts'), $widget_ops); 26 $widget_ops = array( 27 'classname' => 'widget_recent_entries', 28 'description' => __( 'Your site’s most recent Posts.' ), 29 'customize_selective_refresh' => true, 30 ); 31 parent::__construct( 'recent-posts', __( 'Recent Posts' ), $widget_ops ); 28 32 $this->alt_option_name = 'widget_recent_entries'; 29 33 } -
trunk/src/wp-includes/widgets/class-wp-widget-rss.php
r35978 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array( 'description' => __('Entries from any RSS or Atom feed.') ); 26 $widget_ops = array( 27 'description' => __( 'Entries from any RSS or Atom feed.' ), 28 'customize_selective_refresh' => true, 29 ); 27 30 $control_ops = array( 'width' => 400, 'height' => 200 ); 28 parent::__construct( 'rss', __( 'RSS'), $widget_ops, $control_ops );31 parent::__construct( 'rss', __( 'RSS' ), $widget_ops, $control_ops ); 29 32 } 30 33 -
trunk/src/wp-includes/widgets/class-wp-widget-search.php
r34611 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array('classname' => 'widget_search', 'description' => __( "A search form for your site.") ); 26 $widget_ops = array( 27 'classname' => 'widget_search', 28 'description' => __( 'A search form for your site.' ), 29 'customize_selective_refresh' => true, 30 ); 27 31 parent::__construct( 'search', _x( 'Search', 'Search widget' ), $widget_ops ); 28 32 } -
trunk/src/wp-includes/widgets/class-wp-widget-tag-cloud.php
r36622 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array( 'description' => __( "A cloud of your most used tags.") ); 27 parent::__construct('tag_cloud', __('Tag Cloud'), $widget_ops); 26 $widget_ops = array( 27 'description' => __( 'A cloud of your most used tags.' ), 28 'customize_selective_refresh' => true, 29 ); 30 parent::__construct( 'tag_cloud', __( 'Tag Cloud' ), $widget_ops ); 28 31 } 29 32 -
trunk/src/wp-includes/widgets/class-wp-widget-text.php
r36622 r37040 24 24 */ 25 25 public function __construct() { 26 $widget_ops = array('classname' => 'widget_text', 'description' => __('Arbitrary text or HTML.')); 27 $control_ops = array('width' => 400, 'height' => 350); 28 parent::__construct('text', __('Text'), $widget_ops, $control_ops); 26 $widget_ops = array( 27 'classname' => 'widget_text', 28 'description' => __( 'Arbitrary text or HTML.' ), 29 'customize_selective_refresh' => true, 30 ); 31 $control_ops = array( 'width' => 400, 'height' => 350 ); 32 parent::__construct( 'text', __( 'Text' ), $widget_ops, $control_ops ); 29 33 } 30 34 -
trunk/tests/phpunit/tests/customize/manager.php
r36586 r37040 426 426 $this->assertNotEmpty( $data ); 427 427 428 $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices' , 'selectiveRefreshEnabled'), array_keys( $data ) );428 $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices' ), array_keys( $data ) ); 429 429 $this->assertEquals( $autofocus, $data['autofocus'] ); 430 430 $this->assertArrayHasKey( 'save', $data['nonce'] ); -
trunk/tests/phpunit/tests/customize/widgets.php
r36611 r37040 25 25 require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' ); 26 26 27 add_theme_support( 'customize-selective-refresh-widgets' ); 27 28 $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); 28 29 wp_set_current_user( $user_id ); … … 48 49 } 49 50 51 function clean_up_global_scope() { 52 global $wp_widget_factory, $wp_registered_sidebars, $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates; 53 54 $wp_registered_sidebars = array(); 55 $wp_registered_widgets = array(); 56 $wp_registered_widget_controls = array(); 57 $wp_registered_widget_updates = array(); 58 $wp_widget_factory->widgets = array(); 59 60 parent::clean_up_global_scope(); 61 } 62 50 63 function tearDown() { 51 64 $this->manager = null; … … 76 89 $this->assertInstanceOf( 'WP_Customize_Widgets', $this->manager->widgets ); 77 90 $this->assertEquals( $this->manager, $this->manager->widgets->manager ); 91 } 92 93 /** 94 * Tests WP_Customize_Widgets::get_selective_refreshable_widgets(). 95 * 96 * @see WP_Customize_Widgets::get_selective_refreshable_widgets() 97 */ 98 function test_get_selective_refreshable_widgets_when_theme_supports() { 99 global $wp_widget_factory; 100 add_action( 'widgets_init', array( $this, 'override_search_widget_customize_selective_refresh' ), 90 ); 101 add_theme_support( 'customize-selective-refresh-widgets' ); 102 $this->do_customize_boot_actions(); 103 104 $selective_refreshable_widgets = $this->manager->widgets->get_selective_refreshable_widgets(); 105 $this->assertInternalType( 'array', $selective_refreshable_widgets ); 106 $this->assertEquals( count( $wp_widget_factory->widgets ), count( $selective_refreshable_widgets ) ); 107 $this->assertArrayHasKey( 'text', $selective_refreshable_widgets ); 108 $this->assertTrue( $selective_refreshable_widgets['text'] ); 109 $this->assertArrayHasKey( 'search', $selective_refreshable_widgets ); 110 $this->assertFalse( $selective_refreshable_widgets['search'] ); 111 } 112 113 /** 114 * Tests WP_Customize_Widgets::get_selective_refreshable_widgets(). 115 * 116 * @see WP_Customize_Widgets::get_selective_refreshable_widgets() 117 */ 118 function test_get_selective_refreshable_widgets_when_no_theme_supports() { 119 add_action( 'widgets_init', array( $this, 'override_search_widget_customize_selective_refresh' ), 90 ); 120 remove_theme_support( 'customize-selective-refresh-widgets' ); 121 $this->do_customize_boot_actions(); 122 $selective_refreshable_widgets = $this->manager->widgets->get_selective_refreshable_widgets(); 123 $this->assertEmpty( $selective_refreshable_widgets ); 124 } 125 126 /** 127 * Hook into widgets_init to override the search widget's customize_selective_refresh widget option. 128 * 129 * @see Tests_WP_Customize_Widgets::test_get_selective_refreshable_widgets_when_theme_supports() 130 * @see Tests_WP_Customize_Widgets::test_get_selective_refreshable_widgets_when_no_theme_supports() 131 */ 132 function override_search_widget_customize_selective_refresh() { 133 global $wp_widget_factory; 134 $wp_widget_factory->widgets['WP_Widget_Search']->widget_options['customize_selective_refresh'] = false; 135 } 136 137 /** 138 * Tests WP_Customize_Widgets::is_widget_selective_refreshable(). 139 * 140 * @see WP_Customize_Widgets::is_widget_selective_refreshable() 141 */ 142 function test_is_widget_selective_refreshable() { 143 add_action( 'widgets_init', array( $this, 'override_search_widget_customize_selective_refresh' ), 90 ); 144 add_theme_support( 'customize-selective-refresh-widgets' ); 145 $this->do_customize_boot_actions(); 146 $this->assertFalse( $this->manager->widgets->is_widget_selective_refreshable( 'search' ) ); 147 $this->assertTrue( $this->manager->widgets->is_widget_selective_refreshable( 'text' ) ); 148 remove_theme_support( 'customize-selective-refresh-widgets' ); 149 $this->assertFalse( $this->manager->widgets->is_widget_selective_refreshable( 'text' ) ); 78 150 } 79 151 … … 117 189 */ 118 190 function test_get_setting_args() { 191 add_theme_support( 'customize-selective-refresh-widgets' ); 192 $this->do_customize_boot_actions(); 119 193 120 194 add_filter( 'widget_customizer_setting_args', array( $this, 'filter_widget_customizer_setting_args' ), 10, 2 ); 195 196 $default_args = array( 197 'type' => 'option', 198 'capability' => 'edit_theme_options', 199 'transport' => 'refresh', 200 'default' => array(), 201 'sanitize_callback' => array( $this->manager->widgets, 'sanitize_widget_instance' ), 202 'sanitize_js_callback' => array( $this->manager->widgets, 'sanitize_widget_js_instance' ), 203 ); 204 $args = $this->manager->widgets->get_setting_args( 'widget_foo[2]' ); 205 foreach ( $default_args as $key => $default_value ) { 206 $this->assertEquals( $default_value, $args[ $key ] ); 207 } 208 $this->assertEquals( 'WIDGET_FOO[2]', $args['uppercase_id_set_by_filter'] ); 121 209 122 210 $default_args = array( … … 128 216 'sanitize_js_callback' => array( $this->manager->widgets, 'sanitize_widget_js_instance' ), 129 217 ); 130 131 $args = $this->manager->widgets->get_setting_args( 'widget_foo[2]' ); 218 $args = $this->manager->widgets->get_setting_args( 'widget_search[2]' ); 132 219 foreach ( $default_args as $key => $default_value ) { 133 220 $this->assertEquals( $default_value, $args[ $key ] ); 134 221 } 135 $this->assertEquals( 'WIDGET_FOO[2]', $args['uppercase_id_set_by_filter'] ); 222 223 remove_theme_support( 'customize-selective-refresh-widgets' ); 224 $args = $this->manager->widgets->get_setting_args( 'widget_search[2]' ); 225 $this->assertEquals( 'refresh', $args['transport'] ); 226 add_theme_support( 'customize-selective-refresh-widgets' ); 136 227 137 228 $override_args = array( … … 369 460 $this->assertTrue( $args['container_inclusive'] ); 370 461 $this->assertFalse( $args['fallback_refresh'] ); 462 463 remove_theme_support( 'customize-selective-refresh-widgets' ); 464 $args = apply_filters( 'customize_dynamic_partial_args', false, 'widget[search-2]' ); 465 $this->assertFalse( $args ); 371 466 } 372 467 … … 376 471 * @see WP_Customize_Widgets::selective_refresh_init() 377 472 */ 378 function test_selective_refresh_init() { 473 function test_selective_refresh_init_with_theme_support() { 474 add_theme_support( 'customize-selective-refresh-widgets' ); 379 475 $this->manager->widgets->selective_refresh_init(); 380 $this->assertEquals( 10, has_action( 'wp_enqueue_scripts', array( $this->manager->widgets, 'customize_preview_enqueue_deps' ) ) );381 476 $this->assertEquals( 10, has_action( 'dynamic_sidebar_before', array( $this->manager->widgets, 'start_dynamic_sidebar' ) ) ); 382 477 $this->assertEquals( 10, has_action( 'dynamic_sidebar_after', array( $this->manager->widgets, 'end_dynamic_sidebar' ) ) ); … … 386 481 387 482 /** 388 * Test WP_Customize_Widgets::customize_preview_enqueue_deps(). 389 * 390 * @see WP_Customize_Widgets::customize_preview_enqueue_deps() 391 */ 392 function test_customize_preview_enqueue_deps() { 393 $this->manager->widgets->customize_preview_enqueue_deps(); 483 * Test WP_Customize_Widgets::selective_refresh_init(). 484 * 485 * @see WP_Customize_Widgets::selective_refresh_init() 486 */ 487 function test_selective_refresh_init_without_theme_support() { 488 remove_theme_support( 'customize-selective-refresh-widgets' ); 489 $this->manager->widgets->selective_refresh_init(); 490 $this->assertFalse( has_action( 'dynamic_sidebar_before', array( $this->manager->widgets, 'start_dynamic_sidebar' ) ) ); 491 $this->assertFalse( has_action( 'dynamic_sidebar_after', array( $this->manager->widgets, 'end_dynamic_sidebar' ) ) ); 492 $this->assertFalse( has_filter( 'dynamic_sidebar_params', array( $this->manager->widgets, 'filter_dynamic_sidebar_params' ) ) ); 493 $this->assertFalse( has_filter( 'wp_kses_allowed_html', array( $this->manager->widgets, 'filter_wp_kses_allowed_data_attributes' ) ) ); 494 } 495 496 /** 497 * Test WP_Customize_Widgets::customize_preview_enqueue(). 498 * 499 * @see WP_Customize_Widgets::customize_preview_enqueue() 500 */ 501 function test_customize_preview_enqueue() { 502 $this->manager->widgets->customize_preview_enqueue(); 394 503 $this->assertTrue( wp_script_is( 'customize-preview-widgets', 'enqueued' ) ); 395 504 $this->assertTrue( wp_style_is( 'customize-preview', 'enqueued' ) ); … … 459 568 */ 460 569 function test_render_widget_partial() { 570 add_theme_support( 'customize-selective-refresh-widgets' ); 571 $this->do_customize_boot_actions(); 461 572 $this->manager->widgets->selective_refresh_init(); 462 573
Note: See TracChangeset
for help on using the changeset viewer.