: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// Don't want to fire focus and click at same time.
this.setting.bind( update );
update( this.setting() );
* Render the control from its JS template, if it exists.
* The control's container must already exist in the DOM.
renderContent: function () {
var control = this, template, standardTypes, templateId, sectionId;
templateId = control.templateSelector;
// Use default content template when a standard HTML type is used,
// there isn't a more specific template existing, and the control container is empty.
if ( templateId === 'customize-control-' + control.params.type + '-content' &&
_.contains( standardTypes, control.params.type ) &&
! document.getElementById( 'tmpl-' + templateId ) &&
0 === control.container.children().length )
templateId = 'customize-control-default-content';
// Replace the container element's content with the control.
if ( document.getElementById( 'tmpl-' + templateId ) ) {
template = wp.template( templateId );
if ( template && control.container ) {
control.container.html( template( control.params ) );
// Re-render notifications after content has been re-rendered.
control.notifications.container = control.getNotificationsContainerElement();
sectionId = control.section();
if ( ! sectionId || ( api.section.has( sectionId ) && api.section( sectionId ).expanded() ) ) {
control.notifications.render();
* Add a new page to a dropdown-pages control reusing menus code for this.
addNewPage: function () {
var control = this, promise, toggle, container, input, title, select;
if ( 'dropdown-pages' !== control.params.type || ! control.params.allow_addition || ! api.Menus ) {
toggle = control.container.find( '.add-new-toggle' );
container = control.container.find( '.new-content-item-wrapper' );
input = control.container.find( '.create-item-input' );
select = control.container.find( 'select' );
input.addClass( 'invalid' );
input.removeClass( 'invalid' );
input.attr( 'disabled', 'disabled' );
// The menus functions add the page, publish when appropriate,
// and also add the new page to the dropdown-pages controls.
promise = api.Menus.insertAutoDraftPost( {
promise.done( function( data ) {
var availableItem, $content, itemTemplate;
// Prepare the new page as an available menu item.
// See api.Menus.submitNew().
availableItem = new api.Menus.AvailableItemModel( {
'id': 'post-' + data.post_id, // Used for available menu item Backbone models.
'type_label': api.Menus.data.l10n.page_label,
'object_id': data.post_id,
// Add the new item to the list of available menu items.
api.Menus.availableMenuItemsPanel.collection.add( availableItem );
$content = $( '#available-menu-items-post_type-page' ).find( '.available-menu-items-list' );
itemTemplate = wp.template( 'available-menu-item' );
$content.prepend( itemTemplate( availableItem.attributes ) );
// Focus the select control.
control.setting.set( String( data.post_id ) ); // Triggers a preview refresh and updates the setting.
// Reset the create page form.
container.slideUp( 180 );
promise.always( function() {
input.val( '' ).removeAttr( 'disabled' );
* @class wp.customize.ColorControl
* @augments wp.customize.Control
api.ColorControl = api.Control.extend(/** @lends wp.customize.ColorControl.prototype */{
isHueSlider = this.params.mode === 'hue',
picker = this.container.find( '.color-picker-hue' );
picker.val( control.setting() ).wpColorPicker({
change: function( event, ui ) {
control.setting( ui.color.h() );
picker = this.container.find( '.color-picker-hex' );
picker.val( control.setting() ).wpColorPicker({
control.setting.set( picker.wpColorPicker( 'color' ) );
control.setting.set( '' );
control.setting.bind( function ( value ) {
// Bail if the update came from the control itself.
picker.wpColorPicker( 'color', value );
// Collapse color picker when hitting Esc instead of collapsing the current section.
control.container.on( 'keydown', function( event ) {
if ( 27 !== event.which ) { // Esc.
pickerContainer = control.container.find( '.wp-picker-container' );
if ( pickerContainer.hasClass( 'wp-picker-active' ) ) {
picker.wpColorPicker( 'close' );
control.container.find( '.wp-color-result' ).focus();
event.stopPropagation(); // Prevent section from being collapsed.
* A control that implements the media modal.
* @class wp.customize.MediaControl
* @augments wp.customize.Control
api.MediaControl = api.Control.extend(/** @lends wp.customize.MediaControl.prototype */{
* When the control's DOM structure is ready,
* set up internal event bindings.
// Shortcut so that we don't have to use _.bind every time we add a callback.
_.bindAll( control, 'restoreDefault', 'removeFile', 'openFrame', 'select', 'pausePlayer' );
// Bind events, with delegation to facilitate re-rendering.
control.container.on( 'click keydown', '.upload-button', control.openFrame );
control.container.on( 'click keydown', '.upload-button', control.pausePlayer );
control.container.on( 'click keydown', '.thumbnail-image img', control.openFrame );
control.container.on( 'click keydown', '.default-button', control.restoreDefault );
control.container.on( 'click keydown', '.remove-button', control.pausePlayer );
control.container.on( 'click keydown', '.remove-button', control.removeFile );
control.container.on( 'click keydown', '.remove-button', control.cleanupPlayer );
// Resize the player controls when it becomes visible (ie when section is expanded).
api.section( control.section() ).container
.on( 'expanded', function() {
control.player.setControlsSize();
.on( 'collapsed', function() {
* Set attachment data and render content.
* Note that BackgroundImage.prototype.ready applies this ready method
* to itself. Since BackgroundImage is an UploadControl, the value
* is the attachment URL instead of the attachment ID. In this case
* we skip fetching the attachment data because we have no ID available,
* and it is the responsibility of the UploadControl to set the control's
* attachmentData before calling the renderContent method.
* @param {number|string} value Attachment
function setAttachmentDataAndRenderContent( value ) {
var hasAttachmentData = $.Deferred();
if ( control.extended( api.UploadControl ) ) {
hasAttachmentData.resolve();
value = parseInt( value, 10 );
if ( _.isNaN( value ) || value <= 0 ) {
delete control.params.attachment;
hasAttachmentData.resolve();
} else if ( control.params.attachment && control.params.attachment.id === value ) {
hasAttachmentData.resolve();
// Fetch the attachment data.
if ( 'pending' === hasAttachmentData.state() ) {
wp.media.attachment( value ).fetch().done( function() {
control.params.attachment = this.attributes;
hasAttachmentData.resolve();
// Send attachment information to the preview for possible use in `postMessage` transport.
wp.customize.previewer.send( control.setting.id + '-attachment-data', this.attributes );
hasAttachmentData.done( function() {
// Ensure attachment data is initially set (for dynamically-instantiated controls).
setAttachmentDataAndRenderContent( control.setting() );
// Update the attachment data and re-render the control when the setting changes.
control.setting.bind( setAttachmentDataAndRenderContent );
pausePlayer: function () {
this.player && this.player.pause();
cleanupPlayer: function () {
this.player && wp.media.mixin.removePlayer( this.player );
openFrame: function( event ) {
if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
* Create a media modal select frame, and store it so the instance can be reused when needed.
text: this.params.button_labels.frame_button
new wp.media.controller.Library({
title: this.params.button_labels.frame_title,
library: wp.media.query({ type: this.params.mime_type }),
// 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.
* Gets the selected image information, and sets it within the control.
// Get the attachment from the modal frame.
attachment = this.frame.state().get( 'selection' ).first().toJSON(),
mejsSettings = window._wpmejsSettings || {};
this.params.attachment = attachment;
// Set the Customizer setting; the callback takes care of rendering.
this.setting( attachment.id );
node = this.container.find( 'audio, video' ).get(0);
// Initialize audio/video previews.
this.player = new MediaElementPlayer( node, mejsSettings );
* Reset the setting to the default value.
restoreDefault: function( event ) {
if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
this.params.attachment = this.params.defaultAttachment;
this.setting( this.params.defaultAttachment.url );
* Called when the "Remove" link is clicked. Empties the setting.
* @param {Object} event jQuery Event object
removeFile: function( event ) {
if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
this.params.attachment = {};
this.renderContent(); // Not bound to setting change when emptying.
* An upload control, which utilizes the media modal.
* @class wp.customize.UploadControl
* @augments wp.customize.MediaControl
api.UploadControl = api.MediaControl.extend(/** @lends wp.customize.UploadControl.prototype */{
* Callback handler for when an attachment is selected in the media modal.
* Gets the selected image information, and sets it within the control.
// Get the attachment from the modal frame.
attachment = this.frame.state().get( 'selection' ).first().toJSON(),
mejsSettings = window._wpmejsSettings || {};
this.params.attachment = attachment;
// Set the Customizer setting; the callback takes care of rendering.
this.setting( attachment.url );
node = this.container.find( 'audio, video' ).get(0);
// Initialize audio/video previews.
this.player = new MediaElementPlayer( node, mejsSettings );
removerVisibility: function() {}
* A control for uploading images.
* This control no longer needs to do anything more
* than what the upload control does in JS.
* @class wp.customize.ImageControl
* @augments wp.customize.UploadControl
api.ImageControl = api.UploadControl.extend(/** @lends wp.customize.ImageControl.prototype */{
thumbnailSrc: function() {}
* A control for uploading background images.
* @class wp.customize.BackgroundControl
* @augments wp.customize.UploadControl
api.BackgroundControl = api.UploadControl.extend(/** @lends wp.customize.BackgroundControl.prototype */{
* When the control's DOM structure is ready,
* set up internal event bindings.
api.UploadControl.prototype.ready.apply( this, arguments );
* Callback handler for when an attachment is selected in the media modal.
* Does an additional Ajax request for setting the background context.
api.UploadControl.prototype.select.apply( this, arguments );
wp.ajax.post( 'custom-background-add', {
nonce: _wpCustomizeBackground.nonces.add,
customize_theme: api.settings.theme.stylesheet,
attachment_id: this.params.attachment.id
* A control for positioning a background image.
* @class wp.customize.BackgroundPositionControl
* @augments wp.customize.Control
api.BackgroundPositionControl = api.Control.extend(/** @lends wp.customize.BackgroundPositionControl.prototype */{
* Set up control UI once embedded in DOM and settings are created.
var control = this, updateRadios;
control.container.on( 'change', 'input[name="background-position"]', function() {
var position = $( this ).val().split( ' ' );
control.settings.x( position[0] );
control.settings.y( position[1] );