From cb535f6b351ed2eb2d4632b86925490d95044d3c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 15 Nov 2013 01:24:53 -0800 Subject: [PATCH 01/22] Use postMessage as default transport for core widgets Allow transport to be filtered --- widget-customizer.php | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/widget-customizer.php b/widget-customizer.php index 0520c95..9f7b3fd 100644 --- a/widget-customizer.php +++ b/widget-customizer.php @@ -32,6 +32,20 @@ class Widget_Customizer { const UPDATE_WIDGET_AJAX_ACTION = 'update_widget'; const UPDATE_WIDGET_NONCE_POST_KEY = 'update-sidebar-widgets-nonce'; protected static $options_transaction; + protected static $core_widget_base_ids = array( + 'archives', + 'calendar', + 'categories', + 'meta', + 'nav_menu', + 'pages', + 'recent-comments', + 'recent-posts', + 'rss', + 'search', + 'tag_cloud', + 'text', + ); static function setup() { self::load_textdomain(); @@ -298,7 +312,10 @@ static function customize_register( $wp_customize ) { assert( isset( $GLOBALS['wp_registered_widgets'][$widget_id] ) ); $registered_widget = $GLOBALS['wp_registered_widgets'][$widget_id]; $setting_id = self::get_setting_id( $widget_id ); - $wp_customize->add_setting( $setting_id, self::get_setting_args( $setting_id ) ); + $setting_args = self::get_setting_args( $setting_id ); + $id_base = $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base']; + $setting_args['transport'] = self::get_widget_setting_transport( $id_base ); + $wp_customize->add_setting( $setting_id, $setting_args ); /** * Add control for widget if it is active @@ -313,7 +330,7 @@ static function customize_register( $wp_customize ) { 'section' => $section_id, 'sidebar_id' => $sidebar_id, 'widget_id' => $widget_id, - 'widget_id_base' => $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base'], + 'widget_id_base' => $id_base, 'priority' => 10 + $i, ) ); @@ -426,6 +443,20 @@ static function get_setting_args( $id, $overrides = array() ) { return $args; } + /** + * @param string $id_base + * @return string + */ + static function get_widget_setting_transport( $id_base ) { + $transport = 'refresh'; + if ( in_array( $id_base, self::$core_widget_base_ids ) ) { + $transport = 'postMessage'; + } + $transport = apply_filters( 'customizer_widget_transport', $transport, $id_base ); + $transport = apply_filters( "customizer_widget_transport_{$id_base}", $transport ); + return $transport; + } + /** * @see wp_list_widgets() * @return array @@ -487,7 +518,6 @@ static function get_available_widgets() { $list_widget_controls_args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) ); $control_tpl = self::get_widget_control( $list_widget_controls_args ); - $setting_args = self::get_setting_args( self::get_setting_id( $widget['id'] ) ); // The properties here are mapped to the Backbone Widget model $available_widget = array_merge( $available_widget, @@ -498,7 +528,7 @@ static function get_available_widgets() { 'multi_number' => ( $args['_add'] === 'multi' ) ? $args['_multi_num'] : false, 'is_disabled' => $is_disabled, 'id_base' => $id_base, - 'transport' => $setting_args['transport'], + 'transport' => self::get_widget_setting_transport( $id_base ), ) ); $available_widgets[] = $available_widget; From fdc8e78d6dfbbdd9c6cfcaca1c1de275c1976267 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 16 Nov 2013 00:19:00 -0800 Subject: [PATCH 02/22] Listen for postMessage updates to widget settings --- widget-customizer-preview.js | 24 +++++++++++++++++++++++- widget-customizer.php | 5 +++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index 80cf34f..15654d1 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -1,4 +1,4 @@ -/*global jQuery, WidgetCustomizerPreview_exports */ +/*global jQuery, WidgetCustomizerPreview_exports, _, console */ /*exported WidgetCustomizerPreview */ var WidgetCustomizerPreview = (function ($) { 'use strict'; @@ -13,6 +13,7 @@ var WidgetCustomizerPreview = (function ($) { this.buildWidgetSelectors(); this.toggleSections(); this.highlightControls(); + this.livePreview(); }, /** @@ -73,6 +74,27 @@ var WidgetCustomizerPreview = (function ($) { control.container.find(':input:visible:first').focus(); } }); + }, + + /** + * + */ + livePreview: function () { + $.each( self.initial_widget_setting_ids, function( i, setting_id ) { + wp.customize( setting_id, function( value ) { + var initial_value = value(); + var update_count = 0; + value.bind( function( to ) { + // Workaround for http://core.trac.wordpress.org/ticket/26061; + // once fixed, eliminate initial_value, update_count, and this conditional + update_count += 1; + if ( 1 === update_count && _.isEqual( initial_value, to ) ) { + return; + } + console.info( 'TODO: AJAX', setting_id, to ); + } ); + } ); + } ); } }; diff --git a/widget-customizer.php b/widget-customizer.php index 9f7b3fd..bf854e5 100644 --- a/widget-customizer.php +++ b/widget-customizer.php @@ -46,6 +46,7 @@ class Widget_Customizer { 'tag_cloud', 'text', ); + protected static $initial_widget_setting_ids = array(); static function setup() { self::load_textdomain(); @@ -316,6 +317,7 @@ static function customize_register( $wp_customize ) { $id_base = $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base']; $setting_args['transport'] = self::get_widget_setting_transport( $id_base ); $wp_customize->add_setting( $setting_id, $setting_args ); + self::$initial_widget_setting_ids[] = $setting_id; /** * Add control for widget if it is active @@ -612,6 +614,7 @@ static function customize_preview_enqueue_deps() { 'i18n' => array( 'widget_tooltip' => __( 'Edit widget in customizer...', 'widget-customizer' ), ), + 'initial_widget_setting_ids' => self::$initial_widget_setting_ids, ); $wp_scripts->add_data( 'widget-customizer-preview', @@ -624,7 +627,9 @@ static function customize_preview_enqueue_deps() { * At the very end of the page, at the very end of the wp_footer, communicate the sidebars that appeared on the page */ static function export_preview_data() { + global $wp_customize; wp_print_scripts( array( 'widget-customizer-preview' ) ); + ?> Date: Fri, 22 Nov 2013 00:28:42 -0800 Subject: [PATCH 08/22] Sort widgets with postMessage --- widget-customizer-preview.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index 79d601c..3ad38ab 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -191,6 +191,12 @@ var WidgetCustomizerPreview = (function ($) { return; } + // Sort widgets + $.each( to, function ( i, widget_id ) { + var widget = $( '#' + widget_id ); + widget.parent().append( widget ); + } ); + // Create settings for newly-created widgets $.each( to, function ( i, widget_id ) { var setting_id = widget_id_to_setting_id( widget_id ); @@ -215,8 +221,6 @@ var WidgetCustomizerPreview = (function ($) { $( '#' + old_widget_id ).remove(); } } ); - - // @todo Sort elements with IDs contained in the array `to` } ); } ); } ); From 458c111d16621be7404b40bcd89499421ca06829 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 25 Nov 2013 12:03:37 -0800 Subject: [PATCH 09/22] Enable postMessage updates based on opt-in availability Add 'widget-customizer' theme support feature --- widget-customizer-preview.js | 125 ++++++++++++++++++++++++++++++----- widget-customizer.js | 24 +++++-- widget-customizer.php | 46 +++++++++++-- 3 files changed, 170 insertions(+), 25 deletions(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index 3ad38ab..bf3603c 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -5,7 +5,9 @@ var WidgetCustomizerPreview = (function ($) { var self = { rendered_sidebars: [], - rendered_widgets: [], + sidebars_eligible_for_post_message: {}, + rendered_widgets: [], // @todo only used once; not really needed as we can just loop over sidebars_widgets + widgets_eligible_for_post_message: {}, registered_sidebars: {}, widget_selectors: [], render_widget_ajax_action: null, @@ -80,6 +82,53 @@ var WidgetCustomizerPreview = (function ($) { }); }, + /** + * if the containing sidebar is eligible, and if there are sibling widgets the sidebar currently rendered + * @param {String} sidebar_id + * @return {Boolean} + */ + sidebarCanLivePreview: function ( sidebar_id ) { + if ( ! self.current_theme_supports ) { + return false; + } + if ( ! self.sidebars_eligible_for_post_message[sidebar_id] ) { + return false; + } + var widget_ids = wp.customize( sidebar_id_to_setting_id( sidebar_id ) )(); + var rendered_widget_ids = _( widget_ids ).filter( function ( widget_id ) { + return 0 !== $( '#' + widget_id ).length; + } ); + if ( rendered_widget_ids.length === 0 ) { + return false; + } + return true; + }, + + + /** + * We can only know if a sidebar can be live-previewed by letting the preview tell us + * @param {String} sidebar_id + */ + refreshTransports: function () { + $.each( self.rendered_sidebars, function ( i, sidebar_id ) { + var setting_id = sidebar_id_to_setting_id( sidebar_id ); + var sidebar_transport = self.sidebarCanLivePreview( sidebar_id ) ? 'postMessage' : 'refresh'; + parent.wp.customize( setting_id ).transport = sidebar_transport; + + var widget_ids = wp.customize( setting_id )(); + $.each( widget_ids, function ( i, widget_id ){ + var setting_id = widget_id_to_setting_id( widget_id ); + var widget_transport = 'refresh'; + var id_base = widget_id_to_base( widget_id ); + if ( self.current_theme_supports && sidebar_transport === 'postMessage' && self.widgets_eligible_for_post_message[id_base] ) { + widget_transport = 'postMessage'; + } + parent.wp.customize( setting_id ).transport = widget_transport; + } ); + } ); + }, + + /** * */ @@ -100,16 +149,9 @@ var WidgetCustomizerPreview = (function ($) { return; } - var id_base; - var widget_number = null; - var matches = widget_id.match( /^(.+)-(\d+)$/ ); - if ( matches ) { - id_base = matches[1]; - widget_number = parseInt( matches[2], 10 ); - } - else { - // could be an old single widget, or adding a new widget - id_base = widget_id; + var widget_setting_id = widget_id_to_setting_id( widget_id ); + if ( parent.wp.customize( widget_setting_id ).transport !== 'postMessage' ) { + return; } var sidebar_id = null; @@ -133,7 +175,7 @@ var WidgetCustomizerPreview = (function ($) { instance: JSON.stringify( to ) }; var customized = {}; - customized['sidebars_widgets[' + sidebar_id + ']'] = sidebar_widgets; + customized[ sidebar_id_to_setting_id( sidebar_id ) ] = sidebar_widgets; customized[setting_id] = to; data.customized = JSON.stringify(customized); data[self.render_widget_nonce_post_key] = self.render_widget_nonce_value; @@ -154,7 +196,7 @@ var WidgetCustomizerPreview = (function ($) { old_widget.remove(); } else if ( new_widget.length && ! old_widget.length ) { - var sidebar_widgets = wp.customize('sidebars_widgets[' + r.data.sidebar_id + ']')(); + var sidebar_widgets = wp.customize( sidebar_id_to_setting_id( r.data.sidebar_id ) )(); var position = sidebar_widgets.indexOf( widget_id ); if ( -1 === position ) { throw new Error( 'Unable to determine new widget position in sidebar' ); @@ -171,6 +213,7 @@ var WidgetCustomizerPreview = (function ($) { after_widget.before( new_widget ); } } + self.refreshTransports(); } ); } ); }; @@ -179,7 +222,7 @@ var WidgetCustomizerPreview = (function ($) { }; $.each( self.rendered_sidebars, function ( i, sidebar_id ) { - var setting_id = 'sidebars_widgets[' + sidebar_id + ']'; + var setting_id = sidebar_id_to_setting_id( sidebar_id ); wp.customize( setting_id, function( value ) { var initial_value = value(); var update_count = 0; @@ -192,6 +235,7 @@ var WidgetCustomizerPreview = (function ($) { } // Sort widgets + // @todo instead of appending to the parent, we should append relative to the first widget found $.each( to, function ( i, widget_id ) { var widget = $( '#' + widget_id ); widget.parent().append( widget ); @@ -219,26 +263,40 @@ var WidgetCustomizerPreview = (function ($) { // @todo WARNING: If a widget is moved to another sidebar, we need to either not do this, or force a refresh when a widget is moved to another sidebar } $( '#' + old_widget_id ).remove(); + + // @todo If the last widget in a sidebar is remmoved, this should trigger a refresh; switch transport and then invoke a preview refresh } } ); + + // If a widget was removed so that no widgets remain rendered in sidebar, we need to disable postMessage + self.refreshTransports(); } ); } ); } ); + // @todo We don't really need rendered_widgets; we can just loop over all sidebars_widgets, and get all their widget_ids $.each( self.rendered_widgets, function ( widget_id ) { - if ( ! wp.customize.has( widget_id_to_setting_id( widget_id ) ) ) { + var setting_id = widget_id_to_setting_id( widget_id ); + if ( ! wp.customize.has( setting_id ) ) { // Used to have to do this: wp.customize.create( setting_id, instance ); // Now that the settings are registered at the `wp` action, it is late enough // for all filters to be added, e.g. sidebars_widgets for Widget Visibility - throw new Error( 'Expected customize to have registerd setting for widget ' + widget_id ); + throw new Error( 'Expected customize to have registered setting for widget ' + widget_id ); } bind_widget_setting( widget_id ); } ); + + // Opt-in to LivePreview + self.refreshTransports(); } }; $.extend(self, WidgetCustomizerPreview_exports); + /** + * @param {String} widget_id + * @returns {String} + */ function widget_id_to_setting_id( widget_id ) { var setting_id = null; var matches = widget_id.match(/^(.+?)(?:-(\d+)?)$/); @@ -251,6 +309,41 @@ var WidgetCustomizerPreview = (function ($) { return setting_id; } + /** + * @param {String} widget_id + * @returns {String} + */ + function widget_id_to_base( widget_id ) { + return widget_id.replace( /-\d+$/, '' ); + } + + /** + * @param {String} setting_id + * @returns {String|null} + */ + function setting_id_to_widget_id( setting_id ) { + var widget_id = null; + var matches = setting_id.match(/^widget_(.+?)(?:\[(\d+)\])$/); + if ( ! matches ) { + return null; + } + if ( matches[2] ) { + widget_id = matches[1] + '-' + matches[2]; + } + else { + widget_id = matches[1]; + } + return widget_id; + } + + /** + * @param {String} sidebar_id + * @returns {string} + */ + function sidebar_id_to_setting_id( sidebar_id ) { + return 'sidebars_widgets[' + sidebar_id + ']'; + } + $(function () { self.init(); }); diff --git a/widget-customizer.js b/widget-customizer.js index b4dda8b..3a8f616 100644 --- a/widget-customizer.js +++ b/widget-customizer.js @@ -9,7 +9,10 @@ var WidgetCustomizer = (function ($) { update_widget_nonce_value: null, update_widget_nonce_post_key: null, i18n: {}, - available_widgets: [] + available_widgets: [], + sidebars_eligible_for_post_message: {}, + widgets_eligible_for_post_message: {}, + current_theme_supports: false }; $.extend(self, WidgetCustomizer_exports); @@ -256,10 +259,16 @@ var WidgetCustomizer = (function ($) { // Only create setting if it doesn't already exist (if we're adding a pre-existing inactive widget) var is_existing_widget = wp.customize.has( setting_id ); if ( ! is_existing_widget ) { - wp.customize.create( setting_id, setting_id, {}, { - transport: widget.get( 'transport' ), + var setting_args = { + transport: 'refresh', // preview window will opt-in to postMessage if available previewer: control.setting.previewer - } ); + }; + var sidebar_can_live_preview = self.getPreviewWindow().WidgetCustomizerPreview.sidebarCanLivePreview( control.params.sidebar_id ); + var widget_can_live_preview = !! self.widgets_eligible_for_post_message[ widget_id_base ]; + if ( self.current_theme_supports && sidebar_can_live_preview && widget_can_live_preview ) { + setting_args.transport = 'postMessage'; + } + wp.customize.create( setting_id, setting_id, {}, setting_args ); } var Constructor = wp.customize.controlConstructor[customize_control_type]; @@ -662,5 +671,12 @@ var WidgetCustomizer = (function ($) { return found_control; }; + /** + * @returns {DOMWindow} + */ + self.getPreviewWindow = function (){ + return $( '#customize-preview' ).find( 'iframe' ).prop( 'contentWindow' ); + }; + return self; }( jQuery )); diff --git a/widget-customizer.php b/widget-customizer.php index a676ab7..9e3e2fa 100644 --- a/widget-customizer.php +++ b/widget-customizer.php @@ -39,6 +39,7 @@ class Widget_Customizer { 'archives', 'calendar', 'categories', + 'links', 'meta', 'nav_menu', 'pages', @@ -55,7 +56,6 @@ static function setup() { add_action( 'after_setup_theme', array( __CLASS__, 'setup_widget_addition_previews' ) ); add_action( 'customize_controls_init', array( __CLASS__, 'customize_controls_init' ) ); add_action( 'customize_register', array( __CLASS__, 'schedule_customize_register' ), 1 ); - add_action( 'wp_loaded', array( __CLASS__, 'remove_prepreview_filters' ) ); add_action( sprintf( 'wp_ajax_%s', self::UPDATE_WIDGET_AJAX_ACTION ), array( __CLASS__, 'wp_ajax_update_widget' ) ); add_filter( 'query_vars', array( __CLASS__, 'add_render_widget_query_var' ) ); add_action( 'template_redirect', array( __CLASS__, 'render_widget' ) ); @@ -299,6 +299,9 @@ static function schedule_customize_register( $wp_customize ) { } } + static $sidebars_eligible_for_post_message = array(); + static $widgets_eligible_for_post_message = array(); + /** * @action customize_register */ @@ -316,6 +319,8 @@ static function customize_register( $wp_customize = null ) { wp_get_sidebars_widgets() ); + $new_setting_ids = array(); + foreach ( $sidebars_widgets as $sidebar_id => $sidebar_widget_ids ) { if ( empty( $sidebar_widget_ids ) ) { $sidebar_widget_ids = array(); @@ -330,8 +335,14 @@ static function customize_register( $wp_customize = null ) { if ( $is_registered_sidebar || $is_inactive_widgets ) { $setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id ); $setting_args = self::get_setting_args( $setting_id ); - $setting_args['transport'] = self::get_sidebar_widgets_setting_transport( $sidebar_id ); + if ( $is_inactive_widgets ) { + $setting_args['transport'] = 'postMessage'; // prevent refresh since not rendered anyway + } + else { + self::$sidebars_eligible_for_post_message[$sidebar_id] = ( 'postMessage' === self::get_sidebar_widgets_setting_transport( $sidebar_id ) ); + } $wp_customize->add_setting( $setting_id, $setting_args ); + $new_setting_ids[] = $setting_id; /** * Add section to contain controls @@ -357,6 +368,7 @@ static function customize_register( $wp_customize = null ) { 'priority' => 10 - 1, ) ); + $new_setting_ids[] = $setting_id; $wp_customize->add_control( $control ); } } @@ -370,8 +382,9 @@ static function customize_register( $wp_customize = null ) { $setting_id = self::get_setting_id( $widget_id ); $setting_args = self::get_setting_args( $setting_id ); $id_base = $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base']; - $setting_args['transport'] = self::get_widget_setting_transport( $id_base ); + self::$widgets_eligible_for_post_message[$id_base] = ( 'postMessage' === self::get_widget_setting_transport( $id_base ) ); $wp_customize->add_setting( $setting_id, $setting_args ); + $new_setting_ids[] = $setting_id; /** * Add control for widget if it is active @@ -394,6 +407,19 @@ static function customize_register( $wp_customize = null ) { } } } + + /** + * We have to register these settings later than customize_preview_init so that other + * filters have had a chance to run. + * @see self::schedule_customize_register() + */ + if ( did_action( 'customize_preview_init' ) ) { + foreach ( $new_setting_ids as $new_setting_id ) { + $wp_customize->get_setting( $new_setting_id )->preview(); + } + } + + self::remove_prepreview_filters(); } /** @@ -451,6 +477,9 @@ static function customize_controls_enqueue_deps() { 'remove_btn_tooltip' => _x( 'Trash widget by moving it to the inactive widgets sidebar.', 'tooltip on btn a widget to move it to the inactive widgets sidebar', 'widget-customizer' ), ), 'available_widgets' => $available_widgets, + 'sidebars_eligible_for_post_message' => self::$sidebars_eligible_for_post_message, + 'widgets_eligible_for_post_message' => self::$widgets_eligible_for_post_message, + 'current_theme_supports' => current_theme_supports( 'widget-customizer' ), ); $wp_scripts->add_data( @@ -499,9 +528,10 @@ static function get_setting_args( $id, $overrides = array() ) { */ static function get_widget_setting_transport( $id_base ) { $transport = 'refresh'; - if ( in_array( $id_base, self::$core_widget_base_ids ) ) { + if ( current_theme_supports( 'widget-customizer' ) && in_array( $id_base, self::$core_widget_base_ids ) ) { $transport = 'postMessage'; } + // Allow widgets to opt-in for postMessage $transport = apply_filters( 'customizer_widget_transport', $transport, $id_base ); $transport = apply_filters( "customizer_widget_transport_{$id_base}", $transport ); return $transport; @@ -512,7 +542,10 @@ static function get_widget_setting_transport( $id_base ) { * @return string */ static function get_sidebar_widgets_setting_transport( $sidebar_id ) { - $transport = 'postMessage'; // @todo Needs to be theme opt-in + $transport = 'refresh'; + if ( current_theme_supports( 'widget-customizer' ) ) { + $transport = 'postMessage'; + } $transport = apply_filters( 'customizer_sidebar_widgets_transport', $transport, $sidebar_id ); $transport = apply_filters( "customizer_sidebar_widgets_transport_{$sidebar_id}", $transport ); return $transport; @@ -677,6 +710,9 @@ static function customize_preview_enqueue_deps() { 'render_widget_nonce_value' => wp_create_nonce( self::RENDER_WIDGET_AJAX_ACTION ), 'render_widget_nonce_post_key' => self::RENDER_WIDGET_NONCE_POST_KEY, 'request_uri' => wp_unslash( $_SERVER['REQUEST_URI'] ), + 'sidebars_eligible_for_post_message' => self::$sidebars_eligible_for_post_message, + 'widgets_eligible_for_post_message' => self::$widgets_eligible_for_post_message, + 'current_theme_supports' => current_theme_supports( 'widget-customizer' ), ); $wp_scripts->add_data( 'widget-customizer-preview', From 1632a2d0ee4e920c17a2a3c3cc8a32b066fb2b65 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2013 01:15:34 -0800 Subject: [PATCH 10/22] Fix addition of newly-added uninitialized widgets in preview Correct logic for obtaining list of postMessage-eligible widgets Fixes #61 --- widget-customizer-preview.js | 25 +++++++++++++++---------- widget-customizer.php | 19 ++++++++++++++++++- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index bf3603c..ebc59a8 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -139,13 +139,12 @@ var WidgetCustomizerPreview = (function ($) { var setting_id = widget_id_to_setting_id( widget_id ); var binder = function( value ) { already_bound_widgets[widget_id] = true; - var initial_value = value(); var update_count = 0; - value.bind( function( to ) { + value.bind( function( to, from ) { // Workaround for http://core.trac.wordpress.org/ticket/26061; // once fixed, eliminate initial_value, update_count, and this conditional update_count += 1; - if ( 1 === update_count && _.isEqual( initial_value, to ) ) { + if ( 1 === update_count && _.isEqual( from, to ) ) { return; } @@ -224,13 +223,12 @@ var WidgetCustomizerPreview = (function ($) { $.each( self.rendered_sidebars, function ( i, sidebar_id ) { var setting_id = sidebar_id_to_setting_id( sidebar_id ); wp.customize( setting_id, function( value ) { - var initial_value = value(); var update_count = 0; value.bind( function( to, from ) { // Workaround for http://core.trac.wordpress.org/ticket/26061; // once fixed, eliminate initial_value, update_count, and this conditional update_count += 1; - if ( 1 === update_count && _.isEqual( initial_value, to ) ) { + if ( 1 === update_count && _.isEqual( from, to ) ) { return; } @@ -244,14 +242,21 @@ var WidgetCustomizerPreview = (function ($) { // Create settings for newly-created widgets $.each( to, function ( i, widget_id ) { var setting_id = widget_id_to_setting_id( widget_id ); - if ( ! wp.customize( setting_id ) ) { - wp.customize.create( setting_id, {} ); + var setting = wp.customize( setting_id ); + if ( ! setting ) { + setting = wp.customize.create( setting_id, {} ); } + // @todo Is there another way to check if we bound? - if ( already_bound_widgets[widget_id] ) { - return; + if ( ! already_bound_widgets[widget_id] ) { + bind_widget_setting( widget_id ); + } + + // Force the callback to fire if this widget is newly-added + if ( from.indexOf( widget_id ) === -1 ) { + self.refreshTransports(); + setting.callbacks.fireWith( setting, [ setting(), null ] ); } - bind_widget_setting( widget_id ); } ); // Remove widgets (their DOM element and their setting) when removed from sidebar diff --git a/widget-customizer.php b/widget-customizer.php index 9e3e2fa..8d56c7d 100644 --- a/widget-customizer.php +++ b/widget-customizer.php @@ -306,6 +306,7 @@ static function schedule_customize_register( $wp_customize ) { * @action customize_register */ static function customize_register( $wp_customize = null ) { + global $wp_registered_widgets; if ( ! ( $wp_customize instanceof WP_Customize_Manager ) ) { $wp_customize = $GLOBALS['wp_customize']; } @@ -382,7 +383,6 @@ static function customize_register( $wp_customize = null ) { $setting_id = self::get_setting_id( $widget_id ); $setting_args = self::get_setting_args( $setting_id ); $id_base = $GLOBALS['wp_registered_widget_controls'][$widget_id]['id_base']; - self::$widgets_eligible_for_post_message[$id_base] = ( 'postMessage' === self::get_widget_setting_transport( $id_base ) ); $wp_customize->add_setting( $setting_id, $setting_args ); $new_setting_ids[] = $setting_id; @@ -461,6 +461,7 @@ static function customize_controls_enqueue_deps() { foreach ( self::get_available_widgets() as $available_widget ) { unset( $available_widget['control_tpl'] ); $available_widgets[] = $available_widget; + self::$widgets_eligible_for_post_message[$available_widget['id_base']] = ( 'postMessage' === self::get_widget_setting_transport( $available_widget['id_base'] ) ); } // Why not wp_localize_script? Because we're not localizing, and it forces values into strings @@ -685,6 +686,8 @@ static function preview_sidebars_widgets( $sidebars_widgets ) { * @action wp_enqueue_scripts */ static function customize_preview_enqueue_deps() { + global $wp_registered_widgets, $wp_registered_widget_controls; + wp_enqueue_script( 'widget-customizer-preview', self::get_plugin_path_url( 'widget-customizer-preview.js' ), @@ -699,6 +702,20 @@ static function customize_preview_enqueue_deps() { self::get_version() ); + $all_id_bases = array(); + foreach ( $wp_registered_widgets as $widget ) { + if ( isset( $wp_registered_widget_controls[$widget['id']]['id_base'] ) ) { + $all_id_bases[] = $wp_registered_widget_controls[$widget['id']]['id_base']; + } + else { + $all_id_bases[] = $widget['id']; + } + } + $all_id_bases = array_unique( $all_id_bases ); + foreach ( $all_id_bases as $id_base ) { + self::$widgets_eligible_for_post_message[$id_base] = ( 'postMessage' === self::get_widget_setting_transport( $id_base ) ); + } + // Why not wp_localize_script? Because we're not localizing, and it forces values into strings global $wp_scripts; $exports = array( From fe166caa068c9b9a152ae405513ad0299c7f98b7 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 7 Dec 2013 02:09:59 -0800 Subject: [PATCH 11/22] Capture preview and previewer, allow preview to trigger refresh --- widget-customizer-preview.js | 14 ++++++++++++++ widget-customizer.js | 20 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index ebc59a8..cc45113 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -13,6 +13,7 @@ var WidgetCustomizerPreview = (function ($) { render_widget_ajax_action: null, render_widget_nonce_value: null, render_widget_nonce_post_key: null, + preview: null, i18n: {}, init: function () { @@ -193,6 +194,7 @@ var WidgetCustomizerPreview = (function ($) { } else if ( ! new_widget.length && old_widget.length ) { old_widget.remove(); + // @todo if no more widgets are loaded in this sidebar, refresh preview } else if ( new_widget.length && ! old_widget.length ) { var sidebar_widgets = wp.customize( sidebar_id_to_setting_id( r.data.sidebar_id ) )(); @@ -266,6 +268,7 @@ var WidgetCustomizerPreview = (function ($) { if ( wp.customize.has( setting_id ) ) { wp.customize.remove( setting_id ); // @todo WARNING: If a widget is moved to another sidebar, we need to either not do this, or force a refresh when a widget is moved to another sidebar + // @todo if no more widgets are loaded in this sidebar, refresh preview } $( '#' + old_widget_id ).remove(); @@ -298,6 +301,17 @@ var WidgetCustomizerPreview = (function ($) { $.extend(self, WidgetCustomizerPreview_exports); + /** + * Capture the instance of the Preview since it is private + */ + var OldPreview = wp.customize.Preview; + wp.customize.Preview = OldPreview.extend( { + initialize: function( params, options ) { + self.preview = this; + OldPreview.prototype.initialize.call( this, params, options ); + } + } ); + /** * @param {String} widget_id * @returns {String} diff --git a/widget-customizer.js b/widget-customizer.js index 3a8f616..63dc83e 100644 --- a/widget-customizer.js +++ b/widget-customizer.js @@ -12,7 +12,8 @@ var WidgetCustomizer = (function ($) { available_widgets: [], sidebars_eligible_for_post_message: {}, widgets_eligible_for_post_message: {}, - current_theme_supports: false + current_theme_supports: false, + previewer: null }; $.extend(self, WidgetCustomizer_exports); @@ -111,8 +112,9 @@ var WidgetCustomizer = (function ($) { return; } - // Detect if widget dragged to another sidebar and abort + // Detect if widget control was dragged to another sidebar and abort if ( ! $.contains( control.section_content[0], removed_control.container[0] ) ) { + // @todo Trigger refresh? return; } @@ -130,6 +132,8 @@ var WidgetCustomizer = (function ($) { widget.set( 'is_disabled', false ); } } ); + + // @todo if there was a widget in a sidebar, but it was just removed, we need to refresh the preview window }); }, @@ -639,6 +643,18 @@ var WidgetCustomizer = (function ($) { } }); + /** + * Capture the instance of the Previewer since it is private + */ + var OldPreviewer = wp.customize.Previewer; + wp.customize.Previewer = OldPreviewer.extend( { + initialize: function( params, options ) { + self.previewer = this; + OldPreviewer.prototype.initialize.call( this, params, options ); + this.bind( 'refresh', this.refresh ); + } + } ); + /** * Given a widget control, find the sidebar widgets control that contains it. * @param {string} widget_id From e05b9a931c9ffda4a6bc1a33d61e1e4c328f9fff Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 7 Dec 2013 16:09:28 -0800 Subject: [PATCH 12/22] Trigger refresh of preview when last widget removed from sidebar --- widget-customizer-preview.js | 26 ++++++++++++++++++-------- widget-customizer.js | 3 --- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index cc45113..30f2367 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -107,29 +107,43 @@ var WidgetCustomizerPreview = (function ($) { /** - * We can only know if a sidebar can be live-previewed by letting the preview tell us + * We can only know if a sidebar can be live-previewed by letting the + * preview tell us, so this updates the parent's transports to + * postMessage when it is available. If there is a switch from + * postMessage to refresh, the preview window will request a refresh. * @param {String} sidebar_id */ refreshTransports: function () { + var changed_to_refresh = false; $.each( self.rendered_sidebars, function ( i, sidebar_id ) { var setting_id = sidebar_id_to_setting_id( sidebar_id ); + var setting = parent.wp.customize( setting_id ); var sidebar_transport = self.sidebarCanLivePreview( sidebar_id ) ? 'postMessage' : 'refresh'; - parent.wp.customize( setting_id ).transport = sidebar_transport; + if ( 'refresh' === sidebar_transport && 'postMessage' === setting.transport ) { + changed_to_refresh = true; + } + setting.transport = sidebar_transport; var widget_ids = wp.customize( setting_id )(); $.each( widget_ids, function ( i, widget_id ){ var setting_id = widget_id_to_setting_id( widget_id ); + var setting = parent.wp.customize( setting_id ); var widget_transport = 'refresh'; var id_base = widget_id_to_base( widget_id ); if ( self.current_theme_supports && sidebar_transport === 'postMessage' && self.widgets_eligible_for_post_message[id_base] ) { widget_transport = 'postMessage'; } - parent.wp.customize( setting_id ).transport = widget_transport; + if ( 'refresh' === widget_transport && 'postMessage' === setting.transport ) { + changed_to_refresh = true; + } + setting.transport = widget_transport; } ); } ); + if ( changed_to_refresh ) { + self.preview.send( 'refresh' ); + } }, - /** * */ @@ -194,7 +208,6 @@ var WidgetCustomizerPreview = (function ($) { } else if ( ! new_widget.length && old_widget.length ) { old_widget.remove(); - // @todo if no more widgets are loaded in this sidebar, refresh preview } else if ( new_widget.length && ! old_widget.length ) { var sidebar_widgets = wp.customize( sidebar_id_to_setting_id( r.data.sidebar_id ) )(); @@ -268,11 +281,8 @@ var WidgetCustomizerPreview = (function ($) { if ( wp.customize.has( setting_id ) ) { wp.customize.remove( setting_id ); // @todo WARNING: If a widget is moved to another sidebar, we need to either not do this, or force a refresh when a widget is moved to another sidebar - // @todo if no more widgets are loaded in this sidebar, refresh preview } $( '#' + old_widget_id ).remove(); - - // @todo If the last widget in a sidebar is remmoved, this should trigger a refresh; switch transport and then invoke a preview refresh } } ); diff --git a/widget-customizer.js b/widget-customizer.js index 63dc83e..7be4f01 100644 --- a/widget-customizer.js +++ b/widget-customizer.js @@ -114,7 +114,6 @@ var WidgetCustomizer = (function ($) { // Detect if widget control was dragged to another sidebar and abort if ( ! $.contains( control.section_content[0], removed_control.container[0] ) ) { - // @todo Trigger refresh? return; } @@ -132,8 +131,6 @@ var WidgetCustomizer = (function ($) { widget.set( 'is_disabled', false ); } } ); - - // @todo if there was a widget in a sidebar, but it was just removed, we need to refresh the preview window }); }, From 4bbfe9ad864624167ebe87d6d82170d92ed6262d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 7 Dec 2013 16:09:52 -0800 Subject: [PATCH 13/22] Remove unused JS function setting_id_to_widget_id() --- widget-customizer-preview.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index 30f2367..b98010f 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -346,25 +346,6 @@ var WidgetCustomizerPreview = (function ($) { return widget_id.replace( /-\d+$/, '' ); } - /** - * @param {String} setting_id - * @returns {String|null} - */ - function setting_id_to_widget_id( setting_id ) { - var widget_id = null; - var matches = setting_id.match(/^widget_(.+?)(?:\[(\d+)\])$/); - if ( ! matches ) { - return null; - } - if ( matches[2] ) { - widget_id = matches[1] + '-' + matches[2]; - } - else { - widget_id = matches[1]; - } - return widget_id; - } - /** * @param {String} sidebar_id * @returns {string} From 5ff9ea3f27b30b4214470afdc69525c7b18895e3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 7 Dec 2013 23:37:58 -0800 Subject: [PATCH 14/22] Show spinner when updating widgets via postMessage --- widget-customizer-preview.js | 2 +- widget-customizer.js | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index b98010f..e269dde 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -199,7 +199,6 @@ var WidgetCustomizerPreview = (function ($) { throw new Error( r.data && r.data.message ? r.data.message : 'FAIL' ); } - // @todo We should tell the preview that synced has happened after the Ajax finishes // @todo Fire jQuery event to indicate that a widget was updated; here widgets can re-initialize them if they support live widgets var old_widget = $( '#' + widget_id ); var new_widget = $( r.data.rendered_widget ); @@ -227,6 +226,7 @@ var WidgetCustomizerPreview = (function ($) { after_widget.before( new_widget ); } } + self.preview.send( 'widget-updated', widget_id ); self.refreshTransports(); } ); } ); diff --git a/widget-customizer.js b/widget-customizer.js index 7be4f01..6b2727e 100644 --- a/widget-customizer.js +++ b/widget-customizer.js @@ -412,10 +412,14 @@ var WidgetCustomizer = (function ($) { } }); - // @todo For transport=postMessage, the JS in the preview should send back an event so that this class can still be added control.setting.previewer.channel.bind( 'synced', function () { control.container.removeClass( 'previewer-loading' ); }); + self.previewer.bind( 'widget-updated', function ( updated_widget_id ) { + if ( updated_widget_id === control.params.widget_id ) { + control.container.removeClass( 'previewer-loading' ); + } + } ); control.setupControlToggle(); control.setupWidgetTitle(); @@ -431,10 +435,7 @@ var WidgetCustomizer = (function ($) { var data = control.container.find(':input').serialize(); control.container.addClass( 'widget-form-loading' ); - if ( 'refresh' === control.setting.transport ) { - // @todo The JS in the preview should send back an event so that this class can still be added - control.container.addClass( 'previewer-loading' ); - } + control.container.addClass( 'previewer-loading' ); control.container.find( '.widget-content' ).prop( 'disabled', true ); var params = {}; From 9e551158af00fbcba329f7afec405599ba103ad8 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 7 Dec 2013 23:49:06 -0800 Subject: [PATCH 15/22] Fix widget sidebar reassignment postMessage enabled --- widget-customizer.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/widget-customizer.js b/widget-customizer.js index 6b2727e..9f3c06e 100644 --- a/widget-customizer.js +++ b/widget-customizer.js @@ -100,8 +100,13 @@ var WidgetCustomizer = (function ($) { // Re-sort widget form controls control.section_content.append( final_control_containers ); + var must_refresh_preview = false; + // If the widget was dragged into the sidebar, make sure the sidebar_id param is updated _( widget_form_controls ).each( function ( widget_form_control ) { + if ( widget_form_control.params.sidebar_id !== control.params.sidebar_id ) { + must_refresh_preview = true; + } widget_form_control.params.sidebar_id = control.params.sidebar_id; } ); @@ -131,6 +136,10 @@ var WidgetCustomizer = (function ($) { widget.set( 'is_disabled', false ); } } ); + + if ( must_refresh_preview ) { + self.previewer.refresh(); + } }); }, From d8091527004f6e864cb8e876d3cd8148d0e36ea0 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 8 Dec 2013 00:36:21 -0800 Subject: [PATCH 16/22] Add live preview theme support for bundled themes Especially for twentythirteen which uses Masonry to lay out widgets in a sidebar. Other themes can hook into wp.customize.bind( 'sidebar-updated', function ( sidebar_id ) { ... } ) --- theme-support/twentythirteen.js | 14 ++++++++++++ widget-customizer-preview.js | 3 +++ widget-customizer.php | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 theme-support/twentythirteen.js diff --git a/theme-support/twentythirteen.js b/theme-support/twentythirteen.js new file mode 100644 index 0000000..7346166 --- /dev/null +++ b/theme-support/twentythirteen.js @@ -0,0 +1,14 @@ +/*global jQuery, wp */ +jQuery( function ($) { + wp.customize.bind( 'sidebar-updated', function ( sidebar_id ) { + if ( 'sidebar-1' !== sidebar_id ) { + return; + } + + if ( $.isFunction( $.fn.masonry ) ) { + var widget_area = $( '#secondary .widget-area' ); + widget_area.masonry( 'reloadItems' ); + widget_area.masonry(); + } + } ); +} ); diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index e269dde..167c986 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -227,6 +227,8 @@ var WidgetCustomizerPreview = (function ($) { } } self.preview.send( 'widget-updated', widget_id ); + wp.customize.trigger( 'sidebar-updated', sidebar_id ); + wp.customize.trigger( 'widget-updated', widget_id ); self.refreshTransports(); } ); } ); @@ -288,6 +290,7 @@ var WidgetCustomizerPreview = (function ($) { // If a widget was removed so that no widgets remain rendered in sidebar, we need to disable postMessage self.refreshTransports(); + wp.customize.trigger( 'sidebar-updated', sidebar_id ); } ); } ); } ); diff --git a/widget-customizer.php b/widget-customizer.php index 8d56c7d..f11848a 100644 --- a/widget-customizer.php +++ b/widget-customizer.php @@ -51,8 +51,16 @@ class Widget_Customizer { 'text', ); + protected static $builtin_supported_themes_with_scripts = array( + 'tewntyten' => false, + 'tewntyeleven' => false, + 'tewntytwelve' => false, + 'twentythirteen' => true, + ); + static function setup() { self::load_textdomain(); + add_action( 'after_setup_theme', array( __CLASS__, 'add_builtin_theme_support' ) ); add_action( 'after_setup_theme', array( __CLASS__, 'setup_widget_addition_previews' ) ); add_action( 'customize_controls_init', array( __CLASS__, 'customize_controls_init' ) ); add_action( 'customize_register', array( __CLASS__, 'schedule_customize_register' ), 1 ); @@ -103,6 +111,21 @@ static function get_version() { protected static $_customized; protected static $_prepreview_added_filters = array(); + /** + * Do add_theme_support() for any built-in supported theme; other themes need to do this themselves + * @action after_setup_theme + */ + static function add_builtin_theme_support() { + $is_builtin_supported = ( + isset( self::$builtin_supported_themes_with_scripts[ get_stylesheet() ] ) + || + isset( self::$builtin_supported_themes_with_scripts[ get_template() ] ) + ); + if ( $is_builtin_supported ) { + add_theme_support( 'widget-customizer' ); + } + } + /** * Since the widgets get registered (widgets_init) before the customizer settings are set up (customize_register), * we have to filter the options similarly to how the setting previewer will filter the options later. @@ -702,6 +725,23 @@ static function customize_preview_enqueue_deps() { self::get_version() ); + // Enqueue any scripts provided to add live preview support for buultin themes (e.g. twentythirteen) + $applied_themes = array( get_template() ); + if ( get_stylesheet() !== get_template() ) { + $applied_themes[] = get_stylesheet(); + } + foreach ( $applied_themes as $applied_theme ) { + if ( ! empty( self::$builtin_supported_themes_with_scripts[ $applied_theme ] ) ) { + wp_enqueue_script( + "widget-customizer-$applied_theme", + self::get_plugin_path_url( "theme-support/$applied_theme.js" ), + array( 'customize-preview' ), + self::get_version(), + true + ); + } + } + $all_id_bases = array(); foreach ( $wp_registered_widgets as $widget ) { if ( isset( $wp_registered_widget_controls[$widget['id']]['id_base'] ) ) { From c5eb25448a676d80972ef4db074a9ed77f45e8fa Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 8 Dec 2013 00:46:33 -0800 Subject: [PATCH 17/22] Fix unnecessary preview refresh when adding a widget --- widget-customizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget-customizer.js b/widget-customizer.js index 9f3c06e..314f361 100644 --- a/widget-customizer.js +++ b/widget-customizer.js @@ -287,7 +287,7 @@ var WidgetCustomizer = (function ($) { settings: { 'default': setting_id }, - sidebar_id: control.sidebar_id, + sidebar_id: control.params.sidebar_id, widget_id: widget_id, widget_id_base: widget.get( 'id_base' ), type: customize_control_type From 75dbd04326d69e3e245396959a36e7737dbfd2b0 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 8 Dec 2013 00:52:13 -0800 Subject: [PATCH 18/22] Simplify conditions for twentythirteen theme support JS --- theme-support/twentythirteen.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/theme-support/twentythirteen.js b/theme-support/twentythirteen.js index 7346166..ea8bab0 100644 --- a/theme-support/twentythirteen.js +++ b/theme-support/twentythirteen.js @@ -1,11 +1,7 @@ /*global jQuery, wp */ jQuery( function ($) { wp.customize.bind( 'sidebar-updated', function ( sidebar_id ) { - if ( 'sidebar-1' !== sidebar_id ) { - return; - } - - if ( $.isFunction( $.fn.masonry ) ) { + if ( 'sidebar-1' === sidebar_id && $.isFunction( $.fn.masonry ) ) { var widget_area = $( '#secondary .widget-area' ); widget_area.masonry( 'reloadItems' ); widget_area.masonry(); From c29da05cbe5faafdffe478006388e318e3df33b8 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 8 Dec 2013 23:21:26 -0800 Subject: [PATCH 19/22] Opt-in to postMessage Ajax previews in twentyfourteen --- widget-customizer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/widget-customizer.php b/widget-customizer.php index f11848a..8e45b3b 100644 --- a/widget-customizer.php +++ b/widget-customizer.php @@ -56,6 +56,7 @@ class Widget_Customizer { 'tewntyeleven' => false, 'tewntytwelve' => false, 'twentythirteen' => true, + 'twentyfourteen' => false, ); static function setup() { From ca95ff7cefd1f02b9f9afb9e606b679c67e07bd1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 9 Dec 2013 00:22:37 -0800 Subject: [PATCH 20/22] Simplify widget live-previewable opt-in filter to use boolean --- widget-customizer.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/widget-customizer.php b/widget-customizer.php index 8e45b3b..fb235ee 100644 --- a/widget-customizer.php +++ b/widget-customizer.php @@ -552,14 +552,14 @@ static function get_setting_args( $id, $overrides = array() ) { * @return string */ static function get_widget_setting_transport( $id_base ) { - $transport = 'refresh'; + $live_previewable = false; if ( current_theme_supports( 'widget-customizer' ) && in_array( $id_base, self::$core_widget_base_ids ) ) { - $transport = 'postMessage'; + $live_previewable = true; } // Allow widgets to opt-in for postMessage - $transport = apply_filters( 'customizer_widget_transport', $transport, $id_base ); - $transport = apply_filters( "customizer_widget_transport_{$id_base}", $transport ); - return $transport; + $live_previewable = apply_filters( 'customizer_widget_live_previewable', $live_previewable, $id_base ); + $live_previewable = apply_filters( "customizer_widget_live_previewable_{$id_base}", $live_previewable ); + return $live_previewable ? 'postMessage' : 'refresh'; } /** @@ -567,13 +567,13 @@ static function get_widget_setting_transport( $id_base ) { * @return string */ static function get_sidebar_widgets_setting_transport( $sidebar_id ) { - $transport = 'refresh'; + $live_previewable = false; if ( current_theme_supports( 'widget-customizer' ) ) { - $transport = 'postMessage'; + $live_previewable = true; } - $transport = apply_filters( 'customizer_sidebar_widgets_transport', $transport, $sidebar_id ); - $transport = apply_filters( "customizer_sidebar_widgets_transport_{$sidebar_id}", $transport ); - return $transport; + $live_previewable = apply_filters( 'customizer_sidebar_widgets_live_previewable', $live_previewable, $sidebar_id ); + $live_previewable = apply_filters( "customizer_sidebar_widgets_live_previewable_{$sidebar_id}", $live_previewable ); + return $live_previewable ? 'postMessage' : 'refresh'; } /** From 35040cf0fe1dde57bd58f13a288ea535b374a663 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 9 Dec 2013 00:46:39 -0800 Subject: [PATCH 21/22] Fix initial render of newly added widget without live previewability Fixes: Preview does not refresh when initially adding a widget that does not support live previews. Such a widget currently has to be forcibly updated to then appear. This is a problem for widgets that lack forms. --- widget-customizer-preview.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/widget-customizer-preview.js b/widget-customizer-preview.js index 167c986..bc96fac 100644 --- a/widget-customizer-preview.js +++ b/widget-customizer-preview.js @@ -272,7 +272,12 @@ var WidgetCustomizerPreview = (function ($) { // Force the callback to fire if this widget is newly-added if ( from.indexOf( widget_id ) === -1 ) { self.refreshTransports(); - setting.callbacks.fireWith( setting, [ setting(), null ] ); + var parent_setting = parent.wp.customize( setting_id ); + if ( 'postMessage' === parent_setting.transport ) { + setting.callbacks.fireWith( setting, [ setting(), null ] ); + } else { + self.preview.send( 'refresh' ); + } } } ); From a5d1068c1925e3cbb49c7125f6ab9920de55f55a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 12 Dec 2013 15:33:44 -0800 Subject: [PATCH 22/22] Disable scrollIntoView because of jarring experience To be resolved in #16 --- widget-customizer.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/widget-customizer.js b/widget-customizer.js index 314f361..70d6c18 100644 --- a/widget-customizer.js +++ b/widget-customizer.js @@ -583,12 +583,7 @@ var WidgetCustomizer = (function ($) { * Inside of the customizer preview, scroll the widget into view */ scrollPreviewWidgetIntoView: function () { - var control = this; - var widget_el = control.getPreviewWidgetElement(); // @todo scrollIntoView() provides a robust but very poor experience. Animation is needed. See https://github.com/x-team/wp-widget-customizer/issues/16 - if ( widget_el.length ) { - widget_el[0].scrollIntoView( false ); - } }, /**