: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
container.priority = new api.Value();
container.active = new api.Value();
container.activeArgumentsQueue = [];
container.expanded = new api.Value();
container.expandedArgumentsQueue = [];
container.active.bind( function ( active ) {
var args = container.activeArgumentsQueue.shift();
args = $.extend( {}, container.defaultActiveArguments, args );
active = ( active && container.isContextuallyActive() );
container.onChangeActive( active, args );
container.expanded.bind( function ( expanded ) {
var args = container.expandedArgumentsQueue.shift();
args = $.extend( {}, container.defaultExpandedArguments, args );
container.onChangeExpanded( expanded, args );
container.deferred.embedded.done( function () {
container.setupNotifications();
container.attachEvents();
api.utils.bubbleChildValueChanges( container, [ 'priority', 'active' ] );
container.priority.set( container.params.priority );
container.active.set( container.params.active );
container.expanded.set( false );
* Get the element that will contain the notifications.
* @return {jQuery} Notification container element.
getNotificationsContainerElement: function() {
return container.contentContainer.find( '.customize-control-notifications-container:first' );
setupNotifications: function() {
var container = this, renderNotifications;
container.notifications.container = container.getNotificationsContainerElement();
// Render notifications when they change and when the construct is expanded.
renderNotifications = function() {
if ( container.expanded.get() ) {
container.notifications.render();
container.expanded.bind( renderNotifications );
container.notifications.bind( 'change', _.debounce( renderNotifications ) );
* Get the child models associated with this parent, sorting them by their priority Value.
* @param {string} parentType
* @param {string} childType
_children: function ( parentType, childType ) {
api[ childType ].each( function ( child ) {
if ( child[ parentType ].get() === parent.id ) {
children.sort( api.utils.prioritySort );
* To override by subclass, to return whether the container has active children.
isContextuallyActive: function () {
throw new Error( 'Container.isContextuallyActive() must be overridden in a subclass.' );
* Active state change handler.
* Shows the container if it is active, hides it if not.
* To override by subclass, update the container's UI to reflect the provided active state.
* @param {boolean} active - The active state to transiution to.
* @param {Object} [args] - Args.
* @param {Object} [args.duration] - The duration for the slideUp/slideDown animation.
* @param {boolean} [args.unchanged] - Whether the state is already known to not be changed, and so short-circuit with calling completeCallback early.
* @param {Function} [args.completeCallback] - Function to call when the slideUp/slideDown has completed.
onChangeActive: function( active, args ) {
headContainer = construct.headContainer,
duration, expandedOtherPanel;
if ( args.completeCallback ) {
duration = ( 'resolved' === api.previewer.deferred.active.state() ? args.duration : 0 );
if ( construct.extended( api.Panel ) ) {
// If this is a panel is not currently expanded but another panel is expanded, do not animate.
api.panel.each(function ( panel ) {
if ( panel !== construct && panel.expanded() ) {
expandedOtherPanel = panel;
// Collapse any expanded sections inside of this panel first before deactivating.
_.each( construct.sections(), function( section ) {
section.collapse( { duration: 0 } );
if ( ! $.contains( document, headContainer.get( 0 ) ) ) {
// If the element is not in the DOM, then jQuery.fn.slideUp() does nothing.
// In this case, a hard toggle is required instead.
headContainer.toggle( active );
if ( args.completeCallback ) {
headContainer.slideDown( duration, args.completeCallback );
if ( construct.expanded() ) {
completeCallback: function() {
headContainer.slideUp( duration, args.completeCallback );
headContainer.slideUp( duration, args.completeCallback );
* @param {boolean} active
* @param {Object} [params]
* @return {boolean} False if state already applied.
_toggleActive: function ( active, params ) {
if ( ( active && this.active.get() ) || ( ! active && ! this.active.get() ) ) {
self.onChangeActive( self.active.get(), params );
params.unchanged = false;
this.activeArgumentsQueue.push( params );
this.active.set( active );
* @param {Object} [params]
* @return {boolean} False if already active.
activate: function ( params ) {
return this._toggleActive( true, params );
* @param {Object} [params]
* @return {boolean} False if already inactive.
deactivate: function ( params ) {
return this._toggleActive( false, params );
* To override by subclass, update the container's UI to reflect the provided active state.
onChangeExpanded: function () {
throw new Error( 'Must override with subclass.' );
* Handle the toggle logic for expand/collapse.
* @param {boolean} expanded - The new state to apply.
* @param {Object} [params] - Object containing options for expand/collapse.
* @param {Function} [params.completeCallback] - Function to call when expansion/collapse is complete.
* @return {boolean} False if state already applied or active state is false.
_toggleExpanded: function( expanded, params ) {
var instance = this, previousCompleteCallback;
previousCompleteCallback = params.completeCallback;
// Short-circuit expand() if the instance is not active.
if ( expanded && ! instance.active() ) {
api.state( 'paneVisible' ).set( true );
params.completeCallback = function() {
if ( previousCompleteCallback ) {
previousCompleteCallback.apply( instance, arguments );
instance.container.trigger( 'expanded' );
instance.container.trigger( 'collapsed' );
if ( ( expanded && instance.expanded.get() ) || ( ! expanded && ! instance.expanded.get() ) ) {
instance.onChangeExpanded( instance.expanded.get(), params );
params.unchanged = false;
instance.expandedArgumentsQueue.push( params );
instance.expanded.set( expanded );
* @param {Object} [params]
* @return {boolean} False if already expanded or if inactive.
expand: function ( params ) {
return this._toggleExpanded( true, params );
* @param {Object} [params]
* @return {boolean} False if already collapsed.
collapse: function ( params ) {
return this._toggleExpanded( false, params );
* Animate container state change if transitions are supported by the browser.
* @param {function} completeCallback Function to be called after transition is completed.
_animateChangeExpanded: function( completeCallback ) {
// Return if CSS transitions are not supported or if reduced motion is enabled.
if ( ! normalizedTransitionendEventName || isReducedMotion ) {
// Schedule the callback until the next tick to prevent focus loss.
if ( completeCallback ) {
content = construct.contentContainer,
overlay = content.closest( '.wp-full-overlay' ),
elements, transitionEndCallback, transitionParentPane;
// Determine set of elements that are affected by the animation.
elements = overlay.add( content );
if ( ! construct.panel || '' === construct.panel() ) {
transitionParentPane = true;
} else if ( api.panel( construct.panel() ).contentContainer.hasClass( 'skip-transition' ) ) {
transitionParentPane = true;
transitionParentPane = false;
if ( transitionParentPane ) {
elements = elements.add( '#customize-info, .customize-pane-parent' );
// Handle `transitionEnd` event.
transitionEndCallback = function( e ) {
if ( 2 !== e.eventPhase || ! $( e.target ).is( content ) ) {
content.off( normalizedTransitionendEventName, transitionEndCallback );
elements.removeClass( 'busy' );
if ( completeCallback ) {
content.on( normalizedTransitionendEventName, transitionEndCallback );
elements.addClass( 'busy' );
// Prevent screen flicker when pane has been scrolled before expanding.
var container = content.closest( '.wp-full-overlay-sidebar-content' ),
currentScrollTop = container.scrollTop(),
previousScrollTop = content.data( 'previous-scrollTop' ) || 0,
expanded = construct.expanded();
if ( expanded && 0 < currentScrollTop ) {
content.css( 'top', currentScrollTop + 'px' );
content.data( 'previous-scrollTop', currentScrollTop );
} else if ( ! expanded && 0 < currentScrollTop + previousScrollTop ) {
content.css( 'top', previousScrollTop - currentScrollTop + 'px' );
container.scrollTop( previousScrollTop );
* is documented using @borrows in the constructor.
* Return the container html, generated from its JS template, if it exists.
getContainer: function () {
if ( 0 !== $( '#tmpl-' + container.templateSelector ).length ) {
template = wp.template( container.templateSelector );
template = wp.template( 'customize-' + container.containerType + '-default' );
if ( template && container.container ) {
return template( _.extend(
* Find content element which is displayed when the section is expanded.
* After a construct is initialized, the return value will be available via the `contentContainer` property.
* By default the element will be related it to the parent container with `aria-owns` and detached.
* Custom panels and sections (such as the `NewMenuSection`) that do not have a sliding pane should
* just return the content element without needing to add the `aria-owns` element or detach it from
* the container. Such non-sliding pane custom sections also need to override the `onChangeExpanded`
* method to handle animating the panel/section into and out of view.
* @return {jQuery} Detached content element.
container = construct.container,
content = container.find( '.accordion-section-content, .control-panel-content' ).first(),
contentId = 'sub-' + container.attr( 'id' ),
ownedElements = contentId,
alreadyOwnedElements = container.attr( 'aria-owns' );
if ( alreadyOwnedElements ) {
ownedElements = ownedElements + ' ' + alreadyOwnedElements;
container.attr( 'aria-owns', ownedElements );
return content.detach().attr( {
'class': 'customize-pane-child ' + content.attr( 'class' ) + ' ' + container.attr( 'class' )
api.Section = Container.extend(/** @lends wp.customize.Section.prototype */{
containerType: 'section',
containerParent: '#customize-theme-controls',
containerPaneParent: '.customize-pane-parent',
* @constructs wp.customize.Section
* @augments wp.customize~Container
* @param {string} id - The ID for the section.
* @param {Object} options - Options.
* @param {string} options.title - Title shown when section is collapsed and expanded.
* @param {string} [options.description] - Description shown at the top of the section.
* @param {number} [options.priority=100] - The sort priority for the section.
* @param {string} [options.type=default] - The type of the section. See wp.customize.sectionConstructor.
* @param {string} [options.content] - The markup to be used for the section container. If empty, a JS template is used.
* @param {boolean} [options.active=true] - Whether the section is active or not.
* @param {string} options.panel - The ID for the panel this section is associated with.
* @param {string} [options.customizeAction] - Additional context information shown before the section title when expanded.
* @param {Object} [options.params] - Deprecated wrapper for the above properties.
initialize: function ( id, options ) {
var section = this, params;
params = options.params || options;
// Look up the type if one was not supplied.
_.find( api.sectionConstructor, function( Constructor, type ) {
if ( Constructor === section.constructor ) {
Container.prototype.initialize.call( section, id, params );
section.panel = new api.Value();
section.panel.bind( function ( id ) {
$( section.headContainer ).toggleClass( 'control-subsection', !! id );
section.panel.set( section.params.panel || '' );
api.utils.bubbleChildValueChanges( section, [ 'panel' ] );
section.deferred.embedded.done( function () {
* Embed the container in the DOM when any parent panel is ready.
section.containerParent = api.ensure( section.containerParent );
// Watch for changes to the panel state.
inject = function ( panelId ) {
// 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.embedded.done( function () {
parentContainer = panel.contentContainer;
if ( ! section.headContainer.parent().is( parentContainer ) ) {
parentContainer.append( section.headContainer );
if ( ! section.contentContainer.parent().is( section.headContainer ) ) {
section.containerParent.append( section.contentContainer );