: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
api.control( 'background_preset', function( control ) {
var visibility, defaultValues, values, toggleVisibility, updateSettings, preset;
visibility = { // position, size, repeat, attachment.
'default': [ false, false, false, false ],
'fill': [ true, false, false, false ],
'fit': [ true, false, true, false ],
'repeat': [ true, false, false, true ],
'custom': [ true, true, true, true ]
_wpCustomizeBackground.defaults['default-position-x'],
_wpCustomizeBackground.defaults['default-position-y'],
_wpCustomizeBackground.defaults['default-size'],
_wpCustomizeBackground.defaults['default-repeat'],
_wpCustomizeBackground.defaults['default-attachment']
values = { // position_x, position_y, size, repeat, attachment.
'default': defaultValues,
'fill': [ 'left', 'top', 'cover', 'no-repeat', 'fixed' ],
'fit': [ 'left', 'top', 'contain', 'no-repeat', 'fixed' ],
'repeat': [ 'left', 'top', 'auto', 'repeat', 'scroll' ]
// @todo These should actually toggle the active state,
// but without the preview overriding the state in data.activeControls.
toggleVisibility = function( preset ) {
_.each( [ 'background_position', 'background_size', 'background_repeat', 'background_attachment' ], function( controlId, i ) {
var control = api.control( controlId );
control.container.toggle( visibility[ preset ][ i ] );
updateSettings = function( preset ) {
_.each( [ 'background_position_x', 'background_position_y', 'background_size', 'background_repeat', 'background_attachment' ], function( settingId, i ) {
var setting = api( settingId );
setting.set( values[ preset ][ i ] );
preset = control.setting.get();
toggleVisibility( preset );
control.setting.bind( 'change', function( preset ) {
toggleVisibility( preset );
if ( 'custom' !== preset ) {
updateSettings( preset );
api.control( 'background_repeat', function( control ) {
control.elements[0].unsync( api( 'background_repeat' ) );
control.element = new api.Element( control.container.find( 'input' ) );
control.element.set( 'no-repeat' !== control.setting() );
control.element.bind( function( to ) {
control.setting.set( to ? 'repeat' : 'no-repeat' );
control.setting.bind( function( to ) {
control.element.set( 'no-repeat' !== to );
api.control( 'background_attachment', function( control ) {
control.elements[0].unsync( api( 'background_attachment' ) );
control.element = new api.Element( control.container.find( 'input' ) );
control.element.set( 'fixed' !== control.setting() );
control.element.bind( function( to ) {
control.setting.set( to ? 'scroll' : 'fixed' );
control.setting.bind( function( to ) {
control.element.set( 'fixed' !== to );
// Juggle the two controls that use header_textcolor.
api.control( 'display_header_text', function( control ) {
control.elements[0].unsync( api( 'header_textcolor' ) );
control.element = new api.Element( control.container.find('input') );
control.element.set( 'blank' !== control.setting() );
control.element.bind( function( to ) {
last = api( 'header_textcolor' ).get();
control.setting.set( to ? last : 'blank' );
control.setting.bind( function( to ) {
control.element.set( 'blank' !== to );
// Add behaviors to the static front page controls.
api( 'show_on_front', 'page_on_front', 'page_for_posts', function( showOnFront, pageOnFront, pageForPosts ) {
var handleChange = function() {
var setting = this, pageOnFrontId, pageForPostsId, errorCode = 'show_on_front_page_collision';
pageOnFrontId = parseInt( pageOnFront(), 10 );
pageForPostsId = parseInt( pageForPosts(), 10 );
if ( 'page' === showOnFront() ) {
// Change previewed URL to the homepage when changing the page_on_front.
if ( setting === pageOnFront && pageOnFrontId > 0 ) {
api.previewer.previewUrl.set( api.settings.url.home );
// Change the previewed URL to the selected page when changing the page_for_posts.
if ( setting === pageForPosts && pageForPostsId > 0 ) {
api.previewer.previewUrl.set( api.settings.url.home + '?page_id=' + pageForPostsId );
// Toggle notification when the homepage and posts page are both set and the same.
if ( 'page' === showOnFront() && pageOnFrontId && pageForPostsId && pageOnFrontId === pageForPostsId ) {
showOnFront.notifications.add( new api.Notification( errorCode, {
message: api.l10n.pageOnFrontError
showOnFront.notifications.remove( errorCode );
showOnFront.bind( handleChange );
pageOnFront.bind( handleChange );
pageForPosts.bind( handleChange );
handleChange.call( showOnFront, showOnFront() ); // Make sure initial notification is added after loading existing changeset.
// Move notifications container to the bottom.
api.control( 'show_on_front', function( showOnFrontControl ) {
showOnFrontControl.deferred.embedded.done( function() {
showOnFrontControl.container.append( showOnFrontControl.getNotificationsContainerElement() );
// Add code editor for Custom CSS.
var sectionReady = $.Deferred();
api.section( 'custom_css', function( section ) {
section.deferred.embedded.done( function() {
if ( section.expanded() ) {
sectionReady.resolve( section );
section.expanded.bind( function( isExpanded ) {
sectionReady.resolve( section );
// Set up the section description behaviors.
sectionReady.done( function setupSectionDescription( section ) {
var control = api.control( 'custom_css' );
// Hide redundant label for visual users.
control.container.find( '.customize-control-title:first' ).addClass( 'screen-reader-text' );
// Close the section description when clicking the close button.
section.container.find( '.section-description-buttons .section-description-close' ).on( 'click', function() {
section.container.find( '.section-meta .customize-section-description:first' )
section.container.find( '.customize-help-toggle' )
.attr( 'aria-expanded', 'false' )
.focus(); // Avoid focus loss.
// Reveal help text if setting is empty.
if ( control && ! control.setting.get() ) {
section.container.find( '.section-meta .customize-section-description:first' )
section.container.find( '.customize-help-toggle' ).attr( 'aria-expanded', 'true' );
// Toggle visibility of Header Video notice when active state change.
api.control( 'header_video', function( headerVideoControl ) {
headerVideoControl.deferred.embedded.done( function() {
var toggleNotice = function() {
var section = api.section( headerVideoControl.section() ), noticeCode = 'video_header_not_available';
if ( headerVideoControl.active.get() ) {
section.notifications.remove( noticeCode );
section.notifications.add( new api.Notification( noticeCode, {
message: api.l10n.videoHeaderNotice
headerVideoControl.active.bind( toggleNotice );
// Update the setting validities.
api.previewer.bind( 'selective-refresh-setting-validities', function handleSelectiveRefreshedSettingValidities( settingValidities ) {
api._handleSettingValidities( {
settingValidities: settingValidities,
focusInvalidControl: false
// Focus on the control that is associated with the given setting.
api.previewer.bind( 'focus-control-for-setting', function( settingId ) {
var matchedControls = [];
api.control.each( function( control ) {
var settingIds = _.pluck( control.settings, 'id' );
if ( -1 !== _.indexOf( settingIds, settingId ) ) {
matchedControls.push( control );
// Focus on the matched control with the lowest priority (appearing higher).
if ( matchedControls.length ) {
matchedControls.sort( function( a, b ) {
return a.priority() - b.priority();
matchedControls[0].focus();
// Refresh the preview when it requests.
api.previewer.bind( 'refresh', function() {
// Update the edit shortcut visibility state.
api.state( 'paneVisible' ).bind( function( isPaneVisible ) {
if ( window.matchMedia ) {
isMobileScreen = window.matchMedia( 'screen and ( max-width: 640px )' ).matches;
isMobileScreen = $( window ).width() <= 640;
api.state( 'editShortcutVisibility' ).set( isPaneVisible || isMobileScreen ? 'visible' : 'hidden' );
if ( window.matchMedia ) {
window.matchMedia( 'screen and ( max-width: 640px )' ).addListener( function() {
var state = api.state( 'paneVisible' );
state.callbacks.fireWith( state, [ state.get(), state.get() ] );
api.previewer.bind( 'edit-shortcut-visibility', function( visibility ) {
api.state( 'editShortcutVisibility' ).set( visibility );
api.state( 'editShortcutVisibility' ).bind( function( visibility ) {
api.previewer.send( 'edit-shortcut-visibility', visibility );
function startAutosaving() {
var timeoutId, updateChangesetWithReschedule, scheduleChangesetUpdate, updatePending = false;
api.unbind( 'change', startAutosaving ); // Ensure startAutosaving only fires once.
function onChangeSaved( isSaved ) {
if ( ! isSaved && ! api.settings.changeset.autosaved ) {
api.settings.changeset.autosaved = true; // Once a change is made then autosaving kicks in.
api.previewer.send( 'autosaving' );
api.state( 'saved' ).bind( onChangeSaved );
onChangeSaved( api.state( 'saved' ).get() );
* Request changeset update and then re-schedule the next changeset update time.
updateChangesetWithReschedule = function() {
api.requestChangesetUpdate( {}, { autosave: true } ).always( function() {
scheduleChangesetUpdate();
* Schedule changeset update.
scheduleChangesetUpdate = function() {
clearTimeout( timeoutId );
timeoutId = setTimeout( function() {
updateChangesetWithReschedule();
}, api.settings.timeouts.changesetAutoSave );
// Start auto-save interval for updating changeset.
scheduleChangesetUpdate();
// Save changeset when focus removed from window.
$( document ).on( 'visibilitychange.wp-customize-changeset-update', function() {
updateChangesetWithReschedule();
// Save changeset before unloading window.
$( window ).on( 'beforeunload.wp-customize-changeset-update', function() {
updateChangesetWithReschedule();
api.bind( 'change', startAutosaving );
// Make sure TinyMCE dialogs appear above Customizer UI.
$( document ).one( 'tinymce-editor-setup', function() {
if ( window.tinymce.ui.FloatPanel && ( ! window.tinymce.ui.FloatPanel.zIndex || window.tinymce.ui.FloatPanel.zIndex < 500001 ) ) {
window.tinymce.ui.FloatPanel.zIndex = 500001;
body.addClass( 'ready' );