diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
index 7b9f7b1..0a7e4f4 100644
--- src/wp-admin/js/customize-controls.js
+++ src/wp-admin/js/customize-controls.js
@@ -1,8 +1,6 @@
/* globals _wpCustomizeHeader, _wpMediaViewsL10n */
(function( exports, $ ){
- var bubbleChildValueChanges, Container, focus, isKeydownButNotEnterEvent, areElementListsEqual, prioritySort, api = wp.customize;
-
- // @todo Move private helper functions to wp.customize.utils so they can be unit tested
+ var Container, focus, containerRootElement, api = wp.customize;
/**
* @class
@@ -33,12 +31,17 @@
});
/**
+ * Utility function namespace
+ */
+ api.utils = {};
+
+ /**
* Watch all changes to Value properties, and bubble changes to parent Values instance
*
* @param {wp.customize.Class} instance
* @param {Array} properties The names of the Value instances to watch.
*/
- bubbleChildValueChanges = function ( instance, properties ) {
+ api.utils.bubbleChildValueChanges = function ( instance, properties ) {
$.each( properties, function ( i, key ) {
instance[ key ].bind( function ( to, from ) {
if ( instance.parent && to !== from ) {
@@ -49,6 +52,24 @@
};
/**
+ * Set the element which serves as the root container for the Customizer UI.
+ *
+ * @param {Element|jQuery} rootElement
+ */
+ api.utils.setContainerRootElement = function( rootElement ) {
+ containerRootElement = $( rootElement );
+ };
+
+ /**
+ * Get the element which serves as the root container for the Customizer UI.
+ *
+ * @returns {jQuery}
+ */
+ api.utils.getContainerRootElement = function() {
+ return containerRootElement;
+ };
+
+ /**
* Expand a panel, section, or control and focus on the first focusable element.
*
* @param {Object} [params]
@@ -86,7 +107,7 @@
* @param {(wp.customize.Panel|wp.customize.Section|wp.customize.Control)} b
* @returns {Number}
*/
- prioritySort = function ( a, b ) {
+ api.utils.prioritySort = function ( a, b ) {
if ( a.priority() === b.priority() && typeof a.params.instanceNumber === 'number' && typeof b.params.instanceNumber === 'number' ) {
return a.params.instanceNumber - b.params.instanceNumber;
} else {
@@ -100,7 +121,7 @@
* @param {jQuery.Event} event
* @returns {boolean}
*/
- isKeydownButNotEnterEvent = function ( event ) {
+ api.utils.isKeydownButNotEnterEvent = function ( event ) {
return ( 'keydown' === event.type && 13 !== event.which );
};
@@ -111,7 +132,7 @@
* @param {Array|jQuery} listB
* @returns {boolean}
*/
- areElementListsEqual = function ( listA, listB ) {
+ api.utils.areElementListsEqual = function ( listA, listB ) {
var equal = (
listA.length === listB.length && // if lists are different lengths, then naturally they are not equal
-1 === _.map( // are there any false values in the list returned by map?
@@ -142,7 +163,7 @@
container.container = $( container.params.content );
container.deferred = {
- ready: new $.Deferred()
+ embedded: new $.Deferred()
};
container.priority = new api.Value();
container.active = new api.Value();
@@ -155,22 +176,20 @@
args = $.extend( {}, container.defaultActiveArguments, args );
active = ( active && container.isContextuallyActive() );
container.onChangeActive( active, args );
- // @todo trigger 'activated' and 'deactivated' events based on the expanded param?
});
container.expanded.bind( function ( expanded ) {
var args = container.expandedArgumentsQueue.shift();
args = $.extend( {}, container.defaultExpandedArguments, args );
container.onChangeExpanded( expanded, args );
- // @todo trigger 'expanded' and 'collapsed' events based on the expanded param?
});
container.attachEvents();
- bubbleChildValueChanges( container, [ 'priority', 'active' ] );
+ api.utils.bubbleChildValueChanges( container, [ 'priority', 'active' ] );
container.priority.set( isNaN( container.params.priority ) ? 100 : container.params.priority );
container.active.set( container.params.active );
- container.expanded.set( false ); // @todo True if deeplinking?
+ container.expanded.set( false );
},
/**
@@ -193,7 +212,7 @@
children.push( child );
}
} );
- children.sort( prioritySort );
+ children.sort( api.utils.prioritySort );
return children;
},
@@ -335,10 +354,10 @@
$( section.container ).toggleClass( 'control-subsection', !! id );
});
section.panel.set( section.params.panel || '' );
- bubbleChildValueChanges( section, [ 'panel' ] );
+ api.utils.bubbleChildValueChanges( section, [ 'panel' ] );
section.embed();
- section.deferred.ready.done( function () {
+ section.deferred.embedded.done( function () {
section.ready();
});
},
@@ -356,21 +375,21 @@
// The panel has been supplied, so wait until the panel object is registered
api.panel( panelId, function ( panel ) {
// The panel has been registered, wait for it to become ready/initialized
- panel.deferred.ready.done( function () {
+ panel.deferred.embedded.done( function () {
parentContainer = panel.container.find( 'ul:first' );
if ( ! section.container.parent().is( parentContainer ) ) {
parentContainer.append( section.container );
}
- section.deferred.ready.resolve(); // @todo Better to use `embedded` instead of `ready`
+ section.deferred.embedded.resolve();
});
} );
} else {
// There is no panel, so embed the section in the root of the customizer
- parentContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
+ parentContainer = api.utils.getContainerRootElement(); // implements todo: This should be defined elsewhere, and to be configurable
if ( ! section.container.parent().is( parentContainer ) ) {
parentContainer.append( section.container );
}
- section.deferred.ready.resolve();
+ section.deferred.embedded.resolve();
}
};
section.panel.bind( inject );
@@ -385,7 +404,7 @@
// Expand/Collapse accordion sections on click.
section.container.find( '.accordion-section-title' ).on( 'click keydown', function( event ) {
- if ( isKeydownButNotEnterEvent( event ) ) {
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
event.preventDefault(); // Keep this AFTER the key filter above
@@ -479,7 +498,7 @@
var panel = this;
Container.prototype.initialize.call( panel, id, options );
panel.embed();
- panel.deferred.ready.done( function () {
+ panel.deferred.embedded.done( function () {
panel.ready();
});
},
@@ -489,12 +508,12 @@
*/
embed: function () {
var panel = this,
- parentContainer = $( '#customize-theme-controls > ul' ); // @todo This should be defined elsewhere, and to be configurable
+ parentContainer = api.utils.getContainerRootElement(); // implements todo: This should be defined elsewhere, and to be configurable
if ( ! panel.container.parent().is( parentContainer ) ) {
parentContainer.append( panel.container );
}
- panel.deferred.ready.resolve();
+ panel.deferred.embedded.resolve();
},
/**
@@ -505,7 +524,7 @@
// Expand/Collapse accordion sections on click.
panel.container.find( '.accordion-section-title' ).on( 'click keydown', function( event ) {
- if ( isKeydownButNotEnterEvent( event ) ) {
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
event.preventDefault(); // Keep this AFTER the key filter above
@@ -518,7 +537,7 @@
meta = panel.container.find( '.panel-meta:first' );
meta.find( '> .accordion-section-title' ).on( 'click keydown', function( event ) {
- if ( isKeydownButNotEnterEvent( event ) ) {
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
event.preventDefault(); // Keep this AFTER the key filter above
@@ -588,7 +607,8 @@
overlay = section.closest( '.wp-full-overlay' ),
container = section.closest( '.accordion-container' ),
siblings = container.find( '.open' ),
- topPanel = overlay.find( '#customize-theme-controls > ul > .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ),
+ $ContainerRootElement = api.utils.getContainerRootElement(),
+ topPanel = overlay.find( $ContainerRootElement ).find( '> .accordion-section > .accordion-section-title' ).add( '#customize-info > .accordion-section-title' ),
backBtn = overlay.find( '.control-panel-back' ),
panelTitle = section.find( '.accordion-section-title' ).first(),
content = section.find( '.control-panel-content' );
@@ -676,7 +696,7 @@
control.container = control.params.content ? $( control.params.content ) : $( control.selector );
control.deferred = {
- ready: new $.Deferred()
+ embedded: new $.Deferred()
};
control.section = new api.Value();
control.priority = new api.Value();
@@ -720,7 +740,7 @@
control.priority.set( isNaN( control.params.priority ) ? 10 : control.params.priority );
control.active.set( control.params.active );
- bubbleChildValueChanges( control, [ 'section', 'priority', 'active' ] );
+ api.utils.bubbleChildValueChanges( control, [ 'section', 'priority', 'active' ] );
// Associate this control with its settings when they are created
settings = $.map( control.params.settings, function( value ) {
@@ -739,7 +759,7 @@
control.embed();
}) );
- control.deferred.ready.done( function () {
+ control.deferred.embedded.done( function () {
control.ready();
});
},
@@ -760,13 +780,13 @@
// Wait for the section to be registered
api.section( sectionId, function ( section ) {
// Wait for the section to be ready/initialized
- section.deferred.ready.done( function () {
+ section.deferred.embedded.done( function () {
parentContainer = section.container.find( 'ul:first' );
if ( ! control.container.parent().is( parentContainer ) ) {
parentContainer.append( control.container );
control.renderContent();
}
- control.deferred.ready.resolve(); // @todo Better to use `embedded` instead of `ready`
+ control.deferred.embedded.resolve();
});
});
};
@@ -852,7 +872,7 @@
// Support the .dropdown class to open/close complex elements
this.container.on( 'click keydown', '.dropdown', function( event ) {
- if ( isKeydownButNotEnterEvent( event ) ) {
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
@@ -935,7 +955,7 @@
* When the control's DOM structure is ready,
* set up internal event bindings.
*/
- ready: function() {
+ ready: function() {
var control = this;
// Shortcut so that we don't have to use _.bind every time we add a callback.
_.bindAll( control, 'restoreDefault', 'removeFile', 'openFrame', 'select' );
@@ -956,7 +976,7 @@
openFrame: function( event ) {
if ( event.type === 'keydown' && 13 !== event.which ) { // enter
return;
- }
+ }
event.preventDefault();
@@ -984,11 +1004,11 @@
text: this.params.button_labels.frame_button
},
multiple: false
- });
+ });
// When a file is selected, run a callback.
this.frame.on( 'select', this.select );
- },
+ },
/**
* Callback handler for when an attachment is selected in the media modal.
@@ -1008,14 +1028,14 @@
* Reset the setting to the default value.
*/
restoreDefault: function( event ) {
- if ( event.type === 'keydown' && 13 !== event.which ) { // enter
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
event.preventDefault();
this.params.attachment = this.params.defaultAttachment;
this.setting( this.params.defaultAttachment.url );
- },
+ },
/**
* Called when the "Remove" link is clicked. Empties the setting.
@@ -1025,20 +1045,20 @@
removeFile: function( event ) {
if ( event.type === 'keydown' && 13 !== event.which ) { // enter
return;
- }
+ }
event.preventDefault();
this.params.attachment = {};
this.setting( '' );
this.renderContent(); // Not bound to setting change when emptying.
- },
+ },
// @deprecated
success: function() {},
// @deprecated
removerVisibility: function() {}
- });
+ });
/**
* A control for uploading images.
@@ -1740,10 +1760,12 @@
$( function() {
api.settings = window._wpCustomizeSettings;
api.l10n = window._wpCustomizeControlsL10n;
+ api.utils.setContainerRootElement( $( '#customize-theme-controls' ).children( 'ul:first' ) );
// Check if we can run the Customizer.
- if ( ! api.settings )
+ if ( ! api.settings ) {
return;
+ }
// Redirect to the fallback preview if any incompatibilities are found.
if ( ! $.support.postMessage || ( ! $.support.cors && api.settings.isCrossDomain ) )
@@ -1768,7 +1790,7 @@
// Expand/Collapse the main customizer customize info
$( '#customize-info' ).find( '> .accordion-section-title' ).on( 'click keydown', function( event ) {
- if ( isKeydownButNotEnterEvent( event ) ) {
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
event.preventDefault(); // Keep this AFTER the key filter above
@@ -1930,7 +1952,7 @@
if ( id && api[ type ]( id ) ) {
instance = api[ type ]( id );
// Wait until the element is embedded in the DOM
- instance.deferred.ready.done( function () {
+ instance.deferred.embedded.done( function () {
// Wait until the preview has activated and so active panels, sections, controls have been set
api.previewer.deferred.active.done( function () {
instance.focus();
@@ -1956,7 +1978,7 @@
sectionContainers = _.pluck( sections, 'container' );
rootNodes.push( panel );
appendContainer = panel.container.find( 'ul:first' );
- if ( ! areElementListsEqual( sectionContainers, appendContainer.children( '[id]' ) ) ) {
+ if ( ! api.utils.areElementListsEqual( sectionContainers, appendContainer.children( '[id]' ) ) ) {
_( sections ).each( function ( section ) {
appendContainer.append( section.container );
} );
@@ -1972,7 +1994,7 @@
rootNodes.push( section );
}
appendContainer = section.container.find( 'ul:first' );
- if ( ! areElementListsEqual( controlContainers, appendContainer.children( '[id]' ) ) ) {
+ if ( ! api.utils.areElementListsEqual( controlContainers, appendContainer.children( '[id]' ) ) ) {
_( controls ).each( function ( control ) {
appendContainer.append( control.container );
} );
@@ -1981,10 +2003,10 @@
} );
// Sort the root panels and sections
- rootNodes.sort( prioritySort );
+ rootNodes.sort( api.utils.prioritySort );
rootContainers = _.pluck( rootNodes, 'container' );
- appendContainer = $( '#customize-theme-controls' ).children( 'ul' ); // @todo This should be defined elsewhere, and to be configurable
- if ( ! areElementListsEqual( rootContainers, appendContainer.children() ) ) {
+ appendContainer = api.utils.getContainerRootElement(); // implements todo: This should be defined elsewhere, and to be configurable
+ if ( ! api.utils.areElementListsEqual( rootContainers, appendContainer.children() ) ) {
_( rootNodes ).each( function ( rootNode ) {
appendContainer.append( rootNode.container );
} );
@@ -2080,7 +2102,7 @@
// Go back to the top-level Customizer accordion.
$( '#customize-header-actions' ).on( 'click keydown', '.control-panel-back', function( event ) {
- if ( isKeydownButNotEnterEvent( event ) ) {
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
@@ -2104,7 +2126,7 @@
});
$('.collapse-sidebar').on( 'click keydown', function( event ) {
- if ( isKeydownButNotEnterEvent( event ) ) {
+ if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
return;
}
diff --git tests/qunit/index.html tests/qunit/index.html
index ce11144..df51b7a 100644
--- tests/qunit/index.html
+++ tests/qunit/index.html
@@ -31,12 +31,15 @@
+
+
+