: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
selectedChangesetStatus.validate = function( status ) {
if ( '' === status || 'auto-draft' === status ) {
defaultSelectedChangesetStatus = api.settings.changeset.currentUserCanPublish ? 'publish' : 'draft';
changesetStatus( api.settings.changeset.status );
changesetLocked( Boolean( api.settings.changeset.lockUser ) );
changesetDate( api.settings.changeset.publishDate );
selectedChangesetDate( api.settings.changeset.publishDate );
selectedChangesetStatus( '' === api.settings.changeset.status || 'auto-draft' === api.settings.changeset.status ? defaultSelectedChangesetStatus : api.settings.changeset.status );
selectedChangesetStatus.link( changesetStatus ); // Ensure that direct updates to status on server via wp.customizer.previewer.save() will update selection.
if ( '' === changesetStatus() ) { // Handle case for loading starter content.
api.each( function( setting ) {
activated( api.settings.theme.active );
expandedSection( false );
editShortcutVisibility( 'visible' );
api.bind( 'change', function() {
if ( state( 'saved' ).get() ) {
state( 'saved' ).set( false );
// Populate changeset UUID param when state becomes dirty.
if ( api.settings.changeset.branching ) {
saved.bind( function( isSaved ) {
populateChangesetUuidParam( true );
saving.bind( function( isSaving ) {
body.toggleClass( 'saving', isSaving );
trashing.bind( function( isTrashing ) {
body.toggleClass( 'trashing', isTrashing );
api.bind( 'saved', function( response ) {
state('saved').set( true );
if ( 'publish' === response.changeset_status ) {
state( 'activated' ).set( true );
activated.bind( function( to ) {
api.trigger( 'activated' );
* Populate URL with UUID via `history.replaceState()`.
* @param {boolean} isIncluded Is UUID included.
populateChangesetUuidParam = function( isIncluded ) {
var urlParser, queryParams;
// Abort on IE9 which doesn't support history management.
if ( ! history.replaceState ) {
urlParser = document.createElement( 'a' );
urlParser.href = location.href;
queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
if ( queryParams.changeset_uuid === api.settings.changeset.uuid ) {
queryParams.changeset_uuid = api.settings.changeset.uuid;
if ( ! queryParams.changeset_uuid ) {
delete queryParams.changeset_uuid;
urlParser.search = $.param( queryParams );
history.replaceState( {}, document.title, urlParser.href );
// Show changeset UUID in URL when in branching mode and there is a saved changeset.
if ( api.settings.changeset.branching ) {
changesetStatus.bind( function( newStatus ) {
populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus && 'trash' !== newStatus );
* Handles lock notice and take over request.
( function checkAndDisplayLockNotice() {
var LockedNotification = api.OverlayNotification.extend(/** @lends wp.customize~LockedNotification.prototype */{
templateId: 'customize-changeset-locked-notification',
* A notification that is displayed in a full-screen overlay with information about the locked changeset.
* @constructs wp.customize~LockedNotification
* @augments wp.customize.OverlayNotification
* @param {string} [code] - Code.
* @param {Object} [params] - Params.
initialize: function( code, params ) {
var notification = this, _code, _params;
_code = code || 'changeset_locked';
_params.containerClasses += ' notification-changeset-locked';
api.OverlayNotification.prototype.initialize.call( notification, _code, _params );
* @return {jQuery} Notification container.
var notification = this, li, data, takeOverButton, request;
returnUrl: api.settings.url['return'],
previewUrl: api.previewer.previewUrl.get(),
frontendPreviewUrl: api.previewer.getFrontendPreviewUrl()
li = api.OverlayNotification.prototype.render.call( data );
// Try to autosave the changeset now.
api.requestChangesetUpdate( {}, { autosave: true } ).fail( function( response ) {
if ( ! response.autosaved ) {
li.find( '.notice-error' ).prop( 'hidden', false ).text( response.message || api.l10n.unknownRequestFail );
takeOverButton = li.find( '.customize-notice-take-over-button' );
takeOverButton.on( 'click', function( event ) {
takeOverButton.addClass( 'disabled' );
request = wp.ajax.post( 'customize_override_changeset_lock', {
customize_theme: api.settings.theme.stylesheet,
customize_changeset_uuid: api.settings.changeset.uuid,
nonce: api.settings.nonce.override_lock
request.done( function() {
api.notifications.remove( notification.code ); // Remove self.
api.state( 'changesetLocked' ).set( false );
request.fail( function( response ) {
var message = response.message || api.l10n.unknownRequestFail;
li.find( '.notice-error' ).prop( 'hidden', false ).text( message );
request.always( function() {
takeOverButton.removeClass( 'disabled' );
request.always( function() {
* @param {Object} [args] - Args.
* @param {Object} [args.lockUser] - Lock user data.
* @param {boolean} [args.allowOverride=false] - Whether override is allowed.
function startLock( args ) {
if ( args && args.lockUser ) {
api.settings.changeset.lockUser = args.lockUser;
api.state( 'changesetLocked' ).set( true );
api.notifications.add( new LockedNotification( 'changeset_locked', {
lockUser: api.settings.changeset.lockUser,
allowOverride: Boolean( args && args.allowOverride )
// Show initial notification.
if ( api.settings.changeset.lockUser ) {
startLock( { allowOverride: true } );
// Check for lock when sending heartbeat requests.
$( document ).on( 'heartbeat-send.update_lock_notice', function( event, data ) {
data.check_changeset_lock = true;
data.changeset_uuid = api.settings.changeset.uuid;
// Handle heartbeat ticks.
$( document ).on( 'heartbeat-tick.update_lock_notice', function( event, data ) {
var notification, code = 'changeset_locked';
if ( ! data.customize_changeset_lock_user ) {
// Update notification when a different user takes over.
notification = api.notifications( code );
if ( notification && notification.lockUser.id !== api.settings.changeset.lockUser.id ) {
api.notifications.remove( code );
lockUser: data.customize_changeset_lock_user
// Handle locking in response to changeset save errors.
api.bind( 'error', function( response ) {
if ( 'changeset_locked' === response.code && response.lock_user ) {
lockUser: response.lock_user
// Set up initial notifications.
var removedQueryParams = [], autosaveDismissed = false;
* Obtain the URL to restore the autosave.
* @return {string} Customizer URL.
function getAutosaveRestorationUrl() {
var urlParser, queryParams;
urlParser = document.createElement( 'a' );
urlParser.href = location.href;
queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
if ( api.settings.changeset.latestAutoDraftUuid ) {
queryParams.changeset_uuid = api.settings.changeset.latestAutoDraftUuid;
queryParams.customize_autosaved = 'on';
queryParams['return'] = api.settings.url['return'];
urlParser.search = $.param( queryParams );
* Remove parameter from the URL.
* @param {Array} params - Parameter names to remove.
function stripParamsFromLocation( params ) {
var urlParser = document.createElement( 'a' ), queryParams, strippedParams = 0;
urlParser.href = location.href;
queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
_.each( params, function( param ) {
if ( 'undefined' !== typeof queryParams[ param ] ) {
delete queryParams[ param ];
if ( 0 === strippedParams ) {
urlParser.search = $.param( queryParams );
history.replaceState( {}, document.title, urlParser.href );
* Displays a Site Editor notification when a block theme is activated.
* @param {string} [notification] - A notification to display.
function addSiteEditorNotification( notification ) {
api.notifications.add( new api.Notification( 'site_editor_block_theme_notice', {
var notification = api.Notification.prototype.render.call( this ),
button = notification.find( 'button.switch-to-editor' );
button.on( 'click', function( event ) {
location.assign( button.data( 'action' ) );
function dismissAutosave() {
if ( autosaveDismissed ) {
wp.ajax.post( 'customize_dismiss_autosave_or_lock', {
customize_theme: api.settings.theme.stylesheet,
customize_changeset_uuid: api.settings.changeset.uuid,
nonce: api.settings.nonce.dismiss_autosave_or_lock,
autosaveDismissed = true;
* Add notification regarding the availability of an autosave to restore.
function addAutosaveRestoreNotification() {
var code = 'autosave_available', onStateChange;
// Since there is an autosave revision and the user hasn't loaded with autosaved, add notification to prompt to load autosaved version.
api.notifications.add( new api.Notification( code, {
message: api.l10n.autosaveNotice,
var li = api.Notification.prototype.render.call( this ), link;
// Handle clicking on restoration link.
link.prop( 'href', getAutosaveRestorationUrl() );
link.on( 'click', function( event ) {
location.replace( getAutosaveRestorationUrl() );
// Handle dismissal of notice.
li.find( '.notice-dismiss' ).on( 'click', dismissAutosave );
// Remove the notification once the user starts making changes.
onStateChange = function() {
api.notifications.remove( code );
api.unbind( 'change', onStateChange );
api.state( 'changesetStatus' ).unbind( onStateChange );
api.bind( 'change', onStateChange );
api.state( 'changesetStatus' ).bind( onStateChange );
if ( api.settings.changeset.autosaved ) {
api.state( 'saved' ).set( false );
removedQueryParams.push( 'customize_autosaved' );
if ( ! api.settings.changeset.branching && ( ! api.settings.changeset.status || 'auto-draft' === api.settings.changeset.status ) ) {
removedQueryParams.push( 'changeset_uuid' ); // Remove UUID when restoring autosave auto-draft.
if ( removedQueryParams.length > 0 ) {
stripParamsFromLocation( removedQueryParams );
if ( api.settings.changeset.latestAutoDraftUuid || api.settings.changeset.hasAutosaveRevision ) {
addAutosaveRestoreNotification();
var shouldDisplayBlockThemeNotification = !! parseInt( $( '#customize-info' ).data( 'block-theme' ), 10 );
if (shouldDisplayBlockThemeNotification) {
addSiteEditorNotification( api.l10n.blockThemeNotification );
// Check if preview url is valid and load the preview frame.
if ( api.previewer.previewUrl() ) {
api.previewer.previewUrl( api.settings.url.home );
saveBtn.on( 'click', function( event ) {
}).on( 'keydown', function( event ) {
if ( 9 === event.which ) { // Tab.
if ( 13 === event.which ) { // Enter.
closeBtn.on( 'keydown', function( event ) {
if ( 9 === event.which ) { // Tab.
if ( 13 === event.which ) { // Enter.
$( '.collapse-sidebar' ).on( 'click', function() {
api.state( 'paneVisible' ).set( ! api.state( 'paneVisible' ).get() );
api.state( 'paneVisible' ).bind( function( paneVisible ) {
overlay.toggleClass( 'preview-only', ! paneVisible );
overlay.toggleClass( 'expanded', paneVisible );
overlay.toggleClass( 'collapsed', ! paneVisible );
$( '.collapse-sidebar' ).attr({ 'aria-expanded': 'false', 'aria-label': api.l10n.expandSidebar });
$( '.collapse-sidebar' ).attr({ 'aria-expanded': 'true', 'aria-label': api.l10n.collapseSidebar });
// Keyboard shortcuts - esc to exit section/panel.
body.on( 'keydown', function( event ) {
var collapsedObject, expandedControls = [], expandedSections = [], expandedPanels = [];
if ( 27 !== event.which ) { // Esc.
* Abort if the event target is not the body (the default) and not inside of #customize-controls.