Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active January 29, 2017 06:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save westonruter/d4a21d8c891974e808975dcdfe97625d to your computer and use it in GitHub Desktop.
Save westonruter/d4a21d8c891974e808975dcdfe97625d to your computer and use it in GitHub Desktop.
/* global jQuery, wp */
jQuery( function( $ ) {
var initializeAllClocks, initializeClock;
/**
* Find clocks.
*
* @param [container] Scoping element for finding clock elements.
* @returns {void}
*/
initializeAllClocks = function( container ) {
$( container || document.body ).find( '.wpse-247251-clock' ).each( function() {
initializeClock( $( this ) );
} );
};
/**
* Initialize clock.
*
* @param {jQuery} clockElement Clock element.
* @returns {void}
*/
initializeClock = function( clockElement ) {
var timeoutId, populateTime;
if ( clockElement.data( 'initialized' ) ) {
return;
}
populateTime = function() {
clockElement.text( new Date().toString() );
};
timeoutId = setInterval( function() {
// Stop interval when element is no longer in the DOM.
if ( ! $.contains( document, clockElement[0] ) ) {
clearInterval( timeoutId );
return;
}
populateTime();
}, 1000 );
populateTime();
clockElement.data( 'initialized', true );
};
initializeAllClocks();
// Re-initialize clocks when a partial is updated in the customizer preview.
if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.customize && 'undefined' !== typeof wp.customize.selectiveRefresh ) {
wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
initializeAllClocks( $( placement.container ) );
} );
}
} );
.widget.color-scheme-dark,
.widget.color-scheme-dark h2,
.widget.color-scheme-dark p {
color: white !important;
background: navy !important;
}
.widget.color-scheme-light,
.widget.color-scheme-light h2,
.widget.color-scheme-light p {
color: black !important;
background: palevioletred !important;
}
/* global jQuery, wp */
jQuery( function( $ ) {
var initializeAllCountdowns, initializeCountdown;
/**
* Find countdowns.
*
* @param [container] Scoping element for finding countdown elements.
* @returns {void}
*/
initializeAllCountdowns = function( container ) {
$( container || document.body ).find( '.wpse-247251-countdown' ).each( function() {
initializeCountdown( $( this ) );
} );
};
/**
* Initialize countdown.
*
* @param {jQuery} countdownElement Countdown element.
* @returns {void}
*/
initializeCountdown = function( countdownElement ) {
var timeoutId, populateCountdown;
if ( countdownElement.data( 'initialized' ) ) {
return;
}
populateCountdown = function() {
var now, newYearDate;
now = new Date();
newYearDate = new Date( now.getFullYear() + 1, 0, 1, 0, 0, 0 );
countdownElement.text( 'T-' + String( Math.floor( ( newYearDate.valueOf() - now.valueOf() ) / 1000 ) ) );
};
timeoutId = setInterval( function() {
// Stop interval when element is no longer in the DOM.
if ( ! $.contains( document, countdownElement[0] ) ) {
clearInterval( timeoutId );
return;
}
populateCountdown();
}, 1000 );
populateCountdown();
countdownElement.data( 'initialized', true );
};
initializeAllCountdowns();
// Re-initialize countdowns when a partial is updated in the customizer preview.
if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.customize && 'undefined' !== typeof wp.customize.selectiveRefresh ) {
wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function( placement ) {
initializeAllCountdowns( $( placement.container ) );
} );
}
} );
/* global wp, _ */
/* exported WPSE_247251 */
var WPSE_247251 = (function( api ) {
var component = {
data: {
enqueued_styles: {},
enqueued_scripts: {}
}
};
/**
* Init.
*
* @param {object} data Data exported from PHP.
* @returns {void}
*/
component.init = function init( data ) {
if ( data ) {
_.extend( component.data, data );
}
api.selectiveRefresh.bind( 'render-partials-response', component.handleRenderPartialsResponse );
};
/**
* Handle render-partials-response event, requesting full refresh if newly-enqueued styles/styles differ.
*
* @param {object} response Response.
* @returns {void}
*/
component.handleRenderPartialsResponse = function handleRenderPartialsResponse( response ) {
if ( ! response.wpse_247251 ) {
return;
}
if ( ! _.isEqual( component.data.enqueued_styles, response.wpse_247251.enqueued_styles ) ) {
api.selectiveRefresh.requestFullRefresh();
}
if ( ! _.isEqual( component.data.enqueued_scripts, response.wpse_247251.enqueued_scripts ) ) {
api.selectiveRefresh.requestFullRefresh();
}
};
return component;
})( wp.customize );
<?php
/**
* Plugin Name: WPSE 247251: Initiating Full Refresh when Selective Refresh Enqueues Different Styles
* Author: Weston Ruter, XWP
* Author URI: https://make.xwp.co/
* Plugin URI: http://wordpress.stackexchange.com/questions/247251/how-to-mix-partial-and-full-page-refresh-in-the-same-section-of-the-customizer
*
* Copyright (c) 2016 XWP (https://make.xwp.co/)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2 or, at
* your discretion, any later version, as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* @package WPSE_247251
*/
namespace WPSE_247251;
/**
* Class Plugin
*/
class Plugin {
/**
* Example widget.
*
* @var Example_Widget
*/
public $widget;
/**
* Plugin registered style handles.
*
* @var array
*/
public $registered_style_handles = array();
/**
* Plugin registered script handles.
*
* @var array
*/
public $registered_script_handles = array();
/**
* Add hooks.
*/
function add_hooks() {
add_action( 'widgets_init', array( $this, 'register_widget' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_dependencies' ) );
add_action( 'customize_render_partials_before', array( $this, 'enqueue_selective_refresh_dependencies' ), 100 );
add_filter( 'customize_render_partials_response', array( $this, 'filter_customize_render_partials_response' ) );
}
/**
* Register widget.
*/
function register_widget() {
$this->widget = new Example_Widget();
register_widget( $this->widget );
}
/**
* Enqueue dependencies.
*/
function enqueue_dependencies() {
$light_color_scheme_style_handle = 'wpse-247251-color-scheme-light';
$src = plugin_dir_url( __FILE__ ) . 'color-scheme-light.css';
wp_register_style( $light_color_scheme_style_handle, $src );
$dark_color_scheme_style_handle = 'wpse-247251-color-scheme-dark';
$src = plugin_dir_url( __FILE__ ) . 'color-scheme-dark.css';
wp_register_style( $dark_color_scheme_style_handle, $src );
$clock_behavior_script_handle = 'wpse-247251-clock-behavior';
$src = plugin_dir_url( __FILE__ ) . 'clock-behavior.js';
$deps = array( 'jquery' );
if ( is_customize_preview() ) {
$deps[] = 'customize-selective-refresh';
}
wp_register_script( $clock_behavior_script_handle, $src, $deps );
$countdown_behavior_script_handle = 'wpse-247251-countdown-behavior';
$src = plugin_dir_url( __FILE__ ) . 'countdown-behavior.js';
$deps = array( 'jquery' );
if ( is_customize_preview() ) {
$deps[] = 'customize-selective-refresh';
}
wp_register_script( $countdown_behavior_script_handle, $src, $deps );
$this->registered_style_handles = array( $light_color_scheme_style_handle, $dark_color_scheme_style_handle );
$this->registered_script_handles = array( $clock_behavior_script_handle, $countdown_behavior_script_handle );
// Enqueue dependencies only if they are used by widgets.
$instances = $this->widget->get_settings();
foreach ( wp_get_sidebars_widgets() as $sidebar => $sidebar_widgets ) {
foreach ( $sidebar_widgets as $widget_id ) {
if ( ! preg_match( '/^' . Example_Widget::ID_BASE . '-(?P<number>\d+)$/', $widget_id, $matches ) ) {
continue;
}
if ( ! isset( $instances[ $matches['number'] ] ) ) {
continue;
}
$instance = array_merge( $this->widget->default_instance, $instances[ $matches['number'] ] );
if ( 'light' === $instance['color_scheme'] ) {
wp_enqueue_style( $light_color_scheme_style_handle );
} elseif ( 'dark' === $instance['color_scheme'] ) {
wp_enqueue_style( $dark_color_scheme_style_handle );
}
if ( 'clock' === $instance['behavior'] ) {
wp_enqueue_script( $clock_behavior_script_handle );
} elseif ( 'countdown' === $instance['behavior'] ) {
wp_enqueue_script( $countdown_behavior_script_handle );
}
}
}
// Enqueue helper script for listening to selective refresh events.
if ( is_customize_preview() ) {
$preview_handle = 'wpse-247251-customize-preview';
$src = plugin_dir_url( __FILE__ ) . 'customize-preview.js';
$deps = array( 'customize-selective-refresh' );
wp_enqueue_script( $preview_handle, $src, $deps );
// Export which widget styles specifically where enqueued.
wp_add_inline_script(
$preview_handle,
sprintf( 'WPSE_247251.init( %s )', wp_json_encode( $this->get_enqueued_dependencies() ) ),
'after'
);
}
}
/**
* Get enqueued dependencies.
*
* @return array Enqueued dependencies.
*/
public function get_enqueued_dependencies() {
$data = array(
'enqueued_styles' => array(),
'enqueued_scripts' => array(),
);
foreach ( $this->registered_style_handles as $style_handle ) {
if ( wp_style_is( $style_handle, 'enqueued' ) ) {
$data['enqueued_styles'][ $style_handle ] = true;
}
}
foreach ( $this->registered_script_handles as $script_handle ) {
if ( wp_script_is( $script_handle, 'enqueued' ) ) {
$data['enqueued_styles'][ $script_handle ] = true;
}
}
return $data;
}
/**
* Enqueue scripts in selective refresh responses.
*
* Core should do this eventually, hence the conditional. See core docs
* for the `customize_render_partials_before` action.
*/
function enqueue_selective_refresh_dependencies() {
if ( ! did_action( 'wp_enqueue_scripts' ) ) {
wp_enqueue_scripts();
}
}
/**
* Export the enqueued styles in the selective refresh response.
*
* Note that core should eventually include the enqueued scripts/styles by
* default. See docs for the `customize_render_partials_response` filter.
*
* @param array $response Response.
* @return array Response.
*/
function filter_customize_render_partials_response( $response ) {
$response['wpse_247251'] = $this->get_enqueued_dependencies();
return $response;
}
}
/**
* Class Example_Widget
*/
class Example_Widget extends \WP_Widget {
const ID_BASE = 'wpse_247251';
/**
* Default instance.
*
* @var array
*/
public $default_instance;
/**
* Sets up a new widget instance.
*/
public function __construct() {
$widget_ops = array(
'classname' => 'widget_wpse_247251',
'description' => __( 'WPSE 247251', 'wpse-247251' ),
'customize_selective_refresh' => true,
);
$this->default_instance = array(
'title' => '',
'color_scheme' => 'light',
'behavior' => 'clock',
);
parent::__construct( self::ID_BASE, __( 'WPSE 247251', 'wpse-247251' ), $widget_ops );
}
/**
* Outputs the content for the current widget.
*
* @param array $args Display arguments including 'before_title', 'after_title', 'before_widget', and 'after_widget'.
* @param array $instance Settings for the current Text widget instance.
*/
public function widget( $args, $instance ) {
$instance = wp_parse_args( (array) $instance, $this->default_instance );
if ( empty( $instance['title'] ) ) {
$instance['title'] = __( 'WPSE Example', 'wpse-247251' );
}
/** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
$args['before_widget'] = preg_replace(
'/(?<=class=")/',
esc_attr( 'color-scheme-' . $instance['color_scheme'] . ' ' ),
$args['before_widget'],
1
);
echo $args['before_widget'];
printf( '<div class="color-scheme-%s">', esc_attr( $instance['color_scheme'] ) );
echo $args['before_title'] . $title . $args['after_title'];
echo '<p>' . esc_html__( 'This widget demonstrates how full refresh can be invoked when selective refresh returns with different scripts or styles being enqueued.', 'wpse-247251' ) . '</p>';
if ( 'clock' === $instance['behavior'] ) {
echo '<p>' . esc_html__( 'Clock: ', 'wpse-247251' ) . '<span class="wpse-247251-clock">' . esc_html__( 'Loading...', 'wpse-247251' ) . '</span></p>';
} elseif ( 'countdown' === $instance['behavior'] ) {
echo '<p>' . esc_html__( 'New Year Countdown: ', 'wpse-247251' ) . '<span class="wpse-247251-countdown">' . esc_html__( 'Loading...', 'wpse-247251' ) . '</span> ' . esc_html__( '(seconds)', 'wpse-247251' ) . '</p>';
}
echo $args['after_widget'];
}
/**
* Handles updating settings.
*
* @param array $new_instance New settings for this instance.
* @param array $old_instance Old settings for this instance.
* @return array Instance.
*/
public function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = sanitize_text_field( $new_instance['title'] );
if ( in_array( $new_instance['color_scheme'], array( 'light', 'dark' ), true ) ) {
$instance['color_scheme'] = $new_instance['color_scheme'];
}
if ( in_array( $new_instance['behavior'], array( 'clock', 'countdown' ), true ) ) {
$instance['behavior'] = $new_instance['behavior'];
}
return $instance;
}
/**
* Form.
*
* @param array $instance Instance.
* @returns void
*/
function form( $instance ) {
$instance = wp_parse_args( (array) $instance, $this->default_instance );
$color_scheme_options = array(
'light' => __( 'Light', 'wpse-247251' ),
'dark' => __( 'Dark', 'wpse-247251' ),
);
$behavior_options = array(
'clock' => __( 'Clock', 'wpse-247251' ),
'countdown' => __( 'Countdown', 'wpse-247251' ),
);
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:', 'wpse-247251' ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/>
</p>
<p>
<label for="<?php echo $this->get_field_id( 'color_scheme' ); ?>"><?php esc_html_e( 'Color Scheme:', 'wpse-247251' ); ?></label>
<select id="<?php echo $this->get_field_id( 'color_scheme' ); ?>" name="<?php echo $this->get_field_name( 'color_scheme' ); ?>">
<?php foreach ( $color_scheme_options as $value => $text ) : ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $instance['color_scheme'], $value ) ?>><?php echo esc_html( $text ); ?></option>
<?php endforeach; ?>
</select>
</p>
<p>
<label for="<?php echo $this->get_field_id( 'behavior' ); ?>"><?php esc_html_e( 'Behavior:', 'wpse-247251' ); ?></label>
<select id="<?php echo $this->get_field_id( 'behavior' ); ?>" name="<?php echo $this->get_field_name( 'behavior' ); ?>">
<?php foreach ( $behavior_options as $value => $text ) : ?>
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $instance['behavior'], $value ) ?>><?php echo esc_html( $text ); ?></option>
<?php endforeach; ?>
</select>
</p>
<?php
}
}
$wpse_247251_plugin = new Plugin();
add_action( 'plugins_loaded', array( $wpse_247251_plugin, 'add_hooks' ) );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment