diff --git src/wp-admin/customize.php src/wp-admin/customize.php
index 3cfa0c7..2ce23b4 100644
--- src/wp-admin/customize.php
+++ src/wp-admin/customize.php
@@ -160,13 +160,9 @@ do_action( 'customize_controls_print_scripts' );
-
- containers() as $container ) {
- $container->maybe_render();
- }
- ?>
-
+
@@ -249,6 +245,8 @@ do_action( 'customize_controls_print_scripts' );
),
'settings' => array(),
'controls' => array(),
+ 'panels' => array(),
+ 'sections' => array(),
'nonce' => array(
'save' => wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() ),
'preview' => wp_create_nonce( 'preview-customize_' . $wp_customize->get_stylesheet() )
@@ -263,10 +261,22 @@ do_action( 'customize_controls_print_scripts' );
);
}
- // Prepare Customize Control objects to pass to Javascript.
+ // Prepare Customize Control objects to pass to JavaScript.
foreach ( $wp_customize->controls() as $id => $control ) {
- $control->to_json();
- $settings['controls'][ $id ] = $control->json;
+ $settings['controls'][ $id ] = $control->json();
+ }
+
+ // Prepare Customize Section objects to pass to JavaScript.
+ foreach ( $wp_customize->sections() as $id => $section ) {
+ $settings['sections'][ $id ] = $section->json();
+ }
+
+ // Prepare Customize Panel objects to pass to JavaScript.
+ foreach ( $wp_customize->panels() as $id => $panel ) {
+ $settings['panels'][ $id ] = $panel->json();
+ foreach ( $panel->sections as $section_id => $section ) {
+ $settings['sections'][ $section_id ] = $section->json();
+ }
}
?>
diff --git src/wp-admin/js/accordion.js src/wp-admin/js/accordion.js
index 6cb1c1c..63e14e8 100644
--- src/wp-admin/js/accordion.js
+++ src/wp-admin/js/accordion.js
@@ -58,8 +58,6 @@
});
});
- var sectionContent = $( '.accordion-section-content' );
-
/**
* Close the current accordion section and open a new one.
*
@@ -69,7 +67,7 @@
function accordionSwitch ( el ) {
var section = el.closest( '.accordion-section' ),
siblings = section.closest( '.accordion-container' ).find( '.open' ),
- content = section.find( sectionContent );
+ content = section.find( '.accordion-section-content' );
// This section has no content and cannot be expanded.
if ( section.hasClass( 'cannot-expand' ) ) {
@@ -87,7 +85,7 @@
content.toggle( true ).slideToggle( 150 );
} else {
siblings.removeClass( 'open' );
- siblings.find( sectionContent ).show().slideUp( 150 );
+ siblings.find( '.accordion-section-content' ).show().slideUp( 150 );
content.toggle( false ).slideToggle( 150 );
section.toggleClass( 'open' );
}
@@ -125,7 +123,7 @@
} else {
// Close all open sections in any accordion level.
siblings.removeClass( 'open' );
- siblings.find( sectionContent ).show().slideUp( 0 );
+ siblings.find( '.accordion-section-content' ).show().slideUp( 0 );
content.show( 0, function() {
position = content.offset().top;
scroll = container.scrollTop();
diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
index 85b171d..0af5277 100644
--- src/wp-admin/js/customize-controls.js
+++ src/wp-admin/js/customize-controls.js
@@ -34,6 +34,142 @@
* @constructor
* @augments wp.customize.Class
*/
+ api.Section = api.Class.extend({
+
+ /**
+ * @param {String} id
+ * @param {Array} options
+ */
+ initialize: function ( id, options ) {
+ var section = this;
+ section.id = id;
+ section.params = {};
+ $.extend( section, options || {} );
+ section.panel = new api.Value();
+ section.container = $( section.params.content );
+ section.priority = new api.Value( section.params.priority || 100 );
+ section.panel.bind( function ( id ) {
+ $( section.container ).toggleClass( 'control-subsection', !! id );
+ });
+ section.panel.set( section.params.panel || '' );
+ },
+
+ /**
+ *
+ */
+ embed: function ( readyCallback ) {
+ var panel_id,
+ section = this;
+
+ panel_id = this.panel.get();
+ if ( ! panel_id ) {
+ $( '#customize-theme-controls > ul' ).append( section.container );
+ readyCallback();
+ } else {
+ api.panel( panel_id, function ( panel ) {
+ panel.embed();
+ panel.container.find( 'ul:first' ).append( section.container );
+ readyCallback();
+ } );
+ }
+ },
+
+ /**
+ * Get the controls that are associated with this section.
+ *
+ * @returns {Array}
+ */
+ controls: function () {
+ var section = this,
+ controls = [];
+ api.control.each( function ( control ) {
+ if ( control.section.get() === section.id ) {
+ controls.push( control );
+ }
+ } );
+ controls.sort( function ( a, b ) {
+ return a.priority() - b.priority();
+ } );
+ return controls;
+ },
+
+ /**
+ * Expand the accordion section (and collapse all others).
+ */
+ expand: function () {
+ throw new Error( 'Not implemented' ); // @todo
+ },
+
+ /**
+ * Collapse the accordion section.
+ */
+ collapse: function () {
+ throw new Error( 'Not implemented' ); // @todo
+ }
+ });
+
+ /**
+ * @constructor
+ * @augments wp.customize.Class
+ */
+ api.Panel = api.Class.extend({
+ initialize: function ( id, options ) {
+ var panel = this;
+ panel.id = id;
+ panel.params = {};
+ $.extend( panel, options || {} );
+ panel.priority = new api.Value( panel.params.priority || 160 ); // @todo What if the priority gets changed dynamically?
+ panel.container = $( panel.params.content );
+ },
+
+ /**
+ *
+ */
+ embed: function ( readyCallback ) {
+ $( '#customize-theme-controls > ul' ).append( this.container );
+ if ( readyCallback ) {
+ readyCallback();
+ }
+ },
+
+ /**
+ * Get the controls that are associated with this section.
+ *
+ * @returns {Array}
+ */
+ sections: function () {
+ var panel = this,
+ sections = [];
+ api.section.each( function ( section ) {
+ if ( section.panel.get() === panel.id ) {
+ sections.push( section );
+ }
+ } );
+ sections.sort( function ( a, b ) {
+ return a.priority() - b.priority();
+ } );
+ return sections;
+ },
+
+ /**
+ * Expand the accordion section (and collapse all others).
+ */
+ expand: function () {
+ throw new Error( 'Not implemented' ); // @todo
+ },
+
+ /**
+ * Collapse the accordion section.
+ */
+ collapse: function () {
+ throw new Error( 'Not implemented' ); // @todo
+ }
+ });
+
+ /**
+ * @constructor
+ * @augments wp.customize.Class
+ */
api.Control = api.Class.extend({
initialize: function( id, options ) {
var control = this,
@@ -44,7 +180,10 @@
this.id = id;
this.selector = '#customize-control-' + id.replace( /\]/g, '' ).replace( /\[/g, '-' );
- this.container = $( this.selector );
+ this.container = this.params.content ? $( this.params.content ) : $( this.selector );
+
+ this.section = new api.Value( this.params.section );
+ this.priority = new api.Value( this.params.priority || 10 );
this.active = new api.Value( this.params.active );
settings = $.map( this.params.settings, function( value ) {
@@ -60,7 +199,9 @@
}
control.setting = control.settings['default'] || null;
- control.ready();
+ control.embed( function () {
+ control.ready();
+ } );
}) );
control.elements = [];
@@ -74,8 +215,9 @@
if ( node.is(':radio') ) {
name = node.prop('name');
- if ( radios[ name ] )
+ if ( radios[ name ] ) {
return;
+ }
radios[ name ] = true;
node = nodes.filter( '[name="' + name + '"]' );
@@ -96,6 +238,27 @@
},
/**
+ * @param {Function} [readyCallback] Callback to fire when the embedding is done.
+ */
+ embed: function ( readyCallback ) {
+ var section_id,
+ control = this;
+
+ section_id = control.section.get();
+ if ( ! section_id ) {
+ throw new Error( 'A control must have an associated section.' );
+ }
+
+ // Defer until the associated section is available
+ api.section( section_id, function ( section ) {
+ section.embed( function () {
+ section.container.find( 'ul:first' ).append( control.container );
+ readyCallback();
+ } );
+ } );
+ },
+
+ /**
* @abstract
*/
ready: function() {},
@@ -575,6 +738,21 @@
// Create the collection of Control objects.
api.control = new api.Values({ defaultConstructor: api.Control });
+ api.section = new api.Values({ defaultConstructor: api.Section });
+ api.panel = new api.Values({ defaultConstructor: api.Panel });
+
+ // @todo For each, bind the 'add' and 'remove' events
+ // @todo For each, bind to the changes in the priority for each item added, and then reorder the elements based on priority
+ //
+ //api.panel.bind( 'add', function () {
+ // console.info( 'add panel', arguments )
+ //} );
+ //api.section.bind( 'add', function () {
+ // console.info( 'add section', arguments )
+ //} );
+ //api.control.bind( 'add', function () {
+ // console.info( 'add control', arguments )
+ //} );
/**
* @constructor
@@ -1091,6 +1269,7 @@
$.extend( this.nonce, nonce );
});
+ //
$.each( api.settings.settings, function( id, data ) {
api.create( id, id, data.value, {
transport: data.transport,
@@ -1098,14 +1277,30 @@
} );
});
+ $.each( api.settings.panels, function ( id, data ) {
+ var panel = new api.Panel( id, {
+ params: data
+ } );
+ api.panel.add( id, panel );
+ });
+
+ $.each( api.settings.sections, function ( id, data ) {
+ var section = new api.Section( id, {
+ params: data
+ } );
+ api.section.add( id, section );
+ });
+
+ // @todo Extract this out
$.each( api.settings.controls, function( id, data ) {
var constructor = api.controlConstructor[ data.type ] || api.Control,
control;
- control = api.control.add( id, new constructor( id, {
+ control = new constructor( id, {
params: data,
previewer: api.previewer
- } ) );
+ } );
+ api.control.add( id, control );
});
// Check if preview url is valid and load the preview frame.
diff --git src/wp-includes/class-wp-customize-control.php src/wp-includes/class-wp-customize-control.php
index 7ae21c8..4a5985f 100644
--- src/wp-includes/class-wp-customize-control.php
+++ src/wp-includes/class-wp-customize-control.php
@@ -74,6 +74,7 @@ class WP_Customize_Control {
public $input_attrs = array();
/**
+ * @deprecated It is better to just call the json() method
* @access public
* @var array
*/
@@ -219,6 +220,20 @@ class WP_Customize_Control {
$this->json['type'] = $this->type;
$this->json['active'] = $this->active();
+ $this->json['section'] = $this->section;
+ $this->json['content'] = $this->get_content();
+ }
+
+ /**
+ * Get the data to export to the client via JSON.
+ *
+ * @since 4.1.0
+ *
+ * @return array
+ */
+ public function json() {
+ $this->to_json();
+ return $this->json;
}
/**
@@ -242,6 +257,21 @@ class WP_Customize_Control {
}
/**
+ * Get the control's content for insertion into the Customizer pane.
+ *
+ * @since 4.1.0
+ *
+ * @return string
+ */
+ public final function get_content() {
+ ob_start();
+ $this->maybe_render();
+ $template = trim( ob_get_contents() );
+ ob_end_clean();
+ return $template;
+ }
+
+ /**
* Check capabilities and render the control.
*
* @since 3.4.0
diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
index c658198..bf0963c 100644
--- src/wp-includes/class-wp-customize-manager.php
+++ src/wp-includes/class-wp-customize-manager.php
@@ -878,11 +878,11 @@ final class WP_Customize_Manager {
if ( ! $section->panel ) {
// Top-level section.
- $sections[] = $section;
+ $sections[ $section->id ] = $section;
} else {
// This section belongs to a panel.
if ( isset( $this->panels [ $section->panel ] ) ) {
- $this->panels[ $section->panel ]->sections[] = $section;
+ $this->panels[ $section->panel ]->sections[ $section->id ] = $section;
}
}
}
@@ -899,8 +899,8 @@ final class WP_Customize_Manager {
continue;
}
- usort( $panel->sections, array( $this, '_cmp_priority' ) );
- $panels[] = $panel;
+ uasort( $panel->sections, array( $this, '_cmp_priority' ) );
+ $panels[ $panel->id ] = $panel;
}
$this->panels = $panels;
diff --git src/wp-includes/class-wp-customize-panel.php src/wp-includes/class-wp-customize-panel.php
index 8f85049..a04c568 100644
--- src/wp-includes/class-wp-customize-panel.php
+++ src/wp-includes/class-wp-customize-panel.php
@@ -110,6 +110,19 @@ class WP_Customize_Panel {
}
/**
+ * Gather the parameters passed to client JavaScript via JSON.
+ *
+ * @since 4.1.0
+ *
+ * @return array The array to be exported to the client as JSON
+ */
+ public function json() {
+ $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority' ) );
+ $array['content'] = $this->get_content();
+ return $array;
+ }
+
+ /**
* Checks required user capabilities and whether the theme has the
* feature support required by the panel.
*
@@ -130,6 +143,21 @@ class WP_Customize_Panel {
}
/**
+ * Get the panel's content template for insertion into the Customizer pane.
+ *
+ * @since 4.1.0
+ *
+ * @return string
+ */
+ public final function get_content() {
+ ob_start();
+ $this->maybe_render();
+ $template = trim( ob_get_contents() );
+ ob_end_clean();
+ return $template;
+ }
+
+ /**
* Check capabilities and render the panel.
*
* @since 4.0.0
@@ -175,7 +203,7 @@ class WP_Customize_Panel {
- -
+
-
- sections as $section ) {
- $section->maybe_render();
- }
- ?>
+
get_content();
+ return $array;
+ }
+
+ /**
* Checks required user capabilities and whether the theme has the
* feature support required by the section.
*
@@ -136,6 +149,21 @@ class WP_Customize_Section {
}
/**
+ * Get the section's content template for insertion into the Customizer pane.
+ *
+ * @since 4.1.0
+ *
+ * @return string
+ */
+ public final function get_content() {
+ ob_start();
+ $this->maybe_render();
+ $template = trim( ob_get_contents() );
+ ob_end_clean();
+ return $template;
+ }
+
+ /**
* Check capabilities and render the section.
*
* @since 3.4.0
@@ -172,9 +200,6 @@ class WP_Customize_Section {
*/
protected function render() {
$classes = 'control-section accordion-section';
- if ( $this->panel ) {
- $classes .= ' control-subsection';
- }
?>
-
@@ -183,12 +208,10 @@ class WP_Customize_Section {
description ) ) : ?>
- description; ?>
+ -
+
description; ?>
+
- controls as $control )
- $control->maybe_render();
- ?>