<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[19995] trunk/wp-includes: Introduce new theme customizer to replace theme preview.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg > ul, #logmsg > ol { margin-left: 0; margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/19995">19995</a></dd>
<dt>Author</dt> <dd>koopersmith</dd>
<dt>Date</dt> <dd>2012-02-25 04:12:43 +0000 (Sat, 25 Feb 2012)</dd>
</dl>

<h3>Log Message</h3>
<pre>Introduce new theme customizer to replace theme preview. Rough first pass. props koopersmith, ocean90. see <a href="http://core.trac.wordpress.org/ticket/19910">#19910</a>.

Merges in http://plugins.svn.wordpress.org/gandalf/branches/dev/ <a href="http://core.trac.wordpress.org/changeset/510148">rev 510148</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkwpincludesscriptloaderphp">trunk/wp-includes/script-loader.php</a></li>
<li><a href="#trunkwpincludesthemephp">trunk/wp-includes/theme.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkwpincludesclasswpcustomizesectionphp">trunk/wp-includes/class-wp-customize-section.php</a></li>
<li><a href="#trunkwpincludesclasswpcustomizesettingphp">trunk/wp-includes/class-wp-customize-setting.php</a></li>
<li><a href="#trunkwpincludesclasswpcustomizephp">trunk/wp-includes/class-wp-customize.php</a></li>
<li><a href="#trunkwpincludescsscustomizecontrolscss">trunk/wp-includes/css/customize-controls.css</a></li>
<li><a href="#trunkwpincludescsscustomizecontrolsdevcss">trunk/wp-includes/css/customize-controls.dev.css</a></li>
<li><a href="#trunkwpincludescsscustomizeloadercss">trunk/wp-includes/css/customize-loader.css</a></li>
<li><a href="#trunkwpincludescsscustomizeloaderdevcss">trunk/wp-includes/css/customize-loader.dev.css</a></li>
<li><a href="#trunkwpincludescustomizecontrolsphp">trunk/wp-includes/customize-controls.php</a></li>
<li><a href="#trunkwpincludesjscustomizebasedevjs">trunk/wp-includes/js/customize-base.dev.js</a></li>
<li><a href="#trunkwpincludesjscustomizebasejs">trunk/wp-includes/js/customize-base.js</a></li>
<li><a href="#trunkwpincludesjscustomizecontrolsdevjs">trunk/wp-includes/js/customize-controls.dev.js</a></li>
<li><a href="#trunkwpincludesjscustomizecontrolsjs">trunk/wp-includes/js/customize-controls.js</a></li>
<li><a href="#trunkwpincludesjscustomizeloaderdevjs">trunk/wp-includes/js/customize-loader.dev.js</a></li>
<li><a href="#trunkwpincludesjscustomizeloaderjs">trunk/wp-includes/js/customize-loader.js</a></li>
<li><a href="#trunkwpincludesjscustomizepreviewdevjs">trunk/wp-includes/js/customize-preview.dev.js</a></li>
<li><a href="#trunkwpincludesjscustomizepreviewjs">trunk/wp-includes/js/customize-preview.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkwpincludesclasswpcustomizesectionphp"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/class-wp-customize-section.php (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/class-wp-customize-section.php                                (rev 0)
+++ trunk/wp-includes/class-wp-customize-section.php        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,83 @@
</span><ins>+&lt;?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-&gt;id = $id;
+
+                $keys = array_keys( get_class_vars( __CLASS__ ) );
+                foreach ( $keys as $key ) {
+                        if ( isset( $args[ $key ] ) )
+                                $this-&gt;$key = $args[ $key ];
+                }
+
+                $this-&gt;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-&gt;capability || ! current_user_can( $this-&gt;capability ) )
+                        return false;
+
+                if ( $this-&gt;theme_supports &amp;&amp; ! current_theme_supports( $this-&gt;theme_supports ) )
+                        return false;
+
+                return true;
+        }
+
+        /**
+         * Render the section.
+         *
+         * @since 3.4.0
+         */
+        function render() {
+                if ( ! $this-&gt;check_capabilities() )
+                        return;
+                ?&gt;
+                &lt;li id=&quot;customize-section-&lt;?php echo esc_attr( $this-&gt;id ); ?&gt;&quot; class=&quot;control-section customize-section&quot;&gt;
+                        &lt;h3 class=&quot;customize-theme-title&quot;&gt;&lt;?php echo esc_html( $this-&gt;title ); ?&gt;&lt;/h3&gt;
+                        &lt;ul&gt;
+                                &lt;?php if ( $this-&gt;description ) : ?&gt;
+                                        &lt;li&gt;&lt;p class=&quot;howto&quot;&gt;&lt;?php echo $this-&gt;description; ?&gt;&lt;/p&gt;&lt;/li&gt;
+                                &lt;?php endif; ?&gt;
+
+                                &lt;?php foreach ( $this-&gt;settings as $setting ) : ?&gt;
+                                &lt;li&gt;
+                                        &lt;?php $setting-&gt;_render(); ?&gt;
+                                &lt;/li&gt;
+                                &lt;?php endforeach; ?&gt;
+                        &lt;/ul&gt;
+                &lt;/li&gt;
+                &lt;?php
+        }
+}
</ins></span></pre></div>
<a id="trunkwpincludesclasswpcustomizesettingphp"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/class-wp-customize-setting.php (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/class-wp-customize-setting.php                                (rev 0)
+++ trunk/wp-includes/class-wp-customize-setting.php        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,489 @@
</span><ins>+&lt;?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-&gt;$key = $args[ $key ];
+                }
+
+                $this-&gt;id = $id;
+
+                // Parse the ID for array keys.
+                $this-&gt;id_data[ 'keys' ] = preg_split( '/\[/', str_replace( ']', '', $this-&gt;id ) );
+                $this-&gt;id_data[ 'base' ] = array_shift( $this-&gt;id_data[ 'keys' ] );
+
+                // Rebuild the ID.
+                $this-&gt;id = $this-&gt;id_data[ 'base' ];
+                if ( ! empty( $this-&gt;id_data[ 'keys' ] ) )
+                        $this-&gt;id .= '[' . implode( '][', $this-&gt;id_data[ 'keys' ] ) . ']';
+
+                if ( $this-&gt;sanitize_callback != '' )
+                        add_filter( &quot;customize_sanitize_{$this-&gt;id}&quot;, $this-&gt;sanitize_callback );
+
+                return $this;
+        }
+
+        /**
+         * Enqueue setting related scripts/styles.
+         *
+         * @since 3.4.0
+         */
+        public function enqueue() {
+                switch( $this-&gt;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-&gt;type ) {
+                        case 'theme_mod' :
+                                add_filter( 'theme_mod_' . $this-&gt;id_data[ 'base' ], array( $this, '_preview_filter' ) );
+                                break;
+                        case 'option' :
+                                if ( empty( $this-&gt;id_data[ 'keys' ] ) )
+                                        add_filter( 'pre_option_' . $this-&gt;id_data[ 'base' ], array( $this, '_preview_filter' ) );
+                                else
+                                        add_filter( 'option_' . $this-&gt;id_data[ 'base' ], array( $this, '_preview_filter' ) );
+                                break;
+                        default :
+                                do_action( 'customize_preview_' . $this-&gt;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-&gt;multidimensional_replace( $original, $this-&gt;id_data[ 'keys' ], $this-&gt;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-&gt;post_value();
+
+                if ( ! $this-&gt;check_capabilities() || ! isset( $value ) )
+                        return false;
+
+                do_action( 'customize_save_' . $this-&gt;id_data[ 'base' ] );
+
+                $this-&gt;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-&gt;_post_value ) )
+                        return $this-&gt;_post_value;
+
+                $base = self::name_prefix . $this-&gt;id_data[ 'base' ];
+
+                if ( ! isset( $_POST[ $base ] ) )
+                        return $default;
+
+                $result = $this-&gt;multidimensional_get( $_POST[ $base ], $this-&gt;id_data[ 'keys' ] );
+                if ( ! isset( $result ) )
+                        return $default;
+
+                $result = $this-&gt;sanitize( $result );
+                if ( isset( $result ) )
+                        return $this-&gt;_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( &quot;customize_sanitize_{$this-&gt;id}&quot;, $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-&gt;type ) {
+                        case 'theme_mod' :
+                                return $this-&gt;_update_theme_mod( $value );
+                                break;
+                        case 'option' :
+                                return $this-&gt;_update_option( $value );
+                                break;
+                        default :
+                                return do_action( 'customize_update_' . $this-&gt;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-&gt;id_data[ 'keys' ] ) )
+                        return set_theme_mod( $this-&gt;id_data[ 'base' ], $value );
+
+                // Handle array-based theme mod.
+                $mods = get_theme_mod( $this-&gt;id_data[ 'base' ] );
+                $mods = $this-&gt;multidimensional_replace( $mods, $this-&gt;id_data[ 'keys' ], $value );
+                if ( isset( $mods ) )
+                        return set_theme_mod( $this-&gt;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-&gt;id_data[ 'keys' ] ) )
+                        return update_option( $this-&gt;id_data[ 'base' ], $value );
+
+                // Handle array-based options.
+                $options = get_option( $this-&gt;id_data[ 'base' ] );
+                $options = $this-&gt;multidimensional_replace( $options, $this-&gt;id_data[ 'keys' ], $value );
+                if ( isset( $options ) )
+                        return update_option( $this-&gt;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-&gt;type ) {
+                        case 'theme_mod' :
+                                $function = 'get_theme_mod';
+                                break;
+                        case 'option' :
+                                $function = 'get_option';
+                                break;
+                        default :
+                                return apply_filters( 'customize_value_' . $this-&gt;id_data[ 'base' ], $this-&gt;default );
+                }
+
+                // Handle non-array value
+                if ( empty( $this-&gt;id_data[ 'keys' ] ) )
+                        return $function( $this-&gt;id_data[ 'base' ], $this-&gt;default );
+
+                // Handle array-based value
+                $values = $function( $this-&gt;id_data[ 'base' ] );
+                return $this-&gt;multidimensional_get( $values, $this-&gt;id_data[ 'keys' ], $this-&gt;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-&gt;capability || ! current_user_can( $this-&gt;capability ) )
+                        return false;
+
+                if ( $this-&gt;theme_supports &amp;&amp; ! current_theme_supports( $this-&gt;theme_supports ) )
+                        return false;
+
+                $section = $customize-&gt;get_section( $this-&gt;section );
+                if ( isset( $section ) &amp;&amp; ! $section-&gt;check_capabilities() )
+                        return false;
+
+                return true;
+        }
+
+        /**
+         * Render the control.
+         *
+         * @since 3.4.0
+         */
+        public final function _render() {
+                if ( ! $this-&gt;check_capabilities() )
+                        return;
+
+                do_action( 'customize_render_' . $this-&gt;id );
+
+                $this-&gt;render();
+        }
+
+        /**
+         * Render the control.
+         *
+         * @since 3.4.0
+         */
+        protected function render() {
+                $this-&gt;_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-&gt;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=&quot;' . $this-&gt;get_name() . '&quot;';
+        }
+
+        /**
+         * Render the control type.
+         *
+         * @todo Improve value and checked attributes.
+         *
+         * @since 3.4.0
+         */
+        public final function _render_type() {
+                switch( $this-&gt;control ) {
+                        case 'text':
+                                ?&gt;
+                                &lt;label&gt;&lt;?php echo esc_html( $this-&gt;label ); ?&gt;&lt;br/&gt;
+                                        &lt;input type=&quot;text&quot; value=&quot;&lt;?php echo esc_attr( $this-&gt;value() ); ?&gt;&quot; &lt;?php $this-&gt;name(); ?&gt; /&gt;
+                                &lt;/label&gt;
+                                &lt;?php
+                                break;
+                        case 'color':
+                                ?&gt;
+                                &lt;label&gt;&lt;?php echo esc_html( $this-&gt;label ); ?&gt;&lt;br/&gt;
+                                        &lt;span class=&quot;hex-prepend&quot;&gt;#&lt;/span&gt;
+                                        &lt;input type=&quot;text&quot; class=&quot;hex-input&quot; value=&quot;&lt;?php echo esc_attr( $this-&gt;value() ); ?&gt;&quot; &lt;?php $this-&gt;name(); ?&gt; /&gt;
+                                        &lt;a href=&quot;#&quot; class=&quot;pickcolor hex-color-example&quot;&gt;&lt;/a&gt;
+                                &lt;/label&gt;
+                                &lt;?php
+                                break;
+                        case 'checkbox':
+                                ?&gt;
+                                &lt;label&gt;
+                                        &lt;input type=&quot;checkbox&quot; value=&quot;&lt;?php echo esc_attr( $this-&gt;value() ); ?&gt;&quot; &lt;?php $this-&gt;name(); checked( $this-&gt;value() ); ?&gt; /&gt;
+                                        &lt;?php echo esc_html( $this-&gt;label ); ?&gt;
+                                &lt;/label&gt;
+                                &lt;?php
+                                break;
+                        case 'radio':
+                                if ( empty( $this-&gt;choices ) )
+                                        return;
+
+                                echo esc_html( $this-&gt;label ) . '&lt;br/&gt;';
+                                foreach ( $this-&gt;choices as $value =&gt; $label ) :
+                                        ?&gt;
+                                        &lt;label&gt;
+                                                &lt;input type=&quot;radio&quot; value=&quot;&lt;?php echo esc_attr( $value ); ?&gt;&quot; &lt;?php $this-&gt;name(); checked( $this-&gt;value(), $value ); ?&gt; /&gt;
+                                                &lt;?php echo esc_html( $label ); ?&gt;&lt;br/&gt;
+                                        &lt;/label&gt;
+                                        &lt;?php
+                                endforeach;
+                                break;
+                        case 'select':
+                                if ( empty( $this-&gt;choices ) )
+                                        return;
+
+                                ?&gt;
+                                &lt;label&gt;&lt;?php echo esc_html( $this-&gt;label ); ?&gt;&lt;br/&gt;
+                                &lt;select &lt;?php $this-&gt;name(); ?&gt;&gt;
+                                        &lt;?php
+                                        foreach ( $this-&gt;choices as $value =&gt; $label )
+                                                echo '&lt;option value=&quot;' . esc_attr( $value ) . '&quot;' . selected( $this-&gt;value(), $value, false ) . '&gt;' . $label . '&lt;/option&gt;';
+                                        ?&gt;
+                                &lt;/select&gt;
+                                &lt;?php
+                                break;
+                        default:
+                                do_action( 'customize_render_control-' . $this-&gt;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 &amp;&amp; empty( $root ) )
+                        $root = array();
+
+                if ( ! isset( $root ) || empty( $keys ) )
+                        return;
+
+                $last = array_pop( $keys );
+                $node = &amp;$root;
+
+                foreach ( $keys as $key ) {
+                        if ( $create &amp;&amp; ! isset( $node[ $key ] ) )
+                                $node[ $key ] = array();
+
+                        if ( ! is_array( $node ) || ! isset( $node[ $key ] ) )
+                                return;
+
+                        $node = &amp;$node[ $key ];
+                }
+
+                if ( $create &amp;&amp; ! isset( $node[ $last ] ) )
+                        $node[ $last ] = array();
+
+                if ( ! isset( $node[ $last ] ) )
+                        return;
+
+                return array(
+                        'root' =&gt; &amp;$root,
+                        'node' =&gt; &amp;$node,
+                        'key'  =&gt; $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-&gt;multidimensional( &amp;$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-&gt;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-&gt;multidimensional_get( $root, $keys );
+                return isset( $result );
+        }
+}
</ins></span></pre></div>
<a id="trunkwpincludesclasswpcustomizephp"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/class-wp-customize.php (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/class-wp-customize.php                                (rev 0)
+++ trunk/wp-includes/class-wp-customize.php        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,660 @@
</span><ins>+&lt;?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( &quot;theme_mods_$theme&quot;, $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-&gt;set_stylesheet() || isset( $_REQUEST['save'] ) )
+                        return;
+
+                $this-&gt;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 ) &gt; 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-&gt;is_preview() )
+                        return;
+
+                wp_enqueue_script( 'customize-preview' );
+                add_action( 'wp_footer', array( $this, 'customize_preview_settings' ), 20 );
+
+                foreach ( $this-&gt;settings as $setting ) {
+                        $setting-&gt;preview();
+                }
+        }
+
+        /**
+         * Print javascript settings.
+         *
+         * @since 3.4.0
+         */
+        public function customize_preview_settings() {
+                $settings = array(
+                        // @todo: Perhaps grab the URL via $_POST?
+                        'parent' =&gt; esc_url( admin_url( 'themes.php' ) ),
+                );
+                ?&gt;
+                &lt;script type=&quot;text/javascript&quot;&gt;
+                        (function() {
+                                if ( typeof wp === 'undefined' || ! wp.customize )
+                                        return;
+
+                                wp.customize.settings = &lt;?php echo json_encode( $settings ); ?&gt;;
+                        })();
+                &lt;/script&gt;
+                &lt;?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-&gt;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-&gt;template ) )
+                        return $this-&gt;template;
+
+                $template = preg_replace('|[^a-z0-9_./-]|i', '', $_REQUEST['template'] );
+                if ( validate_file( $template ) )
+                        return false;
+
+                return $this-&gt;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-&gt;stylesheet ) )
+                        return $this-&gt;stylesheet;
+
+                $this-&gt;set_template();
+                if ( empty( $this-&gt;template ) )
+                        return false;
+
+                if ( empty( $_REQUEST['stylesheet'] ) ) {
+                        $stylesheet = $this-&gt;template;
+                } else {
+                        $stylesheet = preg_replace( '|[^a-z0-9_./-]|i', '', $_REQUEST['stylesheet'] );
+                        if ( $stylesheet != $this-&gt;template &amp;&amp; validate_file( $stylesheet ) )
+                                return false;
+                }
+                return $this-&gt;stylesheet = $stylesheet;
+
+        }
+
+        /**
+         * Retrieve the template name of the previewed theme.
+         *
+         * @since 3.4.0
+         *
+         * @return string Template name.
+         */
+        public function get_template() {
+                return $this-&gt;template;
+        }
+
+        /**
+         * Retrieve the stylesheet name of the previewed theme.
+         *
+         * @since 3.4.0
+         *
+         * @return string Stylesheet name.
+         */
+        public function get_stylesheet() {
+                return $this-&gt;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-&gt;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-&gt;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-&gt;stylesheet &amp;&amp; $theme['Template'] == $this-&gt;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-&gt;save();
+
+                wp_enqueue_script( 'customize-loader' );
+                wp_enqueue_style( 'customize-loader' );
+
+                if ( ( defined( 'DOING_AJAX' ) &amp;&amp; DOING_AJAX ) )
+                        return;
+
+                if ( ! isset( $_GET['customize'] ) || 'on' != $_GET['customize'] )
+                        return;
+
+                if ( ! $this-&gt;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() {
+                ?&gt;
+                &lt;div id=&quot;customize-container&quot;&gt;
+                        &lt;input type=&quot;hidden&quot; class=&quot;admin-url&quot; value=&quot;&lt;?php echo esc_url( admin_url( 'admin.php' ) ); ?&gt;&quot; /&gt;
+                        &lt;div&gt;
+                                &lt;a href=&quot;#&quot; class=&quot;return-to-admin&quot;&gt;&lt;?php printf( __( '&amp;larr; Return to %s' ), get_admin_page_title() ); ?&gt;&lt;/a&gt;
+                                &lt;a href=&quot;#&quot; class=&quot;collapse-sidebar button-secondary&quot; title=&quot;&lt;?php esc_attr_e('Collapse Sidebar'); ?&gt;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/a&gt;
+                        &lt;/div&gt;
+                &lt;/div&gt;
+                &lt;?php
+        }
+
+        /**
+         * Switch the theme and trigger the save action of each setting.
+         *
+         * @since 3.4.0
+         */
+        public function save() {
+                if ( $this-&gt;is_preview() )
+                        return;
+
+                check_admin_referer( 'customize_controls' );
+
+                if ( ! $this-&gt;set_stylesheet() )
+                        return;
+
+                $active_template   = get_template();
+                $active_stylesheet = get_stylesheet();
+
+                // Do we have to switch themes?
+                if ( $this-&gt;get_template() != $active_template || $this-&gt;get_stylesheet() != $active_stylesheet ) {
+                        if ( ! current_user_can( 'switch_themes' ) )
+                                return;
+
+                        switch_theme( $this-&gt;get_template(), $this-&gt;get_stylesheet() );
+                }
+
+                do_action( 'customize_save' );
+
+                foreach ( $this-&gt;settings as $setting ) {
+                        $setting-&gt;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() {
+                ?&gt;
+                &lt;div class=&quot;updated&quot;&gt;&lt;p&gt;&lt;?php printf( __( 'Settings saved and theme activated. &lt;a href=&quot;%s&quot;&gt;Visit site&lt;/a&gt;.' ), home_url( '/' ) ); ?&gt;&lt;/p&gt;&lt;/div&gt;
+                &lt;?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-&gt;settings[ $setting-&gt;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-&gt;settings[ $id ] ) )
+                        return $this-&gt;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-&gt;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-&gt;sections[ $section-&gt;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-&gt;sections[ $id ] ) )
+                        return $this-&gt;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-&gt;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-&gt;priority;
+                $bp = $b-&gt;priority;
+
+                if ( $ap == $bp )
+                        return 0;
+                return ( $ap &gt; $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-&gt;sections = array_reverse( $this-&gt;sections );
+                uasort( $this-&gt;sections, array( $this, '_cmp_priority' ) );
+
+                $this-&gt;settings = array_reverse( $this-&gt;settings );
+                foreach ( $this-&gt;settings as $setting ) {
+                        if ( ! isset( $this-&gt;sections[ $setting-&gt;section ] ) )
+                                continue;
+
+                        $this-&gt;sections[ $setting-&gt;section ]-&gt;settings[] = $setting;
+
+                        if ( $setting-&gt;check_capabilities() )
+                                $setting-&gt;enqueue();
+                }
+
+                foreach ( $this-&gt;sections as $section ) {
+                        usort( $section-&gt;settings, array( $this, '_cmp_priority' ) );
+                }
+        }
+
+        /**
+         * Register some default controls.
+         *
+         * @since 3.4.0
+         */
+        public function register_controls() {
+
+                /* Custom Header */
+
+                $this-&gt;add_section( 'header', array(
+                        'title'          =&gt; __( 'Header' ),
+                        'theme_supports' =&gt; 'custom-header',
+                ) );
+
+                $this-&gt;add_setting( 'header_textcolor', array(
+                        'label'             =&gt; 'Text Color',
+                        'section'           =&gt; 'header',
+                        'sanitize_callback' =&gt; 'sanitize_hexcolor',
+                        'control'           =&gt; 'color',
+                        'default'           =&gt; defined( 'HEADER_TEXTCOLOR' ) ? HEADER_TEXTCOLOR : ''
+                ) );
+
+                /*
+                $this-&gt;add_setting( 'display_header', array(
+                        'label'   =&gt; 'Display Text',
+                        'section' =&gt; 'header',
+                        'type'    =&gt; 'radio',
+                        'choices' =&gt; array(
+                                'show'  =&gt; 'Yes',
+                                'hide'  =&gt; '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' =&gt; false,
+                ) );
+                */
+
+                // Input type: checkbox
+                // With custom value
+                $this-&gt;add_setting( 'header_image', array(
+                        'label'   =&gt; 'Random Image',
+                        'section' =&gt; 'header',
+                        'control' =&gt; 'checkbox',
+                         // @todo
+                         // not the default, it's the value.
+                         // value is saved in get_theme_support( 'custom-header' )[0][ 'random-default' ]
+                        'default' =&gt; 'random-default-image'
+                ) );
+
+                /* Custom Background */
+
+                $this-&gt;add_section( 'background', array(
+                        'title'          =&gt; __( 'Background' ),
+                        'theme_supports' =&gt; 'custom-background',
+                ) );
+
+                // Input type: Color
+                // With sanitize_callback
+                $this-&gt;add_setting( 'background_color', array(
+                        'label'             =&gt; 'Background Color',
+                        'section'           =&gt; 'background',
+                        'control'           =&gt; 'color',
+                        'default'           =&gt; defined( 'BACKGROUND_COLOR' ) ? BACKGROUND_COLOR : '',
+                        'sanitize_callback' =&gt; '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-&gt;add_section( 'nav', array(
+                        'title'          =&gt; __( 'Navigation' ),
+                        'theme_supports' =&gt; 'menus',
+                        'description'    =&gt; 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 =&gt; $description ) {
+                        $choices = array( 0 =&gt; '' );
+                        foreach ( $menus as $menu ) {
+                                $truncated_name = wp_html_excerpt( $menu-&gt;name, 40 );
+                                $truncated_name == $menu-&gt;name ? $menu-&gt;name : trim( $truncated_name ) . '&amp;hellip;';
+                                $choices[ $menu-&gt;term_id ] = $truncated_name;
+                        }
+
+                        $this-&gt;add_setting( &quot;nav_menu_locations[{$location}]&quot;, array(
+                                'label'             =&gt; $description,
+                                'theme_supports'    =&gt; 'menus', // Todo: Needs also widgets -- array( 'menus', 'widgets' )
+                                'section'           =&gt; 'nav',
+                                'control'           =&gt; 'select',
+                                'choices'           =&gt; $choices,
+                                'sanitize_callback' =&gt; 'absint',
+                        ) );
+                }
+
+                /* Static Front Page */
+                // #WP19627
+
+                $this-&gt;add_section( 'static_front_page', array(
+                        'title'          =&gt; __( 'Static Front Page' ),
+                //        'theme_supports' =&gt; 'static-front-page',
+                        'description'    =&gt; __( 'Your theme supports a static front page.' ),
+                ) );
+
+                $choices = array();
+                $choices['posts'] = __( 'Your latest posts' );
+                $choices['page'] = __( 'A static page (select below)' );
+
+                $this-&gt;add_setting( 'show_on_front', array(
+                        'label'          =&gt; __( 'Front page displays' ),
+                //        'theme_supports' =&gt; 'static-front-page',
+                        'section'        =&gt; 'static_front_page',
+                        'control'        =&gt; 'radio',
+                        'choices'        =&gt; $choices,
+                        'default'        =&gt; get_option( 'show_on_front' ),
+                        'type'           =&gt; 'option',
+                        'capability'     =&gt; 'manage_options'
+                ) );
+
+                $this-&gt;add_setting( 'page_on_front', array(
+                        'label'          =&gt; __( 'Front page:' ),
+                //        'theme_supports' =&gt; 'static-front-page',
+                        'section'        =&gt; 'static_front_page',
+                        'control'        =&gt; 'dropdown-pages',
+                        'type'           =&gt; 'option',
+                        'capability'     =&gt; 'manage_options'
+                ) );
+
+                $this-&gt;add_setting( 'page_for_posts', array(
+                        'label'          =&gt; __( 'Posts page:' ),
+                //        'theme_supports' =&gt; 'static-front-page',
+                        'section'        =&gt; 'static_front_page',
+                        'control'        =&gt; 'dropdown-pages',
+                        'type'           =&gt; 'option',
+                        'capability'     =&gt; 'manage_options'
+                ) );
+
+                /* Site Title &amp; Tagline */
+
+                $this-&gt;add_section( 'strings', array(
+                        'title'          =&gt; __( 'Site Title &amp; Tagline' ),
+                        'description'    =&gt; __( 'Customize some strings.' ),
+                ) );
+
+                $this-&gt;add_setting( 'blogname', array(
+                        'label'          =&gt; __( 'Site Title' ),
+                        'section'        =&gt; 'strings',
+                        'default'        =&gt; get_option( 'blogname' ),
+                        'type'           =&gt; 'option',
+                        'capability'     =&gt; 'manage_options'
+                ) );
+
+                $this-&gt;add_setting( 'blogdescription', array(
+                        'label'          =&gt; __( 'Tagline' ),
+                        'section'        =&gt; 'strings',
+                        'default'        =&gt; get_option( 'blogdescription' ),
+                        'type'           =&gt; 'option',
+                        'capability'     =&gt; '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(
+                __( '&lt;label&gt;%s %s&lt;/label&gt;' ),
+                $setting-&gt;label,
+                wp_dropdown_pages(
+                        array(
+                                'name'              =&gt; $setting-&gt;get_name(),
+                                'echo'              =&gt; 0,
+                                'show_option_none'  =&gt; __( '&amp;mdash; Select &amp;mdash;' ),
+                                'option_none_value' =&gt; '0',
+                                'selected'          =&gt; get_option( $setting-&gt;id )
+                        )
+                )
+        );
+}
+add_action( 'customize_render_control-dropdown-pages', 'customize_control_dropdown_pages' );
</ins></span></pre></div>
<a id="trunkwpincludescsscustomizecontrolsdevcss"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/css/customize-controls.dev.css (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/css/customize-controls.dev.css                                (rev 0)
+++ trunk/wp-includes/css/customize-controls.dev.css        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,216 @@
</span><ins>+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=&quot;text&quot;].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
+}
</ins></span></pre></div>
<a id="trunkwpincludescsscustomizeloaderdevcss"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/css/customize-loader.dev.css (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/css/customize-loader.dev.css                                (rev 0)
+++ trunk/wp-includes/css/customize-loader.dev.css        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,90 @@
</span><ins>+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;
+}
</ins></span></pre></div>
<a id="trunkwpincludescustomizecontrolsphp"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/customize-controls.php (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/customize-controls.php                                (rev 0)
+++ trunk/wp-includes/customize-controls.php        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,108 @@
</span><ins>+&lt;?php
+/**
+ * Customize Controls
+ *
+ * @package WordPress
+ * @subpackage Customize
+ * @since 3.4.0
+ */
+
+if ( ! defined( 'ABSPATH' ) )
+        die;
+
+global $wp_scripts;
+
+$registered = $wp_scripts-&gt;registered;
+$wp_scripts = new WP_Scripts;
+$wp_scripts-&gt;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 &amp;#8212; WordPress' ), strip_tags( sprintf( __( 'Customize %s' ), $theme['Name'] ) ) );
+?&gt;&lt;title&gt;&lt;?php echo $admin_title; ?&gt;&lt;/title&gt;&lt;?php
+
+do_action( 'customize_controls_print_styles' );
+do_action( 'customize_controls_print_scripts' );
+?&gt;
+&lt;/head&gt;
+&lt;body&gt;
+        &lt;form id=&quot;customize-controls&quot; method=&quot;post&quot; class=&quot;wrap&quot; target=&quot;_parent&quot; action=&quot;&lt;?php echo esc_url( add_query_arg( 'save', '1', admin_url( 'themes.php' ) ) ); ?&gt;&quot;&gt;
+                &lt;?php wp_nonce_field( 'customize_controls' ); ?&gt;
+                &lt;input type=&quot;hidden&quot; name=&quot;customize&quot; value=&quot;on&quot; /&gt;
+                &lt;input type=&quot;hidden&quot; id=&quot;customize-template&quot; name=&quot;template&quot; value=&quot;&lt;?php echo esc_attr( $theme['Template'] ); ?&gt;&quot; /&gt;
+                &lt;input type=&quot;hidden&quot; id=&quot;customize-stylesheet&quot; name=&quot;stylesheet&quot; value=&quot;&lt;?php echo esc_attr( $theme['Stylesheet'] ); ?&gt;&quot; /&gt;
+
+                &lt;div id=&quot;customize-info&quot; class=&quot;customize-section&quot;&gt;
+                        &lt;p&gt;
+                                &lt;strong class=&quot;theme-name&quot;&gt;&lt;?php echo $theme['Name']; ?&gt;&lt;/strong&gt;
+                                &lt;span class=&quot;theme-by&quot;&gt;&lt;?php printf( __( 'By %s' ), $theme['Author'] ); ?&gt;&lt;/span&gt;
+                        &lt;/p&gt;
+                        &lt;?php if ( $screenshot ) : ?&gt;
+                                &lt;img class=&quot;theme-screenshot&quot; src=&quot;&lt;?php echo esc_url( $screenshot ); ?&gt;&quot; /&gt;
+                        &lt;?php endif; ?&gt;
+                &lt;/div&gt;
+
+                &lt;div id=&quot;customize-theme-controls&quot;&gt;&lt;ul&gt;
+                        &lt;?php
+                        foreach ( $this-&gt;sections as $section )
+                                $section-&gt;render();
+                        ?&gt;
+                &lt;/ul&gt;&lt;/div&gt;
+
+                &lt;div id=&quot;customize-footer&quot; class=&quot;customize-section&quot;&gt;
+                        &lt;?php
+                        submit_button( __( 'Refresh' ), 'secondary', 'refresh', false );
+                        submit_button( __( 'Save' ), 'primary', 'save', false );
+                        ?&gt;
+                &lt;/div&gt;
+        &lt;/form&gt;
+        &lt;div id=&quot;customize-preview&quot;&gt;
+                &lt;iframe name=&quot;customize-target&quot;&gt;&lt;/iframe&gt;
+        &lt;/div&gt;
+        &lt;?php
+
+        do_action( 'customize_controls_print_footer_scripts' );
+
+        $settings = array(
+                'preview' =&gt; esc_url( home_url( '/' ) ),
+                'values'  =&gt; array(),
+                'prefix'  =&gt; WP_Customize_Setting::name_prefix,
+        );
+
+        foreach ( $this-&gt;settings as $id =&gt; $setting ) {
+                $settings['values'][ $id ] = $setting-&gt;value();
+        }
+
+        ?&gt;
+        &lt;script type=&quot;text/javascript&quot;&gt;
+                (function() {
+                        if ( typeof wp === 'undefined' || ! wp.customize )
+                                return;
+
+                        wp.customize.settings = &lt;?php echo json_encode( $settings ); ?&gt;;
+                })();
+        &lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkwpincludesjscustomizebasedevjs"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/js/customize-base.dev.js (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/js/customize-base.dev.js                                (rev 0)
+++ trunk/wp-includes/js/customize-base.dev.js        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,496 @@
</span><ins>+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 &quot;constructor&quot; property in your `extend` definition), or defaulted
+                // by us to simply call `super()`.
+                if ( protoProps &amp;&amp; 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 &amp;&amp; argsArray &amp;&amp; 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 &amp;&amp; $.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 &amp;&amp; event.origin !== this.origin )
+                        //         return;
+
+                        message = JSON.parse( event.data );
+
+                        if ( message &amp;&amp; message.id &amp;&amp; message.data &amp;&amp; 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 );
</ins></span></pre></div>
<a id="trunkwpincludesjscustomizecontrolsdevjs"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/js/customize-controls.dev.js (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/js/customize-controls.dev.js                                (rev 0)
+++ trunk/wp-includes/js/customize-controls.dev.js        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,101 @@
</span><ins>+(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^=&quot;' + api.settings.prefix + '&quot;]');
+
+                // 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=&quot;' + api.settings.prefix + id + '&quot;]' ),
+                                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^=&quot;' + api.settings.prefix + '&quot;]').each( function() {
+                        // console.log( this.name );
+                });
+        });
+
+})( wp, jQuery );
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkwpincludesjscustomizeloaderdevjs"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/js/customize-loader.dev.js (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/js/customize-loader.dev.js                                (rev 0)
+++ trunk/wp-includes/js/customize-loader.dev.js        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,75 @@
</span><ins>+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( '&lt;iframe /&gt;' );
+                        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=([^&amp;]*)')[1];
+                        stylesheet = href.match('stylesheet=([^&amp;]*)')[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 );
</ins></span></pre></div>
<a id="trunkwpincludesjscustomizepreviewdevjs"></a>
<div class="addfile"><h4>Added: trunk/wp-includes/js/customize-preview.dev.js (0 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/js/customize-preview.dev.js                                (rev 0)
+++ trunk/wp-includes/js/customize-preview.dev.js        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -0,0 +1,59 @@
</span><ins>+(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 );
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkwpincludesscriptloaderphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/script-loader.php (19994 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/script-loader.php        2012-02-25 02:15:56 UTC (rev 19994)
+++ trunk/wp-includes/script-loader.php        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -289,6 +289,11 @@
</span><span class="cx"> 
</span><span class="cx">         $scripts-&gt;add( 'hoverIntent', &quot;/wp-includes/js/hoverIntent$suffix.js&quot;, array('jquery'), 'r6', 1 );
</span><span class="cx"> 
</span><ins>+        $scripts-&gt;add( 'customize-loader', &quot;/wp-includes/js/customize-loader$suffix.js&quot;,   array( 'jquery' ), false, 1 );
+        $scripts-&gt;add( 'customize-base', &quot;/wp-includes/js/customize-base$suffix.js&quot;,       array( 'jquery-postmessage', 'json2' ), false, 1 );
+        $scripts-&gt;add( 'customize-controls', &quot;/wp-includes/js/customize-controls$suffix.js&quot;, array( 'customize-base' ), false, 1 );
+        $scripts-&gt;add( 'customize-preview', &quot;/wp-includes/js/customize-preview$suffix.js&quot;,  array( 'customize-base' ), false, 1 );
+
</ins><span class="cx">         if ( is_admin() ) {
</span><span class="cx">                 $scripts-&gt;add( 'ajaxcat', &quot;/wp-admin/js/cat$suffix.js&quot;, array( 'wp-lists' ) );
</span><span class="cx">                 $scripts-&gt;add_data( 'ajaxcat', 'group', 1 );
</span><span class="lines">@@ -464,6 +469,8 @@
</span><span class="cx">         $styles-&gt;add( 'wp-jquery-ui-dialog', &quot;/wp-includes/css/jquery-ui-dialog$suffix.css&quot; );
</span><span class="cx">         $styles-&gt;add( 'editor-buttons', &quot;/wp-includes/css/editor-buttons$suffix.css&quot; );
</span><span class="cx">         $styles-&gt;add( 'wp-pointer', &quot;/wp-includes/css/wp-pointer$suffix.css&quot; );
</span><ins>+        $styles-&gt;add( 'customize-loader', &quot;/wp-includes/css/customize-loader$suffix.css&quot; );
+        $styles-&gt;add( 'customize-controls', &quot;/wp-includes/css/customize-controls$suffix.css&quot;, array( 'wp-admin', 'colors', 'ie' ) );
</ins><span class="cx"> 
</span><span class="cx">         foreach ( $rtl_styles as $rtl_style ) {
</span><span class="cx">                 $styles-&gt;add_data( $rtl_style, 'rtl', true );
</span></span></pre></div>
<a id="trunkwpincludesthemephp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/theme.php (19994 => 19995)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/theme.php        2012-02-25 02:15:56 UTC (rev 19994)
+++ trunk/wp-includes/theme.php        2012-02-25 04:12:43 UTC (rev 19995)
</span><span class="lines">@@ -2082,3 +2082,15 @@
</span><span class="cx">                 update_option( 'theme_switched', false );
</span><span class="cx">         }
</span><span class="cx"> }
</span><ins>+
+function wp_customize_load() {
+        // Load on themes.php or ?customize=on
+        if ( ! ( isset( $_REQUEST['customize'] ) &amp;&amp; 'on' == $_REQUEST['customize'] ) &amp;&amp; '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' );
</ins></span></pre>
</div>
</div>

</body>
</html>