Ticket #35855: 35855.2.diff
File 35855.2.diff, 71.7 KB (added by , 9 years ago) |
---|
-
src/wp-admin/js/customize-widgets.js
diff --git src/wp-admin/js/customize-widgets.js src/wp-admin/js/customize-widgets.js index 91a6516..6b27b8d 100644
34 34 multi_number: null, 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, 40 40 height: null, … … 1633 1633 * Update ordering of widget control forms when the setting is updated 1634 1634 */ 1635 1635 _setupModel: function() { 1636 var self = this; 1636 var sidebarControl = this, setWidgetSettingTransports; 1637 1638 setWidgetSettingTransports = function( widgetIds ) { 1639 _.each( widgetIds, function( widgetId ) { 1640 var widgetSetting = api( widgetIdToSettingId( widgetId ) ); 1641 if ( ! widgetSetting ) { 1642 return; 1643 } 1644 if ( api.Widgets.data.selectiveRefreshableWidgets[ parseWidgetId( widgetId ).id_base ] ) { 1645 widgetSetting.transport = sidebarControl.setting.transport; 1646 } else { 1647 widgetSetting.transport = 'refresh'; 1648 } 1649 } ); 1650 }; 1651 setWidgetSettingTransports( sidebarControl.setting() ); 1637 1652 1638 this.setting.bind( function( newWidgetIds, oldWidgetIds ) {1653 sidebarControl.setting.bind( function( newWidgetIds, oldWidgetIds ) { 1639 1654 var widgetFormControls, removedWidgetIds, priority; 1640 1655 1641 1656 removedWidgetIds = _( oldWidgetIds ).difference( newWidgetIds ); … … 1651 1666 var widgetFormControl = api.Widgets.getWidgetFormControlForWidget( widgetId ); 1652 1667 1653 1668 if ( ! widgetFormControl ) { 1654 widgetFormControl = s elf.addWidget( widgetId );1669 widgetFormControl = sidebarControl.addWidget( widgetId ); 1655 1670 } 1656 1671 1657 1672 return widgetFormControl; … … 1667 1682 priority = 0; 1668 1683 _( widgetFormControls ).each( function ( control ) { 1669 1684 control.priority( priority ); 1670 control.section( s elf.section() );1685 control.section( sidebarControl.section() ); 1671 1686 priority += 1; 1672 1687 }); 1673 s elf.priority( priority ); // Make sure sidebar control remains at end1688 sidebarControl.priority( priority ); // Make sure sidebar control remains at end 1674 1689 1675 1690 // Re-sort widget form controls (including widgets form other sidebars newly moved here) 1676 s elf._applyCardinalOrderClassNames();1691 sidebarControl._applyCardinalOrderClassNames(); 1677 1692 1678 1693 // If the widget was dragged into the sidebar, make sure the sidebar_id param is updated 1679 1694 _( widgetFormControls ).each( function( widgetFormControl ) { 1680 widgetFormControl.params.sidebar_id = s elf.params.sidebar_id;1695 widgetFormControl.params.sidebar_id = sidebarControl.params.sidebar_id; 1681 1696 } ); 1682 1697 1698 // Update added widget setting transports for selective refresh since this sidebar may not support it. 1699 setWidgetSettingTransports( newWidgetIds ); 1700 1683 1701 // Cleanup after widget removal 1684 1702 _( removedWidgetIds ).each( function( removedWidgetId ) { 1685 1703 … … 1690 1708 1691 1709 // Check if the widget is in another sidebar 1692 1710 api.each( function( otherSetting ) { 1693 if ( otherSetting.id === s elf.setting.id || 0 !== otherSetting.id.indexOf( 'sidebars_widgets[' ) || otherSetting.id === 'sidebars_widgets[wp_inactive_widgets]') {1711 if ( otherSetting.id === sidebarControl.setting.id || 0 !== otherSetting.id.indexOf( 'sidebars_widgets[' ) || 'sidebars_widgets[wp_inactive_widgets]' === otherSetting.id ) { 1694 1712 return; 1695 1713 } 1696 1714 … … 1710 1728 removedControl = api.Widgets.getWidgetFormControlForWidget( removedWidgetId ); 1711 1729 1712 1730 // Detect if widget control was dragged to another sidebar 1713 wasDraggedToAnotherSidebar = removedControl && $.contains( document, removedControl.container[0] ) && ! $.contains( s elf.$sectionContent[0], removedControl.container[0] );1731 wasDraggedToAnotherSidebar = removedControl && $.contains( document, removedControl.container[0] ) && ! $.contains( sidebarControl.$sectionContent[0], removedControl.container[0] ); 1714 1732 1715 1733 // Delete any widget form controls for removed widgets 1716 1734 if ( removedControl && ! wasDraggedToAnotherSidebar ) { … … 1923 1941 * @returns {object|false} widget_form control instance, or false on error 1924 1942 */ 1925 1943 addWidget: function( widgetId ) { 1926 var self= this, controlHtml, $widget, controlType = 'widget_form', controlContainer, controlConstructor,1944 var control = this, controlHtml, $widget, controlType = 'widget_form', controlContainer, controlConstructor, 1927 1945 parsedWidgetId = parseWidgetId( widgetId ), 1928 1946 widgetNumber = parsedWidgetId.number, 1929 1947 widgetIdBase = parsedWidgetId.id_base, 1930 1948 widget = api.Widgets.availableWidgets.findWhere( {id_base: widgetIdBase} ), 1931 settingId, isExistingWidget, widgetFormControl, sidebar Widgets, settingArgs, setting;1949 settingId, isExistingWidget, widgetFormControl, sidebar, sidebarWidgets, settingArgs, setting, settingTransport; 1932 1950 1933 1951 if ( ! widget ) { 1934 1952 return false; … … 1980 1998 1981 1999 // Only create setting if it doesn't already exist (if we're adding a pre-existing inactive widget) 1982 2000 isExistingWidget = api.has( settingId ); 2001 sidebar = api.Widgets.registeredSidebars.get( control.params.sidebar_id ); 2002 settingTransport = ( sidebar.get( 'customize_selective_refresh' ) && widget.get( 'customize_selective_refresh' ) ) ? 'postMessage' : 'refresh'; 1983 2003 if ( ! isExistingWidget ) { 1984 2004 settingArgs = { 1985 transport: api.Widgets.data.selectiveRefresh ? 'postMessage' : 'refresh',1986 previewer: this.setting.previewer2005 transport: settingTransport, 2006 previewer: control.setting.previewer 1987 2007 }; 1988 2008 setting = api.create( settingId, settingId, '', settingArgs ); 1989 2009 setting.set( {} ); // mark dirty, changing from '' to {} 2010 } else { 2011 setting = api( settingId ); 2012 setting.transport = settingTransport; 1990 2013 } 1991 2014 1992 2015 controlConstructor = api.controlConstructor[controlType]; … … 1996 2019 'default': settingId 1997 2020 }, 1998 2021 content: controlContainer, 1999 sidebar_id: self.params.sidebar_id,2022 sidebar_id: control.params.sidebar_id, 2000 2023 widget_id: widgetId, 2001 2024 widget_id_base: widget.get( 'id_base' ), 2002 2025 type: controlType, … … 2006 2029 is_wide: widget.get( 'is_wide' ), 2007 2030 active: true 2008 2031 }, 2009 previewer: self.setting.previewer2032 previewer: control.setting.previewer 2010 2033 } ); 2011 2034 api.control.add( settingId, widgetFormControl ); 2012 2035 2013 2036 // Make sure widget is removed from the other sidebars 2014 2037 api.each( function( otherSetting ) { 2015 if ( otherSetting.id === self.setting.id ) {2038 if ( otherSetting.id === control.setting.id ) { 2016 2039 return; 2017 2040 } 2018 2041 -
src/wp-content/themes/twentyeleven/functions.php
diff --git src/wp-content/themes/twentyeleven/functions.php src/wp-content/themes/twentyeleven/functions.php index 3ae1756..57f5b3c 100644
function twentyeleven_widgets_init() { 449 449 'after_widget' => '</aside>', 450 450 'before_title' => '<h3 class="widget-title">', 451 451 'after_title' => '</h3>', 452 'customize_selective_refresh' => true, 452 453 ) ); 453 454 454 455 register_sidebar( array( … … function twentyeleven_widgets_init() { 459 460 'after_widget' => '</aside>', 460 461 'before_title' => '<h3 class="widget-title">', 461 462 'after_title' => '</h3>', 463 'customize_selective_refresh' => true, 462 464 ) ); 463 465 464 466 register_sidebar( array( … … function twentyeleven_widgets_init() { 469 471 'after_widget' => '</aside>', 470 472 'before_title' => '<h3 class="widget-title">', 471 473 'after_title' => '</h3>', 474 'customize_selective_refresh' => true, 472 475 ) ); 473 476 474 477 register_sidebar( array( … … function twentyeleven_widgets_init() { 479 482 'after_widget' => '</aside>', 480 483 'before_title' => '<h3 class="widget-title">', 481 484 'after_title' => '</h3>', 485 'customize_selective_refresh' => true, 482 486 ) ); 483 487 484 488 register_sidebar( array( … … function twentyeleven_widgets_init() { 489 493 'after_widget' => '</aside>', 490 494 'before_title' => '<h3 class="widget-title">', 491 495 'after_title' => '</h3>', 496 'customize_selective_refresh' => true, 492 497 ) ); 493 498 } 494 499 add_action( 'widgets_init', 'twentyeleven_widgets_init' ); -
src/wp-content/themes/twentyfifteen/functions.php
diff --git src/wp-content/themes/twentyfifteen/functions.php src/wp-content/themes/twentyfifteen/functions.php index 7daeefa..aa88bf3 100644
function twentyfifteen_widgets_init() { 145 145 'after_widget' => '</aside>', 146 146 'before_title' => '<h2 class="widget-title">', 147 147 'after_title' => '</h2>', 148 'customize_selective_refresh' => true, 148 149 ) ); 149 150 } 150 151 add_action( 'widgets_init', 'twentyfifteen_widgets_init' ); -
src/wp-content/themes/twentyfourteen/functions.php
diff --git src/wp-content/themes/twentyfourteen/functions.php src/wp-content/themes/twentyfourteen/functions.php index 4e65214..5d09c6c 100644
function twentyfourteen_widgets_init() { 175 175 'after_widget' => '</aside>', 176 176 'before_title' => '<h1 class="widget-title">', 177 177 'after_title' => '</h1>', 178 'customize_selective_refresh' => true, 178 179 ) ); 179 180 register_sidebar( array( 180 181 'name' => __( 'Content Sidebar', 'twentyfourteen' ), … … function twentyfourteen_widgets_init() { 184 185 'after_widget' => '</aside>', 185 186 'before_title' => '<h1 class="widget-title">', 186 187 'after_title' => '</h1>', 188 'customize_selective_refresh' => true, 187 189 ) ); 188 190 register_sidebar( array( 189 191 'name' => __( 'Footer Widget Area', 'twentyfourteen' ), … … function twentyfourteen_widgets_init() { 193 195 'after_widget' => '</aside>', 194 196 'before_title' => '<h1 class="widget-title">', 195 197 'after_title' => '</h1>', 198 'customize_selective_refresh' => true, 196 199 ) ); 197 200 } 198 201 add_action( 'widgets_init', 'twentyfourteen_widgets_init' ); -
src/wp-content/themes/twentythirteen/functions.php
diff --git src/wp-content/themes/twentythirteen/functions.php src/wp-content/themes/twentythirteen/functions.php index 33c8efd..c4e9a78 100644
function twentythirteen_widgets_init() { 235 235 'after_widget' => '</aside>', 236 236 'before_title' => '<h3 class="widget-title">', 237 237 'after_title' => '</h3>', 238 'customize_selective_refresh' => true, 238 239 ) ); 239 240 240 241 register_sidebar( array( … … function twentythirteen_widgets_init() { 245 246 'after_widget' => '</aside>', 246 247 'before_title' => '<h3 class="widget-title">', 247 248 'after_title' => '</h3>', 249 'customize_selective_refresh' => true, 248 250 ) ); 249 251 } 250 252 add_action( 'widgets_init', 'twentythirteen_widgets_init' ); -
src/wp-content/themes/twentytwelve/functions.php
diff --git src/wp-content/themes/twentytwelve/functions.php src/wp-content/themes/twentytwelve/functions.php index e305447..76c53d5 100644
function twentytwelve_widgets_init() { 245 245 'after_widget' => '</aside>', 246 246 'before_title' => '<h3 class="widget-title">', 247 247 'after_title' => '</h3>', 248 'customize_selective_refresh' => true, 248 249 ) ); 249 250 250 251 register_sidebar( array( … … function twentytwelve_widgets_init() { 255 256 'after_widget' => '</aside>', 256 257 'before_title' => '<h3 class="widget-title">', 257 258 'after_title' => '</h3>', 259 'customize_selective_refresh' => true, 258 260 ) ); 259 261 260 262 register_sidebar( array( … … function twentytwelve_widgets_init() { 265 267 'after_widget' => '</aside>', 266 268 'before_title' => '<h3 class="widget-title">', 267 269 'after_title' => '</h3>', 270 'customize_selective_refresh' => true, 268 271 ) ); 269 272 } 270 273 add_action( 'widgets_init', 'twentytwelve_widgets_init' ); -
src/wp-includes/class-wp-customize-manager.php
diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php index ea30b93..9a13b6d 100644
final class WP_Customize_Manager { 109 109 * @access protected 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 /** 115 115 * Registered instances of WP_Customize_Section. … … final class WP_Customize_Manager { 258 258 */ 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' ); 263 266 $this->widgets = new WP_Customize_Widgets( $this ); … … final class WP_Customize_Manager { 268 271 $this->nav_menus = new WP_Customize_Nav_Menus( $this ); 269 272 } 270 273 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 }275 276 274 add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) ); 277 275 278 276 add_action( 'setup_theme', array( $this, 'setup_theme' ) ); … … final class WP_Customize_Manager { 1730 1728 'autofocus' => $this->get_autofocus(), 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 1736 1733 // Prepare Customize Section objects to pass to JavaScript. … … final class WP_Customize_Manager { 1978 1975 ), 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 */ 1991 1986 -
src/wp-includes/class-wp-customize-nav-menus.php
diff --git src/wp-includes/class-wp-customize-nav-menus.php src/wp-includes/class-wp-customize-nav-menus.php index cb8994c..7784567 100644
final class WP_Customize_Nav_Menus { 393 393 'reorderLabelOn' => esc_attr__( 'Reorder menu items' ), 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( 399 399 'nav_menu' => $temp_nav_menu_setting->default, … … final class WP_Customize_Nav_Menus { 445 445 if ( preg_match( WP_Customize_Nav_Menu_Setting::ID_PATTERN, $setting_id ) ) { 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 } 456 456 return $setting_args; … … final class WP_Customize_Nav_Menus { 535 535 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' ) ); 541 541 } else { … … final class WP_Customize_Nav_Menus { 543 543 'sanitize_callback' => 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 ) ); 549 549 } … … final class WP_Customize_Nav_Menus { 570 570 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 576 576 // Add the menu contents. … … final class WP_Customize_Nav_Menus { 585 585 $value['nav_menu_term_id'] = $menu_id; 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 591 591 // Create a control for each menu item. … … final class WP_Customize_Nav_Menus { 988 988 * @access public 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' ); 998 993 } -
src/wp-includes/class-wp-customize-widgets.php
diff --git src/wp-includes/class-wp-customize-widgets.php src/wp-includes/class-wp-customize-widgets.php index a417c46..cefc508 100644
final class WP_Customize_Widgets { 69 69 * @var array 70 70 */ 71 71 protected $setting_id_patterns = array( 72 'widget_instance' => '/^ (widget_.+?)(?:\[(\d+)\])?$/',73 'sidebar_widgets' => '/^sidebars_widgets\[( .+?)\]$/',72 'widget_instance' => '/^widget_(?P<id_base>.+?)(?:\[(?P<widget_number>\d+)\])?$/', 73 'sidebar_widgets' => '/^sidebars_widgets\[(?P<sidebar_id>.+?)\]$/', 74 74 ); 75 75 76 76 /** … … final class WP_Customize_Widgets { 112 112 } 113 113 114 114 /** 115 * Gets whether each registered widget can be use selective refresh. 116 * 117 * @since 4.5.0 118 * @access private 119 * 120 * @return array Mapping of id_base to support. If theme doesn't support 121 * selective refresh, an empty array is returned. 122 */ 123 protected function get_selective_refreshable_widgets() { 124 global $wp_widget_factory; 125 126 $selective_refreshable_widgets = array(); 127 foreach ( $wp_widget_factory->widgets as $wp_widget ) { 128 $selective_refreshable_widgets[ $wp_widget->id_base ] = ! empty( $wp_widget->widget_options['customize_selective_refresh'] ); 129 } 130 return $selective_refreshable_widgets; 131 } 132 133 /** 115 134 * Retrieves the widget setting type given a setting ID. 116 135 * 117 136 * @since 4.2.0 … … final class WP_Customize_Widgets { 119 138 * 120 139 * @staticvar array $cache 121 140 * 122 * @param $setting_id Setting ID.141 * @param string $setting_id Setting ID. 123 142 * @return string|void Setting type. 124 143 */ 125 144 protected function get_setting_type( $setting_id ) { … … final class WP_Customize_Widgets { 690 709 'widgetReorderNav' => $widget_reorder_nav_tpl, 691 710 'moveWidgetArea' => $move_widget_area_tpl, 692 711 ), 693 'selectiveRefresh ' => isset( $this->manager->selective_refresh),712 'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(), 694 713 ); 695 714 696 715 foreach ( $settings['registeredWidgets'] as &$registered_widget ) { … … final class WP_Customize_Widgets { 762 781 * 763 782 * @since 3.9.0 764 783 * @access public 784 * @global array $wp_registered_sidebars 765 785 * 766 786 * @param string $id Widget setting ID. 767 787 * @param array $overrides Array of setting overrides. 768 788 * @return array Possibly modified setting arguments. 769 789 */ 770 790 public function get_setting_args( $id, $overrides = array() ) { 791 global $wp_registered_sidebars; 771 792 $args = array( 772 793 'type' => 'option', 773 794 'capability' => 'edit_theme_options', 774 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',775 795 'default' => array(), 776 796 ); 777 797 778 798 if ( preg_match( $this->setting_id_patterns['sidebar_widgets'], $id, $matches ) ) { 779 799 $args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' ); 780 800 $args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' ); 801 $args['transport'] = ! empty( $wp_registered_sidebars[ $matches['sidebar_id'] ]['customize_selective_refresh'] ) ? 'postMessage' : 'refresh'; 781 802 } elseif ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) { 782 803 $args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' ); 783 804 $args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' ); 805 $args['transport'] = 'refresh'; // Will switch to postMessage in client if widget and sidebar support it. 784 806 } 785 807 786 808 $args = array_merge( $args, $overrides ); … … final class WP_Customize_Widgets { 845 867 usort( $sort, array( $this, '_sort_name_callback' ) ); 846 868 $done = array(); 847 869 870 $selective_refreshable_widgets = $this->get_selective_refreshable_widgets(); 871 848 872 foreach ( $sort as $widget ) { 849 873 if ( in_array( $widget['callback'], $done, true ) ) { // We already showed this multi-widget 850 874 continue; … … final class WP_Customize_Widgets { 893 917 'multi_number' => ( $args['_add'] === 'multi' ) ? $args['_multi_num'] : false, 894 918 'is_disabled' => $is_disabled, 895 919 'id_base' => $id_base, 896 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh',920 'transport' => ! empty( $selective_refreshable_widgets[ $id_base ] ), 897 921 'width' => $wp_registered_widget_controls[$widget['id']]['width'], 898 922 'height' => $wp_registered_widget_controls[$widget['id']]['height'], 899 923 'is_wide' => $this->is_wide_widget( $widget['id'] ), … … final class WP_Customize_Widgets { 1060 1084 */ 1061 1085 public function export_preview_data() { 1062 1086 global $wp_registered_sidebars, $wp_registered_widgets; 1087 1063 1088 // Prepare Customizer settings to pass to JavaScript. 1064 1089 $settings = array( 1065 1090 'renderedSidebars' => array_fill_keys( array_unique( $this->rendered_sidebars ), true ), … … final class WP_Customize_Widgets { 1069 1094 'l10n' => array( 1070 1095 'widgetTooltip' => __( 'Shift-click to edit this widget.' ), 1071 1096 ), 1072 'selectiveRefresh ' => isset( $this->manager->selective_refresh),1097 'selectiveRefreshableWidgets' => $this->get_selective_refreshable_widgets(), 1073 1098 ); 1074 1099 foreach ( $settings['registeredWidgets'] as &$registered_widget ) { 1075 1100 unset( $registered_widget['callback'] ); // may not be JSON-serializeable … … final class WP_Customize_Widgets { 1479 1504 * @return array (Maybe) modified partial arguments. 1480 1505 */ 1481 1506 public function customize_dynamic_partial_args( $partial_args, $partial_id ) { 1482 1483 1507 if ( preg_match( '/^widget\[(?P<widget_id>.+)\]$/', $partial_id, $matches ) ) { 1484 1508 if ( false === $partial_args ) { 1485 1509 $partial_args = array(); … … final class WP_Customize_Widgets { 1506 1530 * @access public 1507 1531 */ 1508 1532 public function selective_refresh_init() { 1509 if ( ! isset( $this->manager->selective_refresh ) ) {1510 return;1511 }1512 1513 1533 add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue_deps' ) ); 1514 1534 add_filter( 'dynamic_sidebar_params', array( $this, 'filter_dynamic_sidebar_params' ) ); 1515 1535 add_filter( 'wp_kses_allowed_html', array( $this, 'filter_wp_kses_allowed_data_attributes' ) ); … … final class WP_Customize_Widgets { 1524 1544 * @access public 1525 1545 */ 1526 1546 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 1547 wp_enqueue_script( 'customize-preview-widgets' ); 1533 1548 wp_enqueue_style( 'customize-preview' ); 1534 1549 } … … final class WP_Customize_Widgets { 1543 1558 * @type array $widget_args Widget args. 1544 1559 * } 1545 1560 * @see WP_Customize_Nav_Menus_Partial_Refresh::filter_wp_nav_menu_args() 1561 * @global array $wp_registered_sidebars 1546 1562 * 1547 1563 * @return array Params. 1548 1564 */ 1549 1565 public function filter_dynamic_sidebar_params( $params ) { 1566 global $wp_registered_sidebars; 1567 1550 1568 $sidebar_args = array_merge( 1551 1569 array( 1552 1570 'before_widget' => '', … … final class WP_Customize_Widgets { 1562 1580 && 1563 1581 is_registered_sidebar( $sidebar_args['id'] ) 1564 1582 && 1583 ! empty( $wp_registered_sidebars[ $sidebar_args['id'] ]['customize_selective_refresh'] ) 1584 && 1565 1585 ( isset( $this->current_dynamic_sidebar_id_stack[0] ) && $this->current_dynamic_sidebar_id_stack[0] === $sidebar_args['id'] ) 1566 1586 && 1567 1587 preg_match( '#^<(?P<tag_name>\w+)#', $sidebar_args['before_widget'], $matches ) -
src/wp-includes/class-wp-widget.php
diff --git src/wp-includes/class-wp-widget.php src/wp-includes/class-wp-widget.php index e8f4a1e..bb0de50 100644
class WP_Widget { 155 155 $this->id_base = empty($id_base) ? preg_replace( '/(wp_)?widget_/', '', strtolower(get_class($this)) ) : strtolower($id_base); 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 162 162 /** -
src/wp-includes/js/customize-preview-widgets.js
diff --git src/wp-includes/js/customize-preview-widgets.js src/wp-includes/js/customize-preview-widgets.js index 92e7732..ebee98e 100644
wp.customize.widgetsPreview = wp.customize.WidgetCustomizerPreview = (function( 12 12 preview: null, 13 13 l10n: { 14 14 widgetTooltip: '' 15 } 15 }, 16 selectiveRefreshableWidgets: {} 16 17 }; 17 18 18 19 /** … … wp.customize.widgetsPreview = wp.customize.WidgetCustomizerPreview = (function( 24 25 var self = this; 25 26 26 27 self.preview = api.preview; 27 if ( api.selectiveRefresh ) { 28 self.addPartials(); 29 } 30 28 self.addPartials(); 31 29 self.buildWidgetSelectors(); 32 30 self.highlightControls(); 33 31 … … wp.customize.widgetsPreview = wp.customize.WidgetCustomizerPreview = (function( 38 36 } ); 39 37 }; 40 38 41 if ( api.selectiveRefresh ) { 39 /** 40 * Partial representing a widget instance. 41 * 42 * @class 43 * @augments wp.customize.selectiveRefresh.Partial 44 * @since 4.5.0 45 */ 46 self.WidgetPartial = api.selectiveRefresh.Partial.extend({ 42 47 43 48 /** 44 * Partial representing a widget instance.49 * Constructor. 45 50 * 46 * @class47 * @augments wp.customize.selectiveRefresh.Partial48 51 * @since 4.5.0 52 * @param {string} id - Partial ID. 53 * @param {Object} options 54 * @param {Object} options.params 49 55 */ 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 } 56 initialize: function( id, options ) { 57 var partial = this, matches; 58 matches = id.match( /^widget\[(.+)]$/ ); 59 if ( ! matches ) { 60 throw new Error( 'Illegal id for widget partial.' ); 61 } 66 62 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 || {} 77 ); 63 partial.widgetId = matches[1]; 64 partial.widgetIdParts = self.parseWidgetId( partial.widgetId ); 65 options = options || {}; 66 options.params = _.extend( 67 { 68 settings: [ self.getWidgetSettingId( partial.widgetId ) ], 69 containerInclusive: true 70 }, 71 options.params || {} 72 ); 78 73 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 } 74 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options ); 75 }, 76 77 /** 78 * Refresh widget partial. 79 * 80 * @returns {Promise} 81 */ 82 refresh: function() { 83 var partial = this, refreshDeferred; 84 if ( ! self.selectiveRefreshableWidgets[ partial.widgetIdParts.idBase ] ) { 85 refreshDeferred = $.Deferred(); 86 refreshDeferred.reject(); 87 partial.fallback(); 88 return refreshDeferred.promise(); 89 } else { 90 return api.selectiveRefresh.Partial.prototype.refresh.call( partial ); 94 91 } 95 }); 92 }, 93 94 /** 95 * Send widget-updated message to parent so spinner will get removed from widget control. 96 * 97 * @inheritdoc 98 * @param {wp.customize.selectiveRefresh.Placement} placement 99 */ 100 renderContent: function( placement ) { 101 var partial = this; 102 if ( api.selectiveRefresh.Partial.prototype.renderContent.call( partial, placement ) ) { 103 api.preview.send( 'widget-updated', partial.widgetId ); 104 api.selectiveRefresh.trigger( 'widget-updated', partial ); 105 } 106 } 107 }); 108 109 /** 110 * Partial representing a widget area. 111 * 112 * @class 113 * @augments wp.customize.selectiveRefresh.Partial 114 * @since 4.5.0 115 */ 116 self.SidebarPartial = api.selectiveRefresh.Partial.extend({ 96 117 97 118 /** 98 * Partial representing a widget area.119 * Constructor. 99 120 * 100 * @class101 * @augments wp.customize.selectiveRefresh.Partial102 121 * @since 4.5.0 122 * @param {string} id - Partial ID. 123 * @param {Object} options 124 * @param {Object} options.params 103 125 */ 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 ); 126 initialize: function( id, options ) { 127 var partial = this, matches; 128 matches = id.match( /^sidebar\[(.+)]$/ ); 129 if ( ! matches ) { 130 throw new Error( 'Illegal id for sidebar partial.' ); 131 } 132 partial.sidebarId = matches[1]; 129 133 130 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options ); 134 options = options || {}; 135 options.params = _.extend( 136 { 137 settings: [ 'sidebars_widgets[' + partial.sidebarId + ']' ] 138 }, 139 options.params || {} 140 ); 131 141 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 } ); 142 api.selectiveRefresh.Partial.prototype.initialize.call( partial, id, options ); 152 143 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 ); 161 } 162 } ); 144 if ( ! partial.params.sidebarArgs ) { 145 throw new Error( 'The sidebarArgs param was not provided.' ); 146 } 147 if ( partial.params.settings.length > 1 ) { 148 throw new Error( 'Expected SidebarPartial to only have one associated setting' ); 149 } 150 }, 163 151 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; 170 } 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; 199 } 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 ); 214 } 215 } ); 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 238 } 239 } ); 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.' ); 152 /** 153 * Set up the partial. 154 * 155 * @since 4.5.0 156 */ 157 ready: function() { 158 var sidebarPartial = this; 159 160 // Watch for changes to the sidebar_widgets setting. 161 _.each( sidebarPartial.settings(), function( settingId ) { 162 api( settingId ).bind( _.bind( sidebarPartial.handleSettingChange, sidebarPartial ) ); 163 } ); 164 165 // Trigger an event for this sidebar being updated whenever a widget inside is rendered. 166 api.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) { 167 var isAssignedWidgetPartial = ( 168 placement.partial.extended( self.WidgetPartial ) && 169 ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), placement.partial.widgetId ) ) 170 ); 171 if ( isAssignedWidgetPartial ) { 172 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 173 } 174 } ); 175 176 // Make sure that a widget partial has a container in the DOM prior to a refresh. 177 api.bind( 'change', function( widgetSetting ) { 178 var widgetId, parsedId; 179 parsedId = self.parseWidgetSettingId( widgetSetting.id ); 180 if ( ! parsedId ) { 181 return; 255 182 } 256 if ( ! api.has( settingId ) ) { 257 throw new Error( 'Setting does not exist.' ); 183 widgetId = parsedId.idBase; 184 if ( parsedId.number ) { 185 widgetId += '-' + String( parsedId.number ); 258 186 } 259 widgetIds = api( settingId ).get(); 260 if ( ! _.isArray( widgetIds ) ) { 261 throw new Error( 'Expected setting to be array of widget IDs' ); 187 if ( -1 !== _.indexOf( sidebarPartial.getWidgetIds(), widgetId ) ) { 188 sidebarPartial.ensureWidgetPlacementContainers( widgetId ); 262 189 } 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; 190 } ); 191 }, 192 193 /** 194 * Get the before/after boundary nodes for all instances of this sidebar (usually one). 195 * 196 * Note that TreeWalker is not implemented in IE8. 197 * 198 * @since 4.5.0 199 * @returns {Array.<{before: Comment, after: Comment, instanceNumber: number}>} 200 */ 201 findDynamicSidebarBoundaryNodes: function() { 202 var partial = this, regExp, boundaryNodes = {}, recursiveCommentTraversal; 203 regExp = /^(dynamic_sidebar_before|dynamic_sidebar_after):(.+):(\d+)$/; 204 recursiveCommentTraversal = function( childNodes ) { 205 _.each( childNodes, function( node ) { 206 var matches; 207 if ( 8 === node.nodeType ) { 208 matches = node.nodeValue.match( regExp ); 209 if ( ! matches || matches[2] !== partial.sidebarId ) { 210 return; 211 } 212 if ( _.isUndefined( boundaryNodes[ matches[3] ] ) ) { 213 boundaryNodes[ matches[3] ] = { 214 before: null, 215 after: null, 216 instanceNumber: parseInt( matches[3], 10 ) 217 }; 218 } 219 if ( 'dynamic_sidebar_before' === matches[1] ) { 220 boundaryNodes[ matches[3] ].before = node; 221 } else { 222 boundaryNodes[ matches[3] ].after = node; 223 } 224 } else if ( 1 === node.nodeType ) { 225 recursiveCommentTraversal( node.childNodes ); 283 226 } 284 227 } ); 228 }; 285 229 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 ); 230 recursiveCommentTraversal( document.body.childNodes ); 231 return _.values( boundaryNodes ); 232 }, 314 233 315 // @todo Rename partial-placement-moved? 316 api.selectiveRefresh.trigger( 'partial-content-moved', sidebarWidget.placement ); 317 } ); 318 319 sortedSidebarContainers.push( sidebarPlacement ); 234 /** 235 * Get the placements for this partial. 236 * 237 * @since 4.5.0 238 * @returns {Array} 239 */ 240 placements: function() { 241 var partial = this; 242 return _.map( partial.findDynamicSidebarBoundaryNodes(), function( boundaryNodes ) { 243 return new api.selectiveRefresh.Placement( { 244 partial: partial, 245 container: null, 246 startNode: boundaryNodes.before, 247 endNode: boundaryNodes.after, 248 context: { 249 instanceNumber: boundaryNodes.instanceNumber 320 250 } 321 251 } ); 252 } ); 253 }, 322 254 323 if ( sortedSidebarContainers.length > 0 ) { 324 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 325 } 255 /** 256 * Get the list of widget IDs associated with this widget area. 257 * 258 * @since 4.5.0 259 * 260 * @returns {Array} 261 */ 262 getWidgetIds: function() { 263 var sidebarPartial = this, settingId, widgetIds; 264 settingId = sidebarPartial.settings()[0]; 265 if ( ! settingId ) { 266 throw new Error( 'Missing associated setting.' ); 267 } 268 if ( ! api.has( settingId ) ) { 269 throw new Error( 'Setting does not exist.' ); 270 } 271 widgetIds = api( settingId ).get(); 272 if ( ! _.isArray( widgetIds ) ) { 273 throw new Error( 'Expected setting to be array of widget IDs' ); 274 } 275 return widgetIds.slice( 0 ); 276 }, 326 277 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 ); 278 /** 279 * Reflow widgets in the sidebar, ensuring they have the proper position in the DOM. 280 * 281 * @since 4.5.0 282 * 283 * @return {Array.<wp.customize.selectiveRefresh.Placement>} List of placements that were reflowed. 284 */ 285 reflowWidgets: function() { 286 var sidebarPartial = this, sidebarPlacements, widgetIds, widgetPartials, sortedSidebarContainers = []; 287 widgetIds = sidebarPartial.getWidgetIds(); 288 sidebarPlacements = sidebarPartial.placements(); 289 290 widgetPartials = {}; 291 _.each( widgetIds, function( widgetId ) { 292 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + widgetId + ']' ); 293 if ( widgetPartial ) { 294 widgetPartials[ widgetId ] = widgetPartial; 346 295 } 296 } ); 347 297 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 ); 298 _.each( sidebarPlacements, function( sidebarPlacement ) { 299 var sidebarWidgets = [], needsSort = false, thisPosition, lastPosition = -1; 300 301 // Gather list of widget partial containers in this sidebar, and determine if a sort is needed. 302 _.each( widgetPartials, function( widgetPartial ) { 303 _.each( widgetPartial.placements(), function( widgetPlacement ) { 304 305 if ( sidebarPlacement.context.instanceNumber === widgetPlacement.context.sidebar_instance_number ) { 306 thisPosition = widgetPlacement.container.index(); 307 sidebarWidgets.push( { 308 partial: widgetPartial, 309 placement: widgetPlacement, 310 position: thisPosition 311 } ); 312 if ( thisPosition < lastPosition ) { 313 needsSort = true; 314 } 315 lastPosition = thisPosition; 316 } 354 317 } ); 355 if ( foundWidgetPlacement ) { 356 return; 357 } 318 } ); 358 319 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 } ); 320 if ( needsSort ) { 321 _.each( sidebarWidgets, function( sidebarWidget ) { 322 sidebarPlacement.endNode.parentNode.insertBefore( 323 sidebarWidget.placement.container[0], 324 sidebarPlacement.endNode 325 ); 378 326 379 sidebarPlacement.endNode.parentNode.insertBefore( widgetContainerElement[0], sidebarPlacement.endNode );380 wasInserted = true;381 } );327 // @todo Rename partial-placement-moved? 328 api.selectiveRefresh.trigger( 'partial-content-moved', sidebarWidget.placement ); 329 } ); 382 330 383 if ( wasInserted ) { 384 sidebarPartial.reflowWidgets(); 331 sortedSidebarContainers.push( sidebarPlacement ); 385 332 } 333 } ); 386 334 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 } 335 if ( sortedSidebarContainers.length > 0 ) { 336 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 337 } 409 338 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 } ); 339 return sortedSidebarContainers; 340 }, 426 341 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 ); 342 /** 343 * Make sure there is a widget instance container in this sidebar for the given widget ID. 344 * 345 * @since 4.5.0 346 * 347 * @param {string} widgetId 348 * @returns {wp.customize.selectiveRefresh.Partial} Widget instance partial. 349 */ 350 ensureWidgetPlacementContainers: function( widgetId ) { 351 var sidebarPartial = this, widgetPartial, wasInserted = false, partialId = 'widget[' + widgetId + ']'; 352 widgetPartial = api.selectiveRefresh.partial( partialId ); 353 if ( ! widgetPartial ) { 354 widgetPartial = new self.WidgetPartial( partialId, { 355 params: {} 432 356 } ); 357 api.selectiveRefresh.partial.add( widgetPartial.id, widgetPartial ); 358 } 433 359 434 _.each( addedWidgetPartials, function( widgetPartial ) { 435 widgetPartial.refresh(); 360 // Make sure that there is a container element for the widget in the sidebar, if at least a placeholder. 361 _.each( sidebarPartial.placements(), function( sidebarPlacement ) { 362 var foundWidgetPlacement, widgetContainerElement; 363 364 foundWidgetPlacement = _.find( widgetPartial.placements(), function( widgetPlacement ) { 365 return ( widgetPlacement.context.sidebar_instance_number === sidebarPlacement.context.instanceNumber ); 436 366 } ); 367 if ( foundWidgetPlacement ) { 368 return; 369 } 437 370 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(); 371 widgetContainerElement = $( 372 sidebarPartial.params.sidebarArgs.before_widget.replace( '%1$s', widgetId ).replace( '%2$s', 'widget' ) + 373 sidebarPartial.params.sidebarArgs.after_widget 374 ); 375 376 widgetContainerElement.attr( 'data-customize-partial-id', widgetPartial.id ); 377 widgetContainerElement.attr( 'data-customize-partial-type', 'widget' ); 378 widgetContainerElement.attr( 'data-customize-widget-id', widgetId ); 379 380 /* 381 * Make sure the widget container element has the customize-container context data. 382 * The sidebar_instance_number is used to disambiguate multiple instances of the 383 * same sidebar are rendered onto the template, and so the same widget is embedded 384 * multiple times. 385 */ 386 widgetContainerElement.data( 'customize-partial-placement-context', { 387 'sidebar_id': sidebarPartial.sidebarId, 388 'sidebar_instance_number': sidebarPlacement.context.instanceNumber 451 389 } ); 452 390 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 } 391 sidebarPlacement.endNode.parentNode.insertBefore( widgetContainerElement[0], sidebarPlacement.endNode ); 392 wasInserted = true; 393 } ); 461 394 462 return deferred.promise(); 395 if ( wasInserted ) { 396 sidebarPartial.reflowWidgets(); 463 397 } 464 });465 398 466 api.selectiveRefresh.partialConstructor.sidebar = self.SidebarPartial;467 api.selectiveRefresh.partialConstructor.widget = self.WidgetPartial;399 return widgetPartial; 400 }, 468 401 469 402 /** 470 * Add partials for the registered widget areas (sidebars).403 * Handle change to the sidebars_widgets[] setting. 471 404 * 472 405 * @since 4.5.0 406 * 407 * @param {Array} newWidgetIds New widget ids. 408 * @param {Array} oldWidgetIds Old widget ids. 473 409 */ 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 410 handleSettingChange: function( newWidgetIds, oldWidgetIds ) { 411 var sidebarPartial = this, needsRefresh, widgetsRemoved, widgetsAdded, addedWidgetPartials = []; 412 413 needsRefresh = ( 414 ( oldWidgetIds.length > 0 && 0 === newWidgetIds.length ) || 415 ( newWidgetIds.length > 0 && 0 === oldWidgetIds.length ) 416 ); 417 if ( needsRefresh ) { 418 sidebarPartial.fallback(); 419 return; 420 } 421 422 // Handle removal of widgets. 423 widgetsRemoved = _.difference( oldWidgetIds, newWidgetIds ); 424 _.each( widgetsRemoved, function( removedWidgetId ) { 425 var widgetPartial = api.selectiveRefresh.partial( 'widget[' + removedWidgetId + ']' ); 426 if ( widgetPartial ) { 427 _.each( widgetPartial.placements(), function( placement ) { 428 var isRemoved = ( 429 placement.context.sidebar_id === sidebarPartial.sidebarId || 430 ( placement.context.sidebar_args && placement.context.sidebar_args.id === sidebarPartial.sidebarId ) 431 ); 432 if ( isRemoved ) { 433 placement.container.remove(); 482 434 } 483 435 } ); 484 api.selectiveRefresh.partial.add( partial.id, partial );485 436 } 486 437 } ); 487 };488 438 489 } 439 // Handle insertion of widgets. 440 widgetsAdded = _.difference( newWidgetIds, oldWidgetIds ); 441 _.each( widgetsAdded, function( addedWidgetId ) { 442 var widgetPartial = sidebarPartial.ensureWidgetPlacementContainers( addedWidgetId ); 443 addedWidgetPartials.push( widgetPartial ); 444 } ); 445 446 _.each( addedWidgetPartials, function( widgetPartial ) { 447 widgetPartial.refresh(); 448 } ); 449 450 api.selectiveRefresh.trigger( 'sidebar-updated', sidebarPartial ); 451 }, 452 453 /** 454 * Note that the meat is handled in handleSettingChange because it has the context of which widgets were removed. 455 * 456 * @since 4.5.0 457 */ 458 refresh: function() { 459 var partial = this, deferred = $.Deferred(); 460 461 deferred.fail( function() { 462 partial.fallback(); 463 } ); 464 465 if ( 0 === partial.placements().length ) { 466 deferred.reject(); 467 } else { 468 _.each( partial.reflowWidgets(), function( sidebarPlacement ) { 469 api.selectiveRefresh.trigger( 'partial-content-rendered', sidebarPlacement ); 470 } ); 471 deferred.resolve(); 472 } 473 474 return deferred.promise(); 475 } 476 }); 477 478 api.selectiveRefresh.partialConstructor.sidebar = self.SidebarPartial; 479 api.selectiveRefresh.partialConstructor.widget = self.WidgetPartial; 480 481 /** 482 * Add partials for the registered widget areas (sidebars). 483 * 484 * @since 4.5.0 485 */ 486 self.addPartials = function() { 487 _.each( self.registeredSidebars, function( registeredSidebar ) { 488 var partial, partialId = 'sidebar[' + registeredSidebar.id + ']'; 489 partial = api.selectiveRefresh.partial( partialId ); 490 if ( ! partial ) { 491 partial = new self.SidebarPartial( partialId, { 492 params: { 493 sidebarArgs: registeredSidebar 494 } 495 } ); 496 api.selectiveRefresh.partial.add( partial.id, partial ); 497 } 498 } ); 499 }; 490 500 491 501 /** 492 502 * Calculate the selector for the sidebar's widgets based on the registered sidebar's info. -
src/wp-includes/js/customize-selective-refresh.js
diff --git src/wp-includes/js/customize-selective-refresh.js src/wp-includes/js/customize-selective-refresh.js index 8927716..7efee3d 100644
wp.customize.selectiveRefresh = ( function( $, api ) { 109 109 placements: function() { 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 += ', '; 115 115 } -
src/wp-includes/script-loader.php
diff --git src/wp-includes/script-loader.php src/wp-includes/script-loader.php index 9b4643f..63bbab2 100644
function wp_default_scripts( &$scripts ) { 455 455 $scripts->add( 'customize-selective-refresh', "/wp-includes/js/customize-selective-refresh$suffix.js", array( 'jquery', 'wp-util', 'customize-preview' ), false, 1 ); 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 ); 464 464 -
src/wp-includes/theme.php
diff --git src/wp-includes/theme.php src/wp-includes/theme.php index 15681e5..1ad720c 100644
function current_theme_supports( $feature ) { 1914 1914 * 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 1920 1920 * -
src/wp-includes/widgets/class-wp-nav-menu-widget.php
diff --git src/wp-includes/widgets/class-wp-nav-menu-widget.php src/wp-includes/widgets/class-wp-nav-menu-widget.php index d6ac26c..d465525 100644
23 23 * @access public 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 } 29 32 -
src/wp-includes/widgets/class-wp-widget-archives.php
diff --git src/wp-includes/widgets/class-wp-widget-archives.php src/wp-includes/widgets/class-wp-widget-archives.php index 30e4ee0..ba12572 100644
class WP_Widget_Archives extends WP_Widget { 23 23 * @access public 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 } 29 33 -
src/wp-includes/widgets/class-wp-widget-calendar.php
diff --git src/wp-includes/widgets/class-wp-widget-calendar.php src/wp-includes/widgets/class-wp-widget-calendar.php index 2a8911a..9969678 100644
class WP_Widget_Calendar extends WP_Widget { 33 33 * @access public 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 40 44 /** -
src/wp-includes/widgets/class-wp-widget-categories.php
diff --git src/wp-includes/widgets/class-wp-widget-categories.php src/wp-includes/widgets/class-wp-widget-categories.php index 058fd90..2fbc62a 100644
class WP_Widget_Categories extends WP_Widget { 23 23 * @access public 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 30 34 /** -
src/wp-includes/widgets/class-wp-widget-links.php
diff --git src/wp-includes/widgets/class-wp-widget-links.php src/wp-includes/widgets/class-wp-widget-links.php index 96ecc77..68d86f1 100644
class WP_Widget_Links extends WP_Widget { 23 23 * @access public 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 30 33 /** -
src/wp-includes/widgets/class-wp-widget-meta.php
diff --git src/wp-includes/widgets/class-wp-widget-meta.php src/wp-includes/widgets/class-wp-widget-meta.php index c12238f..2de9844 100644
class WP_Widget_Meta extends WP_Widget { 25 25 * @access public 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 32 36 /** -
src/wp-includes/widgets/class-wp-widget-pages.php
diff --git src/wp-includes/widgets/class-wp-widget-pages.php src/wp-includes/widgets/class-wp-widget-pages.php index e8737ac..165f334 100644
class WP_Widget_Pages extends WP_Widget { 23 23 * @access public 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 30 34 /** -
src/wp-includes/widgets/class-wp-widget-recent-comments.php
diff --git src/wp-includes/widgets/class-wp-widget-recent-comments.php src/wp-includes/widgets/class-wp-widget-recent-comments.php index 0f1a3b5..71fb4d2 100644
class WP_Widget_Recent_Comments extends WP_Widget { 23 23 * @access public 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 34 39 /** -
src/wp-includes/widgets/class-wp-widget-recent-posts.php
diff --git src/wp-includes/widgets/class-wp-widget-recent-posts.php src/wp-includes/widgets/class-wp-widget-recent-posts.php index 8f92bf3..fdb54a0 100644
class WP_Widget_Recent_Posts extends WP_Widget { 23 23 * @access public 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 } 30 34 -
src/wp-includes/widgets/class-wp-widget-rss.php
diff --git src/wp-includes/widgets/class-wp-widget-rss.php src/wp-includes/widgets/class-wp-widget-rss.php index b477a6e..a2548c4 100644
class WP_Widget_RSS extends WP_Widget { 23 23 * @access public 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 31 34 /** -
src/wp-includes/widgets/class-wp-widget-search.php
diff --git src/wp-includes/widgets/class-wp-widget-search.php src/wp-includes/widgets/class-wp-widget-search.php index 29bbbf8..f7d67bb 100644
class WP_Widget_Search extends WP_Widget { 23 23 * @access public 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 } 29 33 -
src/wp-includes/widgets/class-wp-widget-tag-cloud.php
diff --git src/wp-includes/widgets/class-wp-widget-tag-cloud.php src/wp-includes/widgets/class-wp-widget-tag-cloud.php index 4115c79..1251e1d 100644
class WP_Widget_Tag_Cloud extends WP_Widget { 23 23 * @access public 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 30 33 /** -
src/wp-includes/widgets/class-wp-widget-text.php
diff --git src/wp-includes/widgets/class-wp-widget-text.php src/wp-includes/widgets/class-wp-widget-text.php index 5a1a056..a8ec3d4 100644
class WP_Widget_Text extends WP_Widget { 23 23 * @access public 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 31 35 /** -
tests/phpunit/tests/customize/manager.php
diff --git tests/phpunit/tests/customize/manager.php tests/phpunit/tests/customize/manager.php index 6f5789d..0b86b4c 100644
class Tests_WP_Customize_Manager extends WP_UnitTestCase { 425 425 $data = json_decode( $json, true ); 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'] ); 431 431 $this->assertArrayHasKey( 'preview', $data['nonce'] ); -
tests/phpunit/tests/customize/widgets.php
diff --git tests/phpunit/tests/customize/widgets.php tests/phpunit/tests/customize/widgets.php index c485b36..0099e56 100644
class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 43 43 remove_action( 'after_setup_theme', 'twentyfifteen_setup' ); 44 44 remove_action( 'after_setup_theme', 'twentysixteen_setup' ); 45 45 remove_action( 'customize_register', 'twentysixteen_customize_register', 11 ); 46 add_theme_support( 'customize-selective-refresh-widgets' ); 46 47 47 48 $this->backup_registered_sidebars = $GLOBALS['wp_registered_sidebars']; 48 49 } … … class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 116 117 * Test WP_Customize_Widgets::get_setting_args() 117 118 */ 118 119 function test_get_setting_args() { 120 global $wp_registered_sidebars; 119 121 120 122 add_filter( 'widget_customizer_setting_args', array( $this, 'filter_widget_customizer_setting_args' ), 10, 2 ); 121 123 122 124 $default_args = array( 123 125 'type' => 'option', 124 126 'capability' => 'edit_theme_options', 125 'transport' => ' postMessage',127 'transport' => 'refresh', 126 128 'default' => array(), 127 129 'sanitize_callback' => array( $this->manager->widgets, 'sanitize_widget_instance' ), 128 130 'sanitize_js_callback' => array( $this->manager->widgets, 'sanitize_widget_js_instance' ), … … class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 148 150 } 149 151 $this->assertEquals( 'WIDGET_BAR[3]', $args['uppercase_id_set_by_filter'] ); 150 152 153 $wp_registered_sidebars['sidebar-1']['customize_selective_refresh'] = true; 151 154 $default_args = array( 152 155 'type' => 'option', 153 156 'capability' => 'edit_theme_options', … … class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 162 165 } 163 166 $this->assertEquals( 'SIDEBARS_WIDGETS[SIDEBAR-1]', $args['uppercase_id_set_by_filter'] ); 164 167 168 $wp_registered_sidebars['sidebar-2']['customize_selective_refresh'] = false; 165 169 $override_args = array( 166 170 'type' => 'theme_mod', 167 171 'capability' => 'edit_posts', 168 'transport' => ' postMessage',172 'transport' => 'refresh', 169 173 'default' => array( 'title' => 'asd' ), 170 174 'sanitize_callback' => '__return_empty_array', 171 175 'sanitize_js_callback' => '__return_empty_array', … … class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 408 412 global $wp_registered_sidebars; 409 413 register_sidebar( array( 410 414 'id' => 'foo', 415 'customize_selective_refresh' => true, 411 416 ) ); 412 417 413 418 $this->manager->widgets->selective_refresh_init();