Introduce new theme customizer to replace theme preview. Rough first pass. props koopersmith, ocean90. see #19910.
Merges in http://plugins.svn.wordpress.org/gandalf/branches/dev/ rev 510148. git-svn-id: http://svn.automattic.com/wordpress/trunk@19995 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
8859ba872c
commit
c832f904ae
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
/**
|
||||
* Customize Section Class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Customize
|
||||
* @since 3.4.0
|
||||
*/
|
||||
|
||||
class WP_Customize_Section {
|
||||
public $id;
|
||||
public $priority = 10;
|
||||
public $capability = 'edit_theme_options';
|
||||
public $theme_supports = '';
|
||||
public $title = '';
|
||||
public $description = '';
|
||||
public $settings;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the section.
|
||||
* @param array $args Section arguments.
|
||||
*/
|
||||
function __construct( $id, $args = array() ) {
|
||||
$this->id = $id;
|
||||
|
||||
$keys = array_keys( get_class_vars( __CLASS__ ) );
|
||||
foreach ( $keys as $key ) {
|
||||
if ( isset( $args[ $key ] ) )
|
||||
$this->$key = $args[ $key ];
|
||||
}
|
||||
|
||||
$this->settings = array(); // Users cannot customize the $settings array.
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the theme supports the section and check user capabilities.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return bool False if theme doesn't support the section or user doesn't have the capability.
|
||||
*/
|
||||
function check_capabilities() {
|
||||
if ( ! $this->capability || ! current_user_can( $this->capability ) )
|
||||
return false;
|
||||
|
||||
if ( $this->theme_supports && ! current_theme_supports( $this->theme_supports ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the section.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
function render() {
|
||||
if ( ! $this->check_capabilities() )
|
||||
return;
|
||||
?>
|
||||
<li id="customize-section-<?php echo esc_attr( $this->id ); ?>" class="control-section customize-section">
|
||||
<h3 class="customize-theme-title"><?php echo esc_html( $this->title ); ?></h3>
|
||||
<ul>
|
||||
<?php if ( $this->description ) : ?>
|
||||
<li><p class="howto"><?php echo $this->description; ?></p></li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ( $this->settings as $setting ) : ?>
|
||||
<li>
|
||||
<?php $setting->_render(); ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php
|
||||
}
|
||||
}
|
|
@ -0,0 +1,489 @@
|
|||
<?php
|
||||
/**
|
||||
* Customize Setting Class
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Customize
|
||||
* @since 3.4.0
|
||||
*/
|
||||
|
||||
class WP_Customize_Setting {
|
||||
public $id;
|
||||
public $priority = 10;
|
||||
public $section = '';
|
||||
public $label = '';
|
||||
public $control = 'text';
|
||||
public $type = 'theme_mod';
|
||||
public $choices = array();
|
||||
public $capability = 'edit_theme_options';
|
||||
public $theme_supports = '';
|
||||
public $default = '';
|
||||
public $sanitize_callback = '';
|
||||
|
||||
protected $id_data = array();
|
||||
private $_post_value; // Cached, sanitized $_POST value.
|
||||
|
||||
// Prefix for $_POST values to prevent naming conflicts.
|
||||
const name_prefix = 'customize_';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the setting. Can be a
|
||||
* theme mod or option name.
|
||||
* @param array $args Setting arguments.
|
||||
*/
|
||||
function __construct( $id, $args = array() ) {
|
||||
$keys = array_keys( get_class_vars( __CLASS__ ) );
|
||||
foreach ( $keys as $key ) {
|
||||
if ( isset( $args[ $key ] ) )
|
||||
$this->$key = $args[ $key ];
|
||||
}
|
||||
|
||||
$this->id = $id;
|
||||
|
||||
// Parse the ID for array keys.
|
||||
$this->id_data[ 'keys' ] = preg_split( '/\[/', str_replace( ']', '', $this->id ) );
|
||||
$this->id_data[ 'base' ] = array_shift( $this->id_data[ 'keys' ] );
|
||||
|
||||
// Rebuild the ID.
|
||||
$this->id = $this->id_data[ 'base' ];
|
||||
if ( ! empty( $this->id_data[ 'keys' ] ) )
|
||||
$this->id .= '[' . implode( '][', $this->id_data[ 'keys' ] ) . ']';
|
||||
|
||||
if ( $this->sanitize_callback != '' )
|
||||
add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue setting related scripts/styles.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function enqueue() {
|
||||
switch( $this->control ) {
|
||||
case 'color':
|
||||
wp_enqueue_script( 'farbtastic' );
|
||||
wp_enqueue_style( 'farbtastic' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle previewing the setting.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function preview() {
|
||||
switch( $this->type ) {
|
||||
case 'theme_mod' :
|
||||
add_filter( 'theme_mod_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
|
||||
break;
|
||||
case 'option' :
|
||||
if ( empty( $this->id_data[ 'keys' ] ) )
|
||||
add_filter( 'pre_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
|
||||
else
|
||||
add_filter( 'option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
|
||||
break;
|
||||
default :
|
||||
do_action( 'customize_preview_' . $this->id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function to filter the theme mods and options.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param mixed Old value.
|
||||
* @return mixed New or old value.
|
||||
*/
|
||||
public function _preview_filter( $original ) {
|
||||
return $this->multidimensional_replace( $original, $this->id_data[ 'keys' ], $this->post_value() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the parameter for a specific theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return bool False if cap check fails or value isn't set.
|
||||
*/
|
||||
public final function save() {
|
||||
$value = $this->post_value();
|
||||
|
||||
if ( ! $this->check_capabilities() || ! isset( $value ) )
|
||||
return false;
|
||||
|
||||
do_action( 'customize_save_' . $this->id_data[ 'base' ] );
|
||||
|
||||
$this->update( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches, validates, and sanitizes the $_POST value.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $default mixed A default value which is used as a fallback. Default is null.
|
||||
* @return mixed Either the default value on failure or sanitized value.
|
||||
*/
|
||||
public final function post_value( $default = null ) {
|
||||
if ( isset( $this->_post_value ) )
|
||||
return $this->_post_value;
|
||||
|
||||
$base = self::name_prefix . $this->id_data[ 'base' ];
|
||||
|
||||
if ( ! isset( $_POST[ $base ] ) )
|
||||
return $default;
|
||||
|
||||
$result = $this->multidimensional_get( $_POST[ $base ], $this->id_data[ 'keys' ] );
|
||||
if ( ! isset( $result ) )
|
||||
return $default;
|
||||
|
||||
$result = $this->sanitize( $result );
|
||||
if ( isset( $result ) )
|
||||
return $this->_post_value = $result;
|
||||
else
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize an input.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $value mixed The value to sanitize.
|
||||
* @return mixed Null if an input isn't valid, otherwise the sanitized value.
|
||||
*/
|
||||
public function sanitize( $value ) {
|
||||
return apply_filters( "customize_sanitize_{$this->id}", $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the parameter for a specific theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $value mixed The value to update.
|
||||
* @return mixed The result of saving the value.
|
||||
*/
|
||||
protected function update( $value ) {
|
||||
switch( $this->type ) {
|
||||
case 'theme_mod' :
|
||||
return $this->_update_theme_mod( $value );
|
||||
break;
|
||||
case 'option' :
|
||||
return $this->_update_option( $value );
|
||||
break;
|
||||
default :
|
||||
return do_action( 'customize_update_' . $this->type, $value );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the theme mod from the value of the parameter.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $value mixed The value to update.
|
||||
* @return mixed The result of saving the value.
|
||||
*/
|
||||
protected function _update_theme_mod( $value ) {
|
||||
// Handle non-array theme mod.
|
||||
if ( empty( $this->id_data[ 'keys' ] ) )
|
||||
return set_theme_mod( $this->id_data[ 'base' ], $value );
|
||||
|
||||
// Handle array-based theme mod.
|
||||
$mods = get_theme_mod( $this->id_data[ 'base' ] );
|
||||
$mods = $this->multidimensional_replace( $mods, $this->id_data[ 'keys' ], $value );
|
||||
if ( isset( $mods ) )
|
||||
return set_theme_mod( $this->id_data[ 'base' ], $mods );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the theme mod from the value of the parameter.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $value mixed The value to update.
|
||||
* @return mixed The result of saving the value.
|
||||
*/
|
||||
protected function _update_option( $value ) {
|
||||
// Handle non-array option.
|
||||
if ( empty( $this->id_data[ 'keys' ] ) )
|
||||
return update_option( $this->id_data[ 'base' ], $value );
|
||||
|
||||
// Handle array-based options.
|
||||
$options = get_option( $this->id_data[ 'base' ] );
|
||||
$options = $this->multidimensional_replace( $options, $this->id_data[ 'keys' ], $value );
|
||||
if ( isset( $options ) )
|
||||
return update_option( $this->id_data[ 'base' ], $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the value of the parameter for a specific theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return mixed The requested value.
|
||||
*/
|
||||
public function value() {
|
||||
switch( $this->type ) {
|
||||
case 'theme_mod' :
|
||||
$function = 'get_theme_mod';
|
||||
break;
|
||||
case 'option' :
|
||||
$function = 'get_option';
|
||||
break;
|
||||
default :
|
||||
return apply_filters( 'customize_value_' . $this->id_data[ 'base' ], $this->default );
|
||||
}
|
||||
|
||||
// Handle non-array value
|
||||
if ( empty( $this->id_data[ 'keys' ] ) )
|
||||
return $function( $this->id_data[ 'base' ], $this->default );
|
||||
|
||||
// Handle array-based value
|
||||
$values = $function( $this->id_data[ 'base' ] );
|
||||
return $this->multidimensional_get( $values, $this->id_data[ 'keys' ], $this->default );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the theme supports the setting and check user capabilities.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return bool False if theme doesn't support the setting or user can't change setting, otherwise true.
|
||||
*/
|
||||
public final function check_capabilities() {
|
||||
global $customize;
|
||||
|
||||
if ( ! $this->capability || ! current_user_can( $this->capability ) )
|
||||
return false;
|
||||
|
||||
if ( $this->theme_supports && ! current_theme_supports( $this->theme_supports ) )
|
||||
return false;
|
||||
|
||||
$section = $customize->get_section( $this->section );
|
||||
if ( isset( $section ) && ! $section->check_capabilities() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the control.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final function _render() {
|
||||
if ( ! $this->check_capabilities() )
|
||||
return;
|
||||
|
||||
do_action( 'customize_render_' . $this->id );
|
||||
|
||||
$this->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the control.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
protected function render() {
|
||||
$this->_render_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the name attribute for an input.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string The name.
|
||||
*/
|
||||
public final function get_name() {
|
||||
return self::name_prefix . esc_attr( $this->id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo the HTML name attribute for an input.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string The HTML name attribute.
|
||||
*/
|
||||
public final function name() {
|
||||
echo 'name="' . $this->get_name() . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the control type.
|
||||
*
|
||||
* @todo Improve value and checked attributes.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public final function _render_type() {
|
||||
switch( $this->control ) {
|
||||
case 'text':
|
||||
?>
|
||||
<label><?php echo esc_html( $this->label ); ?><br/>
|
||||
<input type="text" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); ?> />
|
||||
</label>
|
||||
<?php
|
||||
break;
|
||||
case 'color':
|
||||
?>
|
||||
<label><?php echo esc_html( $this->label ); ?><br/>
|
||||
<span class="hex-prepend">#</span>
|
||||
<input type="text" class="hex-input" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); ?> />
|
||||
<a href="#" class="pickcolor hex-color-example"></a>
|
||||
</label>
|
||||
<?php
|
||||
break;
|
||||
case 'checkbox':
|
||||
?>
|
||||
<label>
|
||||
<input type="checkbox" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->name(); checked( $this->value() ); ?> />
|
||||
<?php echo esc_html( $this->label ); ?>
|
||||
</label>
|
||||
<?php
|
||||
break;
|
||||
case 'radio':
|
||||
if ( empty( $this->choices ) )
|
||||
return;
|
||||
|
||||
echo esc_html( $this->label ) . '<br/>';
|
||||
foreach ( $this->choices as $value => $label ) :
|
||||
?>
|
||||
<label>
|
||||
<input type="radio" value="<?php echo esc_attr( $value ); ?>" <?php $this->name(); checked( $this->value(), $value ); ?> />
|
||||
<?php echo esc_html( $label ); ?><br/>
|
||||
</label>
|
||||
<?php
|
||||
endforeach;
|
||||
break;
|
||||
case 'select':
|
||||
if ( empty( $this->choices ) )
|
||||
return;
|
||||
|
||||
?>
|
||||
<label><?php echo esc_html( $this->label ); ?><br/>
|
||||
<select <?php $this->name(); ?>>
|
||||
<?php
|
||||
foreach ( $this->choices as $value => $label )
|
||||
echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>';
|
||||
?>
|
||||
</select>
|
||||
<?php
|
||||
break;
|
||||
default:
|
||||
do_action( 'customize_render_control-' . $this->control, $this );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multidimensional helper function.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $root
|
||||
* @param $keys
|
||||
* @param bool $create Default is false.
|
||||
* @return null|array
|
||||
*/
|
||||
final protected function multidimensional( $root, $keys, $create = false ) {
|
||||
if ( $create && empty( $root ) )
|
||||
$root = array();
|
||||
|
||||
if ( ! isset( $root ) || empty( $keys ) )
|
||||
return;
|
||||
|
||||
$last = array_pop( $keys );
|
||||
$node = &$root;
|
||||
|
||||
foreach ( $keys as $key ) {
|
||||
if ( $create && ! isset( $node[ $key ] ) )
|
||||
$node[ $key ] = array();
|
||||
|
||||
if ( ! is_array( $node ) || ! isset( $node[ $key ] ) )
|
||||
return;
|
||||
|
||||
$node = &$node[ $key ];
|
||||
}
|
||||
|
||||
if ( $create && ! isset( $node[ $last ] ) )
|
||||
$node[ $last ] = array();
|
||||
|
||||
if ( ! isset( $node[ $last ] ) )
|
||||
return;
|
||||
|
||||
return array(
|
||||
'root' => &$root,
|
||||
'node' => &$node,
|
||||
'key' => $last,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will attempt to replace a specific value in a multidimensional array.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $root
|
||||
* @param $keys
|
||||
* @param mixed $value The value to update.
|
||||
* @return
|
||||
*/
|
||||
final protected function multidimensional_replace( $root, $keys, $value ) {
|
||||
if ( ! isset( $value ) )
|
||||
return $root;
|
||||
elseif ( empty( $keys ) ) // If there are no keys, we're replacing the root.
|
||||
return $value;
|
||||
|
||||
$result = $this->multidimensional( &$root, $keys, true );
|
||||
|
||||
if ( isset( $result ) )
|
||||
$result['node'][ $result['key'] ] = $value;
|
||||
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will attempt to fetch a specific value from a multidimensional array.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $root
|
||||
* @param $keys
|
||||
* @param $default A default value which is used as a fallback. Default is null.
|
||||
* @return mixed The requested value or the default value.
|
||||
*/
|
||||
final protected function multidimensional_get( $root, $keys, $default = null ) {
|
||||
if ( empty( $keys ) ) // If there are no keys, test the root.
|
||||
return isset( $root ) ? $root : $default;
|
||||
|
||||
$result = $this->multidimensional( $root, $keys );
|
||||
return isset( $result ) ? $result['node'][ $result['key'] ] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will attempt to check if a specific value in a multidimensional array is set.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param $root
|
||||
* @param $keys
|
||||
* @return bool True if value is set, false if not.
|
||||
*/
|
||||
final protected function multidimensional_isset( $root, $keys ) {
|
||||
$result = $this->multidimensional_get( $root, $keys );
|
||||
return isset( $result );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,660 @@
|
|||
<?php
|
||||
/**
|
||||
* Customize
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Customize
|
||||
* @since 3.4.0
|
||||
*/
|
||||
|
||||
final class WP_Customize {
|
||||
protected $template;
|
||||
protected $stylesheet;
|
||||
protected $previewing = false;
|
||||
|
||||
protected $settings = array();
|
||||
protected $sections = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function __construct() {
|
||||
require( ABSPATH . WPINC . '/class-wp-customize-setting.php' );
|
||||
require( ABSPATH . WPINC . '/class-wp-customize-section.php' );
|
||||
|
||||
add_action( 'setup_theme', array( $this, 'setup_theme' ) );
|
||||
add_action( 'admin_init', array( $this, 'admin_init' ) );
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
add_action( 'admin_footer', array( $this, 'admin_footer' ) );
|
||||
|
||||
add_action( 'customize_previewing', array( $this, 'customize_previewing' ) );
|
||||
add_action( 'customize_register', array( $this, 'register_controls' ) );
|
||||
add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update theme modifications for the current theme.
|
||||
* Note: Candidate core function.
|
||||
* http://core.trac.wordpress.org/ticket/20091
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param array $mods Theme modifications.
|
||||
*/
|
||||
public function set_theme_mods( $mods ) {
|
||||
$current = get_theme_mods();
|
||||
|
||||
$mods = wp_parse_args( $mods, $current );
|
||||
|
||||
$theme = get_stylesheet();
|
||||
update_option( "theme_mods_$theme", $mods );
|
||||
}
|
||||
|
||||
/**
|
||||
* Start preview and customize theme.
|
||||
* Check if customize query variable exist.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function setup_theme() {
|
||||
if ( ! isset( $_REQUEST['customize'] ) || 'on' != $_REQUEST['customize'] )
|
||||
return;
|
||||
|
||||
if ( ! $this->set_stylesheet() || isset( $_REQUEST['save'] ) )
|
||||
return;
|
||||
|
||||
$this->previewing = true;
|
||||
do_action( 'customize_previewing' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Init filters to filter theme options.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function customize_previewing() {
|
||||
global $wp_theme_directories;
|
||||
|
||||
show_admin_bar( false );
|
||||
|
||||
add_filter( 'template', array( $this, 'get_template' ) );
|
||||
add_filter( 'stylesheet', array( $this, 'get_stylesheet' ) );
|
||||
add_filter( 'pre_option_current_theme', array( $this, 'current_theme' ) );
|
||||
|
||||
// @link: http://core.trac.wordpress.org/ticket/20027
|
||||
add_filter( 'pre_option_stylesheet', array( $this, 'get_stylesheet' ) );
|
||||
add_filter( 'pre_option_template', array( $this, 'get_template' ) );
|
||||
|
||||
// Handle custom theme roots.
|
||||
if ( count( $wp_theme_directories ) > 1 ) {
|
||||
add_filter( 'pre_option_stylesheet_root', array( $this, 'get_stylesheet_root' ) );
|
||||
add_filter( 'pre_option_template_root', array( $this, 'get_template_root' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register styles/scripts and Init the preview of each setting
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function init() {
|
||||
do_action( 'customize_register' );
|
||||
|
||||
if ( ! $this->is_preview() )
|
||||
return;
|
||||
|
||||
wp_enqueue_script( 'customize-preview' );
|
||||
add_action( 'wp_footer', array( $this, 'customize_preview_settings' ), 20 );
|
||||
|
||||
foreach ( $this->settings as $setting ) {
|
||||
$setting->preview();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print javascript settings.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function customize_preview_settings() {
|
||||
$settings = array(
|
||||
// @todo: Perhaps grab the URL via $_POST?
|
||||
'parent' => esc_url( admin_url( 'themes.php' ) ),
|
||||
);
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
if ( typeof wp === 'undefined' || ! wp.customize )
|
||||
return;
|
||||
|
||||
wp.customize.settings = <?php echo json_encode( $settings ); ?>;
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Is it a theme preview?
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return bool True if it's a preview, false if not.
|
||||
*/
|
||||
public function is_preview() {
|
||||
return (bool) $this->previewing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the template name of the previewed theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return bool|string Template name.
|
||||
*/
|
||||
public function set_template() {
|
||||
if ( ! empty( $this->template ) )
|
||||
return $this->template;
|
||||
|
||||
$template = preg_replace('|[^a-z0-9_./-]|i', '', $_REQUEST['template'] );
|
||||
if ( validate_file( $template ) )
|
||||
return false;
|
||||
|
||||
return $this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stylesheet name of the previewed theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return bool|string Stylesheet name.
|
||||
*/
|
||||
public function set_stylesheet() {
|
||||
if ( ! empty( $this->stylesheet ) )
|
||||
return $this->stylesheet;
|
||||
|
||||
$this->set_template();
|
||||
if ( empty( $this->template ) )
|
||||
return false;
|
||||
|
||||
if ( empty( $_REQUEST['stylesheet'] ) ) {
|
||||
$stylesheet = $this->template;
|
||||
} else {
|
||||
$stylesheet = preg_replace( '|[^a-z0-9_./-]|i', '', $_REQUEST['stylesheet'] );
|
||||
if ( $stylesheet != $this->template && validate_file( $stylesheet ) )
|
||||
return false;
|
||||
}
|
||||
return $this->stylesheet = $stylesheet;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the template name of the previewed theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string Template name.
|
||||
*/
|
||||
public function get_template() {
|
||||
return $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the stylesheet name of the previewed theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string Stylesheet name.
|
||||
*/
|
||||
public function get_stylesheet() {
|
||||
return $this->stylesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the template root of the previewed theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string Theme root.
|
||||
*/
|
||||
public function get_template_root() {
|
||||
return get_raw_theme_root( $this->template, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the stylesheet root of the previewed theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string Theme root.
|
||||
*/
|
||||
public function get_stylesheet_root() {
|
||||
return get_raw_theme_root( $this->stylesheet, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the current theme and return the name of the previewed theme.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @return string Theme name.
|
||||
*/
|
||||
public function current_theme( $current_theme ) {
|
||||
$themes = get_themes();
|
||||
|
||||
if ( ! $themes )
|
||||
return $current_theme;
|
||||
|
||||
foreach ( $themes as $theme ) {
|
||||
if ( $theme['Stylesheet'] == $this->stylesheet && $theme['Template'] == $this->template )
|
||||
return $theme['Name'];
|
||||
}
|
||||
|
||||
return $current_theme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger save action and load customize controls.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function admin_init() {
|
||||
if ( isset( $_REQUEST['save'] ) )
|
||||
$this->save();
|
||||
|
||||
wp_enqueue_script( 'customize-loader' );
|
||||
wp_enqueue_style( 'customize-loader' );
|
||||
|
||||
if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
|
||||
return;
|
||||
|
||||
if ( ! isset( $_GET['customize'] ) || 'on' != $_GET['customize'] )
|
||||
return;
|
||||
|
||||
if ( ! $this->is_preview() )
|
||||
return;
|
||||
|
||||
if ( ! current_user_can( 'edit_theme_options' ) )
|
||||
return;
|
||||
|
||||
include( ABSPATH . WPINC . '/customize-controls.php' );
|
||||
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the customize template.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function admin_footer() {
|
||||
?>
|
||||
<div id="customize-container">
|
||||
<input type="hidden" class="admin-url" value="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>" />
|
||||
<div>
|
||||
<a href="#" class="return-to-admin"><?php printf( __( '← Return to %s' ), get_admin_page_title() ); ?></a>
|
||||
<a href="#" class="collapse-sidebar button-secondary" title="<?php esc_attr_e('Collapse Sidebar'); ?>"><span></span></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the theme and trigger the save action of each setting.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function save() {
|
||||
if ( $this->is_preview() )
|
||||
return;
|
||||
|
||||
check_admin_referer( 'customize_controls' );
|
||||
|
||||
if ( ! $this->set_stylesheet() )
|
||||
return;
|
||||
|
||||
$active_template = get_template();
|
||||
$active_stylesheet = get_stylesheet();
|
||||
|
||||
// Do we have to switch themes?
|
||||
if ( $this->get_template() != $active_template || $this->get_stylesheet() != $active_stylesheet ) {
|
||||
if ( ! current_user_can( 'switch_themes' ) )
|
||||
return;
|
||||
|
||||
switch_theme( $this->get_template(), $this->get_stylesheet() );
|
||||
}
|
||||
|
||||
do_action( 'customize_save' );
|
||||
|
||||
foreach ( $this->settings as $setting ) {
|
||||
$setting->save();
|
||||
}
|
||||
|
||||
add_action( 'admin_notices', array( $this, '_save_feedback' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an admin notice after settings are saved.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function _save_feedback() {
|
||||
?>
|
||||
<div class="updated"><p><?php printf( __( 'Settings saved and theme activated. <a href="%s">Visit site</a>.' ), home_url( '/' ) ); ?></p></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a customize setting.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the setting. Can be a
|
||||
* theme mod or option name.
|
||||
* @param array $args Setting arguments.
|
||||
*/
|
||||
public function add_setting( $id, $args = array() ) {
|
||||
$setting = new WP_Customize_Setting( $id, $args );
|
||||
|
||||
$this->settings[ $setting->id ] = $setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a customize setting.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the setting.
|
||||
* @return object The settings object.
|
||||
*/
|
||||
public function get_setting( $id ) {
|
||||
if ( isset( $this->settings[ $id ] ) )
|
||||
return $this->settings[ $id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a customize setting.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the setting.
|
||||
*/
|
||||
public function remove_setting( $id ) {
|
||||
unset( $this->settings[ $id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a customize section.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the section.
|
||||
* @param array $args Section arguments.
|
||||
*/
|
||||
public function add_section( $id, $args = array() ) {
|
||||
$section = new WP_Customize_Section( $id, $args );
|
||||
|
||||
$this->sections[ $section->id ] = $section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a customize section.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the section.
|
||||
* @return object The section object.
|
||||
*/
|
||||
public function get_section( $id ) {
|
||||
if ( isset( $this->sections[ $id ] ) )
|
||||
return $this->sections[ $id ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a customize section.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param string $id An specific ID of the section.
|
||||
*/
|
||||
public function remove_section( $id ) {
|
||||
unset( $this->sections[ $id ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to compare two objects by priority.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*
|
||||
* @param object $a Object A.
|
||||
* @param object $b Object B.
|
||||
*/
|
||||
protected function _cmp_priority( $a, $b ) {
|
||||
$ap = $a->priority;
|
||||
$bp = $b->priority;
|
||||
|
||||
if ( $ap == $bp )
|
||||
return 0;
|
||||
return ( $ap > $bp ) ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare settings and sections. Also enqueue needed scripts/styles.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function prepare_controls() {
|
||||
// Reversing makes uasort sort by time added when conflicts occur.
|
||||
|
||||
$this->sections = array_reverse( $this->sections );
|
||||
uasort( $this->sections, array( $this, '_cmp_priority' ) );
|
||||
|
||||
$this->settings = array_reverse( $this->settings );
|
||||
foreach ( $this->settings as $setting ) {
|
||||
if ( ! isset( $this->sections[ $setting->section ] ) )
|
||||
continue;
|
||||
|
||||
$this->sections[ $setting->section ]->settings[] = $setting;
|
||||
|
||||
if ( $setting->check_capabilities() )
|
||||
$setting->enqueue();
|
||||
}
|
||||
|
||||
foreach ( $this->sections as $section ) {
|
||||
usort( $section->settings, array( $this, '_cmp_priority' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register some default controls.
|
||||
*
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public function register_controls() {
|
||||
|
||||
/* Custom Header */
|
||||
|
||||
$this->add_section( 'header', array(
|
||||
'title' => __( 'Header' ),
|
||||
'theme_supports' => 'custom-header',
|
||||
) );
|
||||
|
||||
$this->add_setting( 'header_textcolor', array(
|
||||
'label' => 'Text Color',
|
||||
'section' => 'header',
|
||||
'sanitize_callback' => 'sanitize_hexcolor',
|
||||
'control' => 'color',
|
||||
'default' => defined( 'HEADER_TEXTCOLOR' ) ? HEADER_TEXTCOLOR : ''
|
||||
) );
|
||||
|
||||
/*
|
||||
$this->add_setting( 'display_header', array(
|
||||
'label' => 'Display Text',
|
||||
'section' => 'header',
|
||||
'type' => 'radio',
|
||||
'choices' => array(
|
||||
'show' => 'Yes',
|
||||
'hide' => 'No'
|
||||
),
|
||||
// Showing header text is actually done by setting header_textcolor to 'blank'.
|
||||
// @todo: Do some JS magic to make this work (since we'll be hiding the textcolor input).
|
||||
'theme_mod' => false,
|
||||
) );
|
||||
*/
|
||||
|
||||
// Input type: checkbox
|
||||
// With custom value
|
||||
$this->add_setting( 'header_image', array(
|
||||
'label' => 'Random Image',
|
||||
'section' => 'header',
|
||||
'control' => 'checkbox',
|
||||
// @todo
|
||||
// not the default, it's the value.
|
||||
// value is saved in get_theme_support( 'custom-header' )[0][ 'random-default' ]
|
||||
'default' => 'random-default-image'
|
||||
) );
|
||||
|
||||
/* Custom Background */
|
||||
|
||||
$this->add_section( 'background', array(
|
||||
'title' => __( 'Background' ),
|
||||
'theme_supports' => 'custom-background',
|
||||
) );
|
||||
|
||||
// Input type: Color
|
||||
// With sanitize_callback
|
||||
$this->add_setting( 'background_color', array(
|
||||
'label' => 'Background Color',
|
||||
'section' => 'background',
|
||||
'control' => 'color',
|
||||
'default' => defined( 'BACKGROUND_COLOR' ) ? BACKGROUND_COLOR : '',
|
||||
'sanitize_callback' => 'sanitize_hexcolor'
|
||||
) );
|
||||
|
||||
/* Nav Menus */
|
||||
|
||||
$locations = get_registered_nav_menus();
|
||||
$menus = wp_get_nav_menus();
|
||||
$menu_locations = get_nav_menu_locations();
|
||||
$num_locations = count( array_keys( $locations ) );
|
||||
|
||||
$this->add_section( 'nav', array(
|
||||
'title' => __( 'Navigation' ),
|
||||
'theme_supports' => 'menus',
|
||||
'description' => sprintf( _n('Your theme supports %s menu. Select which menu you would like to use.', 'Your theme supports %s menus. Select which menu appears in each location.', $num_locations ), number_format_i18n( $num_locations ) ),
|
||||
) );
|
||||
|
||||
foreach ( $locations as $location => $description ) {
|
||||
$choices = array( 0 => '' );
|
||||
foreach ( $menus as $menu ) {
|
||||
$truncated_name = wp_html_excerpt( $menu->name, 40 );
|
||||
$truncated_name == $menu->name ? $menu->name : trim( $truncated_name ) . '…';
|
||||
$choices[ $menu->term_id ] = $truncated_name;
|
||||
}
|
||||
|
||||
$this->add_setting( "nav_menu_locations[{$location}]", array(
|
||||
'label' => $description,
|
||||
'theme_supports' => 'menus', // Todo: Needs also widgets -- array( 'menus', 'widgets' )
|
||||
'section' => 'nav',
|
||||
'control' => 'select',
|
||||
'choices' => $choices,
|
||||
'sanitize_callback' => 'absint',
|
||||
) );
|
||||
}
|
||||
|
||||
/* Static Front Page */
|
||||
// #WP19627
|
||||
|
||||
$this->add_section( 'static_front_page', array(
|
||||
'title' => __( 'Static Front Page' ),
|
||||
// 'theme_supports' => 'static-front-page',
|
||||
'description' => __( 'Your theme supports a static front page.' ),
|
||||
) );
|
||||
|
||||
$choices = array();
|
||||
$choices['posts'] = __( 'Your latest posts' );
|
||||
$choices['page'] = __( 'A static page (select below)' );
|
||||
|
||||
$this->add_setting( 'show_on_front', array(
|
||||
'label' => __( 'Front page displays' ),
|
||||
// 'theme_supports' => 'static-front-page',
|
||||
'section' => 'static_front_page',
|
||||
'control' => 'radio',
|
||||
'choices' => $choices,
|
||||
'default' => get_option( 'show_on_front' ),
|
||||
'type' => 'option',
|
||||
'capability' => 'manage_options'
|
||||
) );
|
||||
|
||||
$this->add_setting( 'page_on_front', array(
|
||||
'label' => __( 'Front page:' ),
|
||||
// 'theme_supports' => 'static-front-page',
|
||||
'section' => 'static_front_page',
|
||||
'control' => 'dropdown-pages',
|
||||
'type' => 'option',
|
||||
'capability' => 'manage_options'
|
||||
) );
|
||||
|
||||
$this->add_setting( 'page_for_posts', array(
|
||||
'label' => __( 'Posts page:' ),
|
||||
// 'theme_supports' => 'static-front-page',
|
||||
'section' => 'static_front_page',
|
||||
'control' => 'dropdown-pages',
|
||||
'type' => 'option',
|
||||
'capability' => 'manage_options'
|
||||
) );
|
||||
|
||||
/* Site Title & Tagline */
|
||||
|
||||
$this->add_section( 'strings', array(
|
||||
'title' => __( 'Site Title & Tagline' ),
|
||||
'description' => __( 'Customize some strings.' ),
|
||||
) );
|
||||
|
||||
$this->add_setting( 'blogname', array(
|
||||
'label' => __( 'Site Title' ),
|
||||
'section' => 'strings',
|
||||
'default' => get_option( 'blogname' ),
|
||||
'type' => 'option',
|
||||
'capability' => 'manage_options'
|
||||
) );
|
||||
|
||||
$this->add_setting( 'blogdescription', array(
|
||||
'label' => __( 'Tagline' ),
|
||||
'section' => 'strings',
|
||||
'default' => get_option( 'blogdescription' ),
|
||||
'type' => 'option',
|
||||
'capability' => 'manage_options'
|
||||
) );
|
||||
}
|
||||
};
|
||||
|
||||
// Callback function for sanitizing a hex color
|
||||
function sanitize_hexcolor( $color ) {
|
||||
$color = preg_replace( '/[^0-9a-fA-F]/', '', $color );
|
||||
|
||||
if ( preg_match('|[A-Fa-f0-9]{3,6}|', $color ) )
|
||||
return $color;
|
||||
|
||||
return $color;
|
||||
}
|
||||
|
||||
// Custome render type for a dropdown menu.
|
||||
function customize_control_dropdown_pages( $setting ) {
|
||||
printf(
|
||||
__( '<label>%s %s</label>' ),
|
||||
$setting->label,
|
||||
wp_dropdown_pages(
|
||||
array(
|
||||
'name' => $setting->get_name(),
|
||||
'echo' => 0,
|
||||
'show_option_none' => __( '— Select —' ),
|
||||
'option_none_value' => '0',
|
||||
'selected' => get_option( $setting->id )
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
add_action( 'customize_render_control-dropdown-pages', 'customize_control_dropdown_pages' );
|
|
@ -0,0 +1,216 @@
|
|||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.customize-section {
|
||||
border-top: 1px solid #fff;
|
||||
border-bottom: 1px solid #dfdfdf;
|
||||
padding: 15px 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.customize-section:last-child {
|
||||
box-shadow: 0 1px 0 0px #fff;
|
||||
}
|
||||
|
||||
#customize-controls {
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
background: #f5f5f5;
|
||||
box-shadow: inset -11px 0 8px -8px rgba( 0, 0, 0, 0.1 );
|
||||
border-right: 1px solid rgba( 0, 0, 0, 0.2 );
|
||||
}
|
||||
|
||||
#customize-controls .theme-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#customize-controls .theme-screenshot {
|
||||
width: 258px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
#customize-controls .submit {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#customize-info {
|
||||
padding-top: 55px;
|
||||
}
|
||||
|
||||
#customize-theme-controls {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
||||
#customize-theme-controls ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#customize-theme-controls ul ul {
|
||||
margin-top: 1em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#customize-theme-controls ul h3 {
|
||||
margin: 0;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#customize-theme-controls ul h3:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#customize-theme-controls ul h3:after {
|
||||
content: '';
|
||||
border-color: #ccc transparent transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
float: right;
|
||||
margin-top: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#customize-theme-controls ul h3.open:after {
|
||||
-moz-transform: rotate(180deg);
|
||||
-webkit-transform: rotate(180deg);
|
||||
-o-transform: rotate(180deg);
|
||||
-ms-transform: rotate(180deg);
|
||||
transform: rotate(180deg);
|
||||
-moz-transform-origin: center 3px;
|
||||
-webkit-transform-origin: center 3px;
|
||||
-o-transform-origin: center 3px;
|
||||
-ms-transform-origin: center 3px;
|
||||
transform-origin: center 3px;
|
||||
}
|
||||
|
||||
#customize-footer {
|
||||
border-bottom: 0;
|
||||
border-top: 1px solid #dfdfdf;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 260px;
|
||||
padding: 15px 20px;
|
||||
margin: 0;
|
||||
z-index: 10;
|
||||
background: #f5f5f5;
|
||||
box-shadow:
|
||||
inset -11px 0 8px -8px rgba( 0, 0, 0, 0.1 ),
|
||||
inset 0 1px 0 0px #fff,
|
||||
0 0 11px 0 rgba( 0, 0, 0, 0.1 );
|
||||
}
|
||||
|
||||
#customize-preview {
|
||||
position: fixed;
|
||||
left: 300px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
#customize-preview iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.customize-loader {
|
||||
background: transparent;
|
||||
border: 4px solid #666;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
margin: 10px auto;
|
||||
text-indent: -9999px;
|
||||
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
|
||||
/* Animation */
|
||||
-webkit-animation: customize-loader 1s infinite ease-out;
|
||||
-moz-animation: customize-loader 1s infinite ease-out;
|
||||
animation: customize-loader 1s infinite ease-out;
|
||||
}
|
||||
@-moz-keyframes customize-loader {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-moz-transform: scale(0.7);
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
-moz-transform: scale(1);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes customize-loader {
|
||||
0% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(0.7);
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
-webkit-transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes customize-loader {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.7);
|
||||
}
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Style for custom settings
|
||||
*/
|
||||
.customize-section select {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.customize-section .hex-prepend {
|
||||
float: left;
|
||||
display: block;
|
||||
margin: 1px -2px 0 0;
|
||||
line-height: 15px;
|
||||
padding: 3px 5px;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dfdfdf;
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
|
||||
.customize-section input[type="text"].hex-input {
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.customize-section .hex-color-example {
|
||||
border: 1px solid #dfdfdf;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
padding: 3px 14px;
|
||||
background-color: #ef45da
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
body.customize-active {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#customize-container {
|
||||
display: none;
|
||||
background: #fff;
|
||||
z-index: 500000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.customize-active #customize-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#customize-container.collapsed {
|
||||
left: -302px;
|
||||
}
|
||||
|
||||
#customize-container .collapse-sidebar,
|
||||
#customize-container .return-to-admin {
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
#customize-container div {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 260px;
|
||||
padding: 15px 20px;
|
||||
background: #f5f5f5;
|
||||
box-shadow:
|
||||
inset -11px 0 8px -8px rgba( 0, 0, 0, 0.1 ),
|
||||
inset 0 -1px 0 #dfdfdf,
|
||||
-1px 1px 0 #fff;
|
||||
border-right: 1px solid rgba( 0, 0, 0, 0.2 );
|
||||
}
|
||||
|
||||
#customize-container iframe {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
/* Collapse Button */
|
||||
#customize-container .collapse-sidebar {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 265px;
|
||||
z-index: 50;
|
||||
display: block;
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
#customize-container.collapsed .collapse-sidebar {
|
||||
position: absolute;
|
||||
left: 315px;
|
||||
}
|
||||
#customize-container .collapse-sidebar span {
|
||||
margin-top: 2px;
|
||||
margin-left: 2px;
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: transparent url('../../wp-admin/images/arrows.png') no-repeat 0 -72px;
|
||||
}
|
||||
#customize-container.collapsed .collapse-sidebar span {
|
||||
background-position: 0 -108px;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
#customize-container,
|
||||
#customize-container .collapse-sidebar {
|
||||
-moz-transition-property: left, right, top, bottom;
|
||||
-webkit-transition-property: left, right, top, bottom;
|
||||
-o-transition-property: left, right, top, bottom;
|
||||
-ms-transition-property: left, right, top, bottom;
|
||||
transition-property: left, right, top, bottom;
|
||||
-moz-transition-duration: 0.2s;
|
||||
-webkit-transition-duration: 0.2s;
|
||||
-o-transition-duration: 0.2s;
|
||||
-ms-transition-duration: 0.2s;
|
||||
transition-duration: 0.2s;
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
/**
|
||||
* Customize Controls
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Customize
|
||||
* @since 3.4.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) )
|
||||
die;
|
||||
|
||||
global $wp_scripts;
|
||||
|
||||
$registered = $wp_scripts->registered;
|
||||
$wp_scripts = new WP_Scripts;
|
||||
$wp_scripts->registered = $registered;
|
||||
|
||||
add_action( 'customize_controls_print_scripts', 'print_head_scripts', 20 );
|
||||
add_action( 'customize_controls_print_footer_scripts', '_wp_footer_scripts' );
|
||||
add_action( 'customize_controls_print_styles', 'print_admin_styles', 20 );
|
||||
|
||||
do_action( 'customize_controls_init' );
|
||||
|
||||
wp_enqueue_script( 'customize-controls' );
|
||||
wp_enqueue_style( 'customize-controls' );
|
||||
|
||||
do_action( 'customize_controls_enqueue_scripts' );
|
||||
|
||||
$theme = get_theme( get_current_theme() );
|
||||
|
||||
if ( $theme['Screenshot'] )
|
||||
$screenshot = $theme['Theme Root URI'] . '/' . $theme['Stylesheet'] . '/' . $theme['Screenshot'];
|
||||
else
|
||||
$screenshot = '';
|
||||
|
||||
// Let's roll.
|
||||
@header('Content-Type: ' . get_option('html_type') . '; charset=' . get_option('blog_charset'));
|
||||
|
||||
wp_user_settings();
|
||||
_wp_admin_html_begin();
|
||||
|
||||
$admin_title = sprintf( __( '%1$s — WordPress' ), strip_tags( sprintf( __( 'Customize %s' ), $theme['Name'] ) ) );
|
||||
?><title><?php echo $admin_title; ?></title><?php
|
||||
|
||||
do_action( 'customize_controls_print_styles' );
|
||||
do_action( 'customize_controls_print_scripts' );
|
||||
?>
|
||||
</head>
|
||||
<body>
|
||||
<form id="customize-controls" method="post" class="wrap" target="_parent" action="<?php echo esc_url( add_query_arg( 'save', '1', admin_url( 'themes.php' ) ) ); ?>">
|
||||
<?php wp_nonce_field( 'customize_controls' ); ?>
|
||||
<input type="hidden" name="customize" value="on" />
|
||||
<input type="hidden" id="customize-template" name="template" value="<?php echo esc_attr( $theme['Template'] ); ?>" />
|
||||
<input type="hidden" id="customize-stylesheet" name="stylesheet" value="<?php echo esc_attr( $theme['Stylesheet'] ); ?>" />
|
||||
|
||||
<div id="customize-info" class="customize-section">
|
||||
<p>
|
||||
<strong class="theme-name"><?php echo $theme['Name']; ?></strong>
|
||||
<span class="theme-by"><?php printf( __( 'By %s' ), $theme['Author'] ); ?></span>
|
||||
</p>
|
||||
<?php if ( $screenshot ) : ?>
|
||||
<img class="theme-screenshot" src="<?php echo esc_url( $screenshot ); ?>" />
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div id="customize-theme-controls"><ul>
|
||||
<?php
|
||||
foreach ( $this->sections as $section )
|
||||
$section->render();
|
||||
?>
|
||||
</ul></div>
|
||||
|
||||
<div id="customize-footer" class="customize-section">
|
||||
<?php
|
||||
submit_button( __( 'Refresh' ), 'secondary', 'refresh', false );
|
||||
submit_button( __( 'Save' ), 'primary', 'save', false );
|
||||
?>
|
||||
</div>
|
||||
</form>
|
||||
<div id="customize-preview">
|
||||
<iframe name="customize-target"></iframe>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
do_action( 'customize_controls_print_footer_scripts' );
|
||||
|
||||
$settings = array(
|
||||
'preview' => esc_url( home_url( '/' ) ),
|
||||
'values' => array(),
|
||||
'prefix' => WP_Customize_Setting::name_prefix,
|
||||
);
|
||||
|
||||
foreach ( $this->settings as $id => $setting ) {
|
||||
$settings['values'][ $id ] = $setting->value();
|
||||
}
|
||||
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
if ( typeof wp === 'undefined' || ! wp.customize )
|
||||
return;
|
||||
|
||||
wp.customize.settings = <?php echo json_encode( $settings ); ?>;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,496 @@
|
|||
if ( typeof wp === 'undefined' )
|
||||
var wp = {};
|
||||
|
||||
(function( exports, $ ){
|
||||
var api, extend, ctor, inherits, ready,
|
||||
slice = Array.prototype.slice;
|
||||
|
||||
/* =====================================================================
|
||||
* Micro-inheritance - thank you, backbone.js.
|
||||
* ===================================================================== */
|
||||
|
||||
extend = function( protoProps, classProps ) {
|
||||
var child = inherits( this, protoProps, classProps );
|
||||
child.extend = this.extend;
|
||||
return child;
|
||||
};
|
||||
|
||||
// Shared empty constructor function to aid in prototype-chain creation.
|
||||
ctor = function() {};
|
||||
|
||||
// Helper function to correctly set up the prototype chain, for subclasses.
|
||||
// Similar to `goog.inherits`, but uses a hash of prototype properties and
|
||||
// class properties to be extended.
|
||||
inherits = function( parent, protoProps, staticProps ) {
|
||||
var child;
|
||||
|
||||
// The constructor function for the new subclass is either defined by you
|
||||
// (the "constructor" property in your `extend` definition), or defaulted
|
||||
// by us to simply call `super()`.
|
||||
if ( protoProps && protoProps.hasOwnProperty( 'constructor' ) ) {
|
||||
child = protoProps.constructor;
|
||||
} else {
|
||||
child = function() {
|
||||
// Storing the result `super()` before returning the value
|
||||
// prevents a bug in Opera where, if the constructor returns
|
||||
// a function, Opera will reject the return value in favor of
|
||||
// the original object. This causes all sorts of trouble.
|
||||
var result = parent.apply( this, arguments );
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// Inherit class (static) properties from parent.
|
||||
$.extend( child, parent );
|
||||
|
||||
// Set the prototype chain to inherit from `parent`, without calling
|
||||
// `parent`'s constructor function.
|
||||
ctor.prototype = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
|
||||
// Add prototype properties (instance properties) to the subclass,
|
||||
// if supplied.
|
||||
if ( protoProps )
|
||||
$.extend( child.prototype, protoProps );
|
||||
|
||||
// Add static properties to the constructor function, if supplied.
|
||||
if ( staticProps )
|
||||
$.extend( child, staticProps );
|
||||
|
||||
// Correctly set child's `prototype.constructor`.
|
||||
child.prototype.constructor = child;
|
||||
|
||||
// Set a convenience property in case the parent's prototype is needed later.
|
||||
child.__super__ = parent.prototype;
|
||||
|
||||
return child;
|
||||
};
|
||||
|
||||
/* =====================================================================
|
||||
* customize function.
|
||||
* ===================================================================== */
|
||||
ready = $.Callbacks( 'once memory' );
|
||||
|
||||
/*
|
||||
* Sugar for main customize function. Supports several signatures.
|
||||
*
|
||||
* customize( callback, [context] );
|
||||
* Binds a callback to be fired when the customizer is ready.
|
||||
* - callback, function
|
||||
* - context, object
|
||||
*
|
||||
* customize( setting );
|
||||
* Fetches a setting object by ID.
|
||||
* - setting, string - The setting ID.
|
||||
*
|
||||
*/
|
||||
api = {};
|
||||
// api = function( callback, context ) {
|
||||
// if ( $.isFunction( callback ) ) {
|
||||
// if ( context )
|
||||
// callback = $.proxy( callback, context );
|
||||
// ready.add( callback );
|
||||
//
|
||||
// return api;
|
||||
// }
|
||||
// }
|
||||
|
||||
/* =====================================================================
|
||||
* Base class.
|
||||
* ===================================================================== */
|
||||
|
||||
api.Class = function( applicator, argsArray, options ) {
|
||||
var magic, args = arguments;
|
||||
|
||||
if ( applicator && argsArray && api.Class.applicator === applicator ) {
|
||||
args = argsArray;
|
||||
$.extend( this, options || {} );
|
||||
}
|
||||
|
||||
magic = this;
|
||||
if ( this.instance ) {
|
||||
magic = function() {
|
||||
return magic.instance.apply( magic, arguments );
|
||||
};
|
||||
|
||||
$.extend( magic, this );
|
||||
}
|
||||
|
||||
magic.initialize.apply( magic, args );
|
||||
return magic;
|
||||
};
|
||||
|
||||
api.Class.applicator = {};
|
||||
|
||||
api.Class.prototype.initialize = function() {};
|
||||
|
||||
/*
|
||||
* Checks whether a given instance extended a constructor.
|
||||
*
|
||||
* The magic surrounding the instance parameter causes the instanceof
|
||||
* keyword to return inaccurate results; it defaults to the function's
|
||||
* prototype instead of the constructor chain. Hence this function.
|
||||
*/
|
||||
api.Class.prototype.extended = function( constructor ) {
|
||||
var proto = this;
|
||||
|
||||
while ( typeof proto.constructor !== 'undefined' ) {
|
||||
if ( proto.constructor === constructor )
|
||||
return true;
|
||||
if ( typeof proto.constructor.__super__ === 'undefined' )
|
||||
return false;
|
||||
proto = proto.constructor.__super__;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
api.Class.extend = extend;
|
||||
|
||||
/* =====================================================================
|
||||
* Light two-way binding.
|
||||
* ===================================================================== */
|
||||
|
||||
api.Value = api.Class.extend({
|
||||
initialize: function( initial, options ) {
|
||||
this._value = initial;
|
||||
this.callbacks = $.Callbacks();
|
||||
|
||||
$.extend( this, options || {} );
|
||||
},
|
||||
|
||||
/*
|
||||
* Magic. Returns a function that will become the instance.
|
||||
* Set to null to prevent the instance from extending a function.
|
||||
*/
|
||||
instance: function() {
|
||||
return arguments.length ? this.set.apply( this, arguments ) : this.get();
|
||||
},
|
||||
|
||||
get: function() {
|
||||
return this._value;
|
||||
},
|
||||
|
||||
set: function( to ) {
|
||||
var from = this._value;
|
||||
|
||||
to = this.validate( to );
|
||||
|
||||
// Bail if the sanitized value is null or unchanged.
|
||||
if ( null === to || this._value === to )
|
||||
return this;
|
||||
|
||||
this._value = to;
|
||||
|
||||
this.callbacks.fireWith( this, [ to, from ] );
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
validate: function( value ) {
|
||||
return value;
|
||||
},
|
||||
|
||||
bind: function( callback ) {
|
||||
this.callbacks.add.apply( this.callbacks, arguments );
|
||||
return this;
|
||||
},
|
||||
|
||||
unbind: function( callback ) {
|
||||
this.callbacks.remove.apply( this.callbacks, arguments );
|
||||
return this;
|
||||
},
|
||||
|
||||
/*
|
||||
* Allows the creation of composite values.
|
||||
* Overrides the native link method (can be reverted with `unlink`).
|
||||
*/
|
||||
link: function() {
|
||||
var keys = slice.call( arguments ),
|
||||
callback = keys.pop(),
|
||||
self = this,
|
||||
set, key, active;
|
||||
|
||||
if ( this.links )
|
||||
this.unlink();
|
||||
|
||||
this.links = [];
|
||||
|
||||
// Single argument means a direct binding.
|
||||
if ( ! keys.length ) {
|
||||
keys = [ callback ];
|
||||
callback = function( value, to ) {
|
||||
return to;
|
||||
};
|
||||
}
|
||||
|
||||
while ( key = keys.shift() ) {
|
||||
if ( this._parent && $.type( key ) == 'string' )
|
||||
this.links.push( this._parent[ key ] );
|
||||
else
|
||||
this.links.push( key );
|
||||
}
|
||||
|
||||
// Replace this.set with the assignment function.
|
||||
set = function() {
|
||||
var args, result;
|
||||
|
||||
// If we call set from within the assignment function,
|
||||
// pass the arguments to the original set.
|
||||
if ( active )
|
||||
return self.set.original.apply( self, arguments );
|
||||
|
||||
active = true;
|
||||
|
||||
args = self.links.concat( slice.call( arguments ) );
|
||||
result = callback.apply( self, args );
|
||||
|
||||
active = false;
|
||||
|
||||
if ( typeof result !== 'undefined' )
|
||||
self.set.original.call( self, result );
|
||||
};
|
||||
|
||||
set.original = this.set;
|
||||
this.set = set;
|
||||
|
||||
// Bind the new function to the master values.
|
||||
$.each( this.links, function( key, value ) {
|
||||
value.bind( self.set );
|
||||
});
|
||||
|
||||
this.set( this.get() );
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
unlink: function() {
|
||||
var set = this.set;
|
||||
|
||||
$.each( this.links, function( key, value ) {
|
||||
value.unbind( set );
|
||||
});
|
||||
|
||||
delete this.links;
|
||||
this.set = this.set.original;
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
api.ensure = function( element ) {
|
||||
return typeof element == 'string' ? $( element ) : element;
|
||||
};
|
||||
|
||||
sync = {
|
||||
'val': {
|
||||
update: function() {
|
||||
this.element[ this._updater ]( this._value );
|
||||
},
|
||||
refresh: function() {
|
||||
this.set( this.element[ this._refresher ]() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
api.Element = api.Value.extend({
|
||||
initialize: function( element, options ) {
|
||||
var synchronizer = api.Element.synchronizer.html,
|
||||
type;
|
||||
|
||||
this.element = api.ensure( element );
|
||||
this.events = '';
|
||||
|
||||
if ( this.element.is('input, select, textarea') ) {
|
||||
this.events += 'change';
|
||||
synchronizer = api.Element.synchronizer.val;
|
||||
|
||||
if ( this.element.is('input') ) {
|
||||
type = this.element.prop('type');
|
||||
if ( api.Element.synchronizer[ type ] )
|
||||
synchronizer = api.Element.synchronizer[ type ];
|
||||
if ( 'text' === type || 'password' === type )
|
||||
this.events += ' keyup';
|
||||
}
|
||||
}
|
||||
|
||||
api.Value.prototype.initialize.call( this, null, $.extend( options || {}, synchronizer ) );
|
||||
this._value = this.get();
|
||||
|
||||
this.bind( this.update );
|
||||
|
||||
this.refresh = $.proxy( this.refresh, this );
|
||||
this.element.bind( this.events, this.refresh );
|
||||
},
|
||||
|
||||
find: function( selector ) {
|
||||
return $( selector, this.element );
|
||||
},
|
||||
|
||||
refresh: function() {},
|
||||
update: function() {}
|
||||
});
|
||||
|
||||
api.Element.synchronizer = {};
|
||||
|
||||
$.each( [ 'html', 'val' ], function( i, method ) {
|
||||
api.Element.synchronizer[ method ] = {
|
||||
update: function( to ) {
|
||||
this.element[ method ]( to );
|
||||
},
|
||||
refresh: function() {
|
||||
this.set( this.element[ method ]() );
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
api.Element.synchronizer.checkbox = {
|
||||
update: function( to ) {
|
||||
this.element.prop( 'checked', to );
|
||||
},
|
||||
refresh: function() {
|
||||
this.set( this.element.prop( 'checked' ) );
|
||||
}
|
||||
};
|
||||
|
||||
api.Element.synchronizer.radio = {
|
||||
update: function( to ) {
|
||||
this.element.filter( function() {
|
||||
return this.value === to;
|
||||
}).prop( 'checked', true );
|
||||
},
|
||||
refresh: function() {
|
||||
this.set( this.element.filter( ':checked' ).val() );
|
||||
}
|
||||
};
|
||||
|
||||
api.ValueFactory = function( constructor ) {
|
||||
constructor = constructor || api.Value;
|
||||
|
||||
return function( key ) {
|
||||
var args = slice.call( arguments, 1 );
|
||||
this[ key ] = new constructor( api.Class.applicator, args );
|
||||
this[ key ]._parent = this;
|
||||
return this[ key ];
|
||||
};
|
||||
};
|
||||
|
||||
api.Values = api.Value.extend({
|
||||
defaultConstructor: api.Value,
|
||||
|
||||
initialize: function( options ) {
|
||||
api.Value.prototype.initialize.call( this, {}, options || {} );
|
||||
},
|
||||
|
||||
instance: function( id ) {
|
||||
return this.value( id );
|
||||
},
|
||||
|
||||
value: function( id ) {
|
||||
return this._value[ id ];
|
||||
},
|
||||
|
||||
has: function( id ) {
|
||||
return typeof this._value[ id ] !== 'undefined';
|
||||
},
|
||||
|
||||
add: function( id, value ) {
|
||||
if ( this.has( id ) )
|
||||
return;
|
||||
|
||||
this._value[ id ] = value;
|
||||
this._value[ id ]._parent = this._value;
|
||||
return this._value[ id ];
|
||||
},
|
||||
|
||||
set: function( id ) {
|
||||
if ( this.has( id ) )
|
||||
return this.pass( 'set', arguments );
|
||||
|
||||
return this.add( id, new this.defaultConstructor( api.Class.applicator, slice.call( arguments, 1 ) ) );
|
||||
},
|
||||
|
||||
remove: function( id ) {
|
||||
delete this._value[ id ];
|
||||
},
|
||||
|
||||
pass: function( fn, args ) {
|
||||
var id, value;
|
||||
|
||||
args = slice.call( args );
|
||||
id = args.shift();
|
||||
|
||||
if ( ! this.has( id ) )
|
||||
return;
|
||||
|
||||
value = this.value( id );
|
||||
return value[ fn ].apply( value, args );
|
||||
}
|
||||
});
|
||||
|
||||
$.each( [ 'get', 'bind', 'unbind', 'link', 'unlink' ], function( i, method ) {
|
||||
api.Values.prototype[ method ] = function() {
|
||||
return this.pass( method, arguments );
|
||||
};
|
||||
});
|
||||
|
||||
/* =====================================================================
|
||||
* Messenger for postMessage.
|
||||
* ===================================================================== */
|
||||
|
||||
api.Messenger = api.Class.extend({
|
||||
add: api.ValueFactory(),
|
||||
|
||||
initialize: function( url, options ) {
|
||||
$.extend( this, options || {} );
|
||||
|
||||
this.add( 'url', url );
|
||||
this.add( 'origin' ).link( 'url', function( url ) {
|
||||
return url().replace( /([^:]+:\/\/[^\/]+).*/, '$1' );
|
||||
});
|
||||
|
||||
this.topics = {};
|
||||
|
||||
$.receiveMessage( $.proxy( this.receive, this ), this.origin() || null );
|
||||
},
|
||||
receive: function( event ) {
|
||||
var message;
|
||||
|
||||
console.log( 'messenger receiveMessage', arguments );
|
||||
|
||||
// @todo: remove, this is done in the postMessage plugin.
|
||||
// if ( this.origin && event.origin !== this.origin )
|
||||
// return;
|
||||
|
||||
message = JSON.parse( event.data );
|
||||
|
||||
if ( message && message.id && message.data && this.topics[ message.id ] )
|
||||
this.topics[ message.id ].fireWith( this, [ message.data ]);
|
||||
},
|
||||
send: function( id, data ) {
|
||||
var message;
|
||||
|
||||
if ( ! this.url() )
|
||||
return;
|
||||
|
||||
console.log( 'sending message', id, data );
|
||||
message = JSON.stringify({ id: id, data: data });
|
||||
$.postMessage( message, this.url(), this.targetWindow || null );
|
||||
},
|
||||
bind: function( id, callback ) {
|
||||
var topic = this.topics[ id ] || ( this.topics[ id ] = $.Callbacks() );
|
||||
topic.add( callback );
|
||||
},
|
||||
unbind: function( id, callback ) {
|
||||
if ( this.topics[ id ] )
|
||||
this.topics[ id ].remove( callback );
|
||||
}
|
||||
});
|
||||
|
||||
/* =====================================================================
|
||||
* Core customize object.
|
||||
* ===================================================================== */
|
||||
|
||||
api = $.extend( new api.Values(), api );
|
||||
|
||||
// Expose the API to the world.
|
||||
exports.customize = api;
|
||||
})( wp, jQuery );
|
|
@ -0,0 +1,101 @@
|
|||
(function( exports, $ ){
|
||||
var api = wp.customize;
|
||||
|
||||
api.Previewer = api.Messenger.extend({
|
||||
/**
|
||||
* Requires params:
|
||||
* - iframe - a selector or jQuery element
|
||||
* - form - a selector or jQuery element
|
||||
* - url - the URL of preview frame
|
||||
*/
|
||||
initialize: function( params, options ) {
|
||||
$.extend( this, options || {} );
|
||||
|
||||
this.iframe = api.ensure( params.iframe );
|
||||
this.form = api.ensure( params.form );
|
||||
|
||||
api.Messenger.prototype.initialize.call( this, params.url, {
|
||||
targetWindow: this.iframe[0].contentWindow
|
||||
});
|
||||
|
||||
this._formOriginalProps = {
|
||||
target: this.form.prop('target'),
|
||||
action: this.form.prop('action')
|
||||
};
|
||||
|
||||
this.bind( 'url', function( url ) {
|
||||
// Bail if we're navigating to the current url, to a different origin, or wp-admin.
|
||||
if ( this.url() == url || 0 !== url.indexOf( this.origin() + '/' ) || -1 !== url.indexOf( 'wp-admin' ) )
|
||||
return;
|
||||
|
||||
this.url( url );
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
this.refresh();
|
||||
},
|
||||
refresh: function() {
|
||||
this.submit({
|
||||
target: this.iframe.prop('name'),
|
||||
action: this.url()
|
||||
});
|
||||
},
|
||||
submit: function( props ) {
|
||||
if ( props )
|
||||
this.form.prop( props );
|
||||
this.form.submit();
|
||||
if ( props )
|
||||
this.form.prop( this._formOriginalProps );
|
||||
}
|
||||
});
|
||||
|
||||
/* =====================================================================
|
||||
* Ready.
|
||||
* ===================================================================== */
|
||||
|
||||
$( function() {
|
||||
var previewer,
|
||||
controls = $('[name^="' + api.settings.prefix + '"]');
|
||||
|
||||
// Initialize Previewer
|
||||
previewer = new api.Previewer({
|
||||
iframe: '#customize-preview iframe',
|
||||
form: '#customize-controls',
|
||||
url: api.settings.preview
|
||||
});
|
||||
|
||||
$.each( api.settings.values, function( id, value ) {
|
||||
var elements = controls.filter( '[name="' + api.settings.prefix + id + '"]' ),
|
||||
setting = api.set( id, value );
|
||||
|
||||
setting.control = new wp.customize.Element( elements );
|
||||
|
||||
setting.control.link( setting );
|
||||
setting.link( setting.control );
|
||||
});
|
||||
|
||||
// Temporary accordion code.
|
||||
$('.control-section h3').click( function() {
|
||||
$( this ).siblings('ul').slideToggle( 200 );
|
||||
$( this ).toggleClass( 'open' );
|
||||
return false;
|
||||
});
|
||||
|
||||
// Button bindings.
|
||||
$('#save').click( function() {
|
||||
previewer.submit();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#refresh').click( function() {
|
||||
previewer.refresh();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Fetch prefixed settings.
|
||||
$('[name^="' + api.settings.prefix + '"]').each( function() {
|
||||
// console.log( this.name );
|
||||
});
|
||||
});
|
||||
|
||||
})( wp, jQuery );
|
|
@ -0,0 +1,75 @@
|
|||
if ( typeof wp === 'undefined' )
|
||||
var wp = {};
|
||||
|
||||
(function( exports, $ ){
|
||||
var Loader = {
|
||||
initialize: function() {
|
||||
this.body = $( document.body );
|
||||
this.element = $( '#customize-container' );
|
||||
this.base = $( '.admin-url', this.element ).val();
|
||||
this.doc_title = $( document ).attr( 'title' );
|
||||
|
||||
this.element.on( 'click', '.return-to-admin', function() {
|
||||
Loader.close();
|
||||
return false;
|
||||
});
|
||||
|
||||
this.element.on( 'click', '.collapse-sidebar', function() {
|
||||
Loader.element.toggleClass('collapsed');
|
||||
return false;
|
||||
});
|
||||
},
|
||||
open: function( params ) {
|
||||
params.customize = 'on';
|
||||
|
||||
this.element.append( '<iframe />' );
|
||||
this.iframe = $( 'iframe' ).attr( 'src', this.base + '?' + jQuery.param( params ) );
|
||||
|
||||
$('iframe').load( function() {
|
||||
title = $(this).contents().find( 'title' ).text();
|
||||
$( document ).attr( 'title', title );
|
||||
});
|
||||
|
||||
this.element.fadeIn( 200, function() {
|
||||
Loader.body.addClass( 'customize-active' );
|
||||
});
|
||||
},
|
||||
close: function() {
|
||||
this.element.fadeOut( 200, function() {
|
||||
Loader.iframe.remove();
|
||||
Loader.iframe = null;
|
||||
Loader.body.removeClass( 'customize-active' );
|
||||
$( document ).attr( 'title', Loader.doc_title );
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$( function() {
|
||||
Loader.initialize();
|
||||
|
||||
// Override 'preview' links on themes page.
|
||||
$('.thickbox-preview').click( function( event ) {
|
||||
var href, template, stylesheet;
|
||||
|
||||
// Stop the thickbox.
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// Extract the template/stylesheet from the preview link's url.
|
||||
href = $(this).attr('href');
|
||||
template = href.match('template=([^&]*)')[1];
|
||||
stylesheet = href.match('stylesheet=([^&]*)')[1];
|
||||
|
||||
// Load the theme.
|
||||
Loader.open({
|
||||
template: template,
|
||||
stylesheet: stylesheet
|
||||
});
|
||||
}).filter( function() {
|
||||
return 'Preview' == $(this).text();
|
||||
}).text('Customize');
|
||||
});
|
||||
|
||||
// Expose the API to the world.
|
||||
exports.CustomizeLoader = Loader;
|
||||
})( wp, jQuery );
|
|
@ -0,0 +1,59 @@
|
|||
(function( exports, $ ){
|
||||
var api = wp.customize;
|
||||
|
||||
api.Preview = api.Messenger.extend({
|
||||
/**
|
||||
* Requires params:
|
||||
* - url - the URL of preview frame
|
||||
*
|
||||
* @todo: Perhaps add a window.onbeforeunload dialog in case the theme
|
||||
* somehow attempts to leave the page and we don't catch it
|
||||
* (which really shouldn't happen).
|
||||
*/
|
||||
initialize: function( url, options ) {
|
||||
var self = this;
|
||||
|
||||
$.extend( this, options || {} );
|
||||
|
||||
api.Messenger.prototype.initialize.call( this, url );
|
||||
|
||||
this.body = $( document.body );
|
||||
this.body.on( 'click.preview', 'a', function( event ) {
|
||||
event.preventDefault();
|
||||
self.send( 'url', $(this).attr('href') );
|
||||
});
|
||||
|
||||
// You cannot submit forms.
|
||||
// @todo: Namespace customizer settings so that we can mix the
|
||||
// $_POST data with the customize setting $_POST data.
|
||||
this.body.on( 'submit.preview', 'form', function( event ) {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
this.bind( 'url', function( url ) {
|
||||
this.url( url );
|
||||
this.refresh();
|
||||
});
|
||||
},
|
||||
refresh: function() {
|
||||
this.submit({
|
||||
target: this.iframe.prop('name'),
|
||||
action: this.url()
|
||||
});
|
||||
},
|
||||
submit: function( props ) {
|
||||
if ( props )
|
||||
this.form.prop( props );
|
||||
this.form.submit();
|
||||
if ( props )
|
||||
this.form.prop( this._formOriginalProps );
|
||||
}
|
||||
});
|
||||
|
||||
$( function() {
|
||||
var preview;
|
||||
|
||||
preview = new api.Preview( api.settings.parent );
|
||||
});
|
||||
|
||||
})( wp, jQuery );
|
|
@ -289,6 +289,11 @@ function wp_default_scripts( &$scripts ) {
|
|||
|
||||
$scripts->add( 'hoverIntent', "/wp-includes/js/hoverIntent$suffix.js", array('jquery'), 'r6', 1 );
|
||||
|
||||
$scripts->add( 'customize-loader', "/wp-includes/js/customize-loader$suffix.js", array( 'jquery' ), false, 1 );
|
||||
$scripts->add( 'customize-base', "/wp-includes/js/customize-base$suffix.js", array( 'jquery-postmessage', 'json2' ), false, 1 );
|
||||
$scripts->add( 'customize-controls', "/wp-includes/js/customize-controls$suffix.js", array( 'customize-base' ), false, 1 );
|
||||
$scripts->add( 'customize-preview', "/wp-includes/js/customize-preview$suffix.js", array( 'customize-base' ), false, 1 );
|
||||
|
||||
if ( is_admin() ) {
|
||||
$scripts->add( 'ajaxcat', "/wp-admin/js/cat$suffix.js", array( 'wp-lists' ) );
|
||||
$scripts->add_data( 'ajaxcat', 'group', 1 );
|
||||
|
@ -464,6 +469,8 @@ function wp_default_styles( &$styles ) {
|
|||
$styles->add( 'wp-jquery-ui-dialog', "/wp-includes/css/jquery-ui-dialog$suffix.css" );
|
||||
$styles->add( 'editor-buttons', "/wp-includes/css/editor-buttons$suffix.css" );
|
||||
$styles->add( 'wp-pointer', "/wp-includes/css/wp-pointer$suffix.css" );
|
||||
$styles->add( 'customize-loader', "/wp-includes/css/customize-loader$suffix.css" );
|
||||
$styles->add( 'customize-controls', "/wp-includes/css/customize-controls$suffix.css", array( 'wp-admin', 'colors', 'ie' ) );
|
||||
|
||||
foreach ( $rtl_styles as $rtl_style ) {
|
||||
$styles->add_data( $rtl_style, 'rtl', true );
|
||||
|
|
|
@ -2082,3 +2082,15 @@ function check_theme_switched() {
|
|||
update_option( 'theme_switched', false );
|
||||
}
|
||||
}
|
||||
|
||||
function wp_customize_load() {
|
||||
// Load on themes.php or ?customize=on
|
||||
if ( ! ( isset( $_REQUEST['customize'] ) && 'on' == $_REQUEST['customize'] ) && 'themes.php' != $GLOBALS['pagenow'] )
|
||||
return;
|
||||
|
||||
require( ABSPATH . WPINC . '/class-wp-customize.php' );
|
||||
// Init Customize class
|
||||
// @todo Dependency injection instead
|
||||
$GLOBALS['customize'] = new WP_Customize;
|
||||
}
|
||||
add_action( 'plugins_loaded', 'wp_customize_load' );
|
||||
|
|
Loading…
Reference in New Issue