: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Bulk Smush Background Optimization.
import Fetcher from '../utils/fetcher';
import tracker from '../utils/tracker';
import SmushProgress from '../common/progressbar';
import {GlobalStats, UpsellManger} from "../common/globalStats";
import loopbackTester from '../loopback-tester';
if (!window.wp_smush_msgs) {
const $ = document.querySelector.bind(document);
const NO_FREE_LIMITED = 50;
* Handle Background Process.
const BackgroundProcess = () => {
return Fetcher.background[action]();
return Fetcher.background.initState();
* Background Optimization.
const BackgroundSmush = (() => {
const startBtn = window.wp_smushit_data && window.wp_smushit_data.bo_stats && $('.wp-smush-bo-start');
const BO = new BackgroundProcess();
const bulkWrapper = $('.bulk-smush-wrapper');
const reScanImagesButton = $('.wp-smush-scan');
let cancellationInProgress = false;
let statusSyncInProgress = false;
let statSyncInProgress = false;
intervalHandle = setInterval(() => {
if (statusSyncInProgress) {
statusSyncInProgress = true;
const statusSynced = this.syncBackgroundStatus();
// Do a full update every nth time
statusSynced.then(() => {
if (!statSyncInProgress) {
this.syncStats().then(() => {
statSyncInProgress = false;
statSyncInProgress = true;
statusSynced.finally(() => {
statusSyncInProgress = false;
GlobalStats.setBoStats( {
this.resetBOStatsOnStart();
loopbackTester.performTest().then( ( res ) => {
const isLoopbackHealthy = res?.loopback;
if ( isLoopbackHealthy ) {
this.showLoopbackErrorModal();
} ).catch( ( error ) => {
console.error( 'Error:', error );
this.showLoopbackErrorModal();
BO.handle('start').then((res) => {
const updatedStats = this.updateStats(res.data, false);
GlobalStats.renderStats();
this.showFailureNotice( res );
this.onStartFailure( res );
showFailureNotice( res ) {
WP_Smush.helpers.showNotice( res, {
showLoopbackErrorModal() {
const loopbackErrorModal = document.getElementById( 'smush-loopback-error-dialog' );
if ( ! loopbackErrorModal || ! window.SUI ) {
// Cache current process type.
loopbackErrorModal.dataset.processType = 'smush';
WP_Smush.helpers.showModal( loopbackErrorModal.id );
* Initial state when load the Bulk Smush page while BO is running.
if (!GlobalStats.getBoStats().in_processing) {
BO.initState().then((res) => {
this.updateStats(res.data, false);
if ( res.data.errors && ! Object.keys( GlobalStats.getErrors() ).length ) {
GlobalStats.setErrors( res.data.errors );
GlobalStats.renderStats();
WP_Smush.helpers.showNotice( res );
cancellationInProgress = true;
this.setCancelButtonStateToStarted();
BO.handle('cancel').then((res) => {
WP_Smush.helpers.showNotice( res );
SmushProgress.close().update(0, GlobalStats.getBoStats().total_items);
SmushProgress.update(GlobalStats.getBoStats().processed_items, GlobalStats.getBoStats().total_items);
* @param {Object} newStats Included increment stats and new BO stats.
updateStats(newStats, updateGlobal) {
// Make sure we have processed_stats/errors property (not added by default when start).
newStats.global_stats = newStats.global_stats || {};
newStats.errors = newStats.errors || {};
if ( ! GlobalStats.isChangedStats( newBoStats ) ) {
GlobalStats.setBoStats(newBoStats);
GlobalStats.setGlobalStats(global_stats);
GlobalStats.setErrors( errors );
if (100 === GlobalStats.getGlobalStats().percent_optimized) {
// If the last item was getting processed when the user cancelled then the process might have completed
GlobalStats.setBoStats( {
// Update status of boStats.
GlobalStats.setBoStats( {
// Reset and hide progress bar.
// Bulk is cancelled, show bulk desc.
SmushProgress.showBulkSmushDescription();
cancellationInProgress = false;
// Render completed message.
// Show completed message.
const processedWrapper = bulkWrapper.querySelector('.wp-smush-all-done');
if ( GlobalStats.getBoStats().failed_items ) {
let completeMessage = wp_smush_msgs.all_failed;
if ( ! this.isFailedAllItems() ) {
completeMessage = wp_smush_msgs.error_in_bulk.replace( '{{smushed}}', GlobalStats.getBoStats().total_items - GlobalStats.getBoStats().failed_items )
.replace('{{total}}', GlobalStats.getBoStats().total_items )
.replace('{{errors}}', GlobalStats.getBoStats().failed_items );
processedWrapper.querySelector('p').innerHTML = completeMessage;
processedWrapper.classList.remove('sui-notice-success', 'sui-notice-warning');
const noticeType = this.getNoticeType();
const noticeIcon = 'warning' === noticeType ? 'info' : 'check-tick';
const iconElement = processedWrapper.querySelector('.sui-notice-icon');
processedWrapper.classList.add( 'sui-notice-' + noticeType );
iconElement.classList.remove('sui-icon-check-tick', 'sui-icon-info');
iconElement.classList.add( 'sui-icon-' + noticeIcon );
processedWrapper.querySelector('p').innerHTML = wp_smush_msgs.all_smushed;
processedWrapper.classList.remove('sui-hidden');
return GlobalStats.getBoStats().failed_items === GlobalStats.getBoStats().total_items;
return this.isFailedAllItems() ? 'warning' : 'success';
// Reset and hide progress bar.
// Bulk is completed, hide bulk desc.
SmushProgress.hideBulkSmushDescription();
// Show completed message.
this.showCompletedMessage();
// Reset the progress when we finish so the next smushing starts from zero.
SmushProgress.update(0, GlobalStats.getBoStats().total_items);
this.syncStats(() => this.onCompletedBulk());
syncStats(onComplete = () => false) {
return BO.handle('getStats').then((res) => {
const responseErrors = res.data.errors || {};
this.updateStats( { global_stats: res.data, errors: responseErrors }, true );
GlobalStats.renderStats();
if ( res.data.content ) {
$('#wp-smush-bulk-content').innerHTML = res.data.content;
WP_Smush.helpers.showNotice( res );
}).catch( (error) => console.log('error', error));
return BO.handle('getStatus').then((res) => {
if ((res.data || {}).in_process_notice) {
SmushProgress.setNotice( res.data.in_process_notice );
if ( this.updateStats(res.data, false) ) {
SmushProgress.update(GlobalStats.getBoStats().processed_items, GlobalStats.getBoStats().total_items);
if (! GlobalStats.getBoStats().is_cancelled && ! GlobalStats.getBoStats().is_completed) {
GlobalStats.renderStats();
if (GlobalStats.getBoStats().is_cancelled && !cancellationInProgress) {
// Cancelled on server side
} else if (GlobalStats.getBoStats().is_completed) {
} else if ( GlobalStats.getBoStats().is_dead ) {
WP_Smush.helpers.showNotice( res );
startBtn.setAttribute('disabled', '');
// Disable re-check images.
reScanImagesButton && reScanImagesButton.setAttribute('disabled', '');
$('.wp-smush-restore').setAttribute('disabled', '');
UpsellManger.maybeShowCDNUpsellForPreSiteOnStart();
this.hideBulkSmushFailedNotice();
this.setCancelButtonStateToInitial();
hideBulkSmushFailedNotice() {
const bulkSmushFailedNotice = document.querySelector( '.wp-smush-inline-retry-bulk-smush-notice' );
if ( bulkSmushFailedNotice ) {
bulkSmushFailedNotice.parentElement.classList.add( 'sui-hidden' );
clearInterval(intervalHandle);
startBtn.removeAttribute('disabled');
// Reset and hide Progress Bar.
// Disable re-check images.
reScanImagesButton && reScanImagesButton.removeAttribute('disabled', '');
$('.wp-smush-restore').removeAttribute('disabled', '');
UpsellManger.maybeShowCDNUpsellForPreSiteOnCompleted();
SmushProgress.showBulkSmushDescription();
this.showRetryBulkSmushModal();
showRetryBulkSmushModal() {
const retryModalElement = document.getElementById( 'smush-retry-bulk-smush-notice' );
if ( ! window.SUI || ! retryModalElement ) {
retryModalElement.querySelector('.smush-retry-bulk-smush-notice-button').onclick = (e) => {
window.SUI.closeModal( 'smush-retry-bulk-smush-notice' );
'smush-retry-bulk-smush-notice',
startBtn.onclick = () => {
const requiredScanMedia = startBtn.classList.contains('wp-smush-scan-and-bulk-smush');
if ( requiredScanMedia ) {
const triggerBulkSmushButton = document.querySelector( '.wp-smush-inline-retry-bulk-smush-notice .wp-smush-trigger-bulk-smush' );
if ( triggerBulkSmushButton ) {
triggerBulkSmushButton.addEventListener( 'click', ( e ) => {
// If BO is running, initial new state.
setCancelButtonStateToInitial() {
SmushProgress.setCancelButtonLabel( wp_smush_msgs.cancel );
SmushProgress.setOnCancelCallback( this.cancel.bind(this) );
setCancelButtonStateToStarted() {
SmushProgress.setCancelButtonLabel( wp_smush_msgs.cancelling );
SmushProgress.setOnCancelCallback( () => false );
BackgroundSmush && BackgroundSmush.init();
* For globalStats, we will need to update it on reload and after re-checking images,
* 1. On finish, we handled via BackgroundSmush.syncStats -> BackgroundSmush.updateStats
* 2. On reload or after re-checking images, we need to update globalStats from global variable:
// Update global stats after re-checking images.
document.addEventListener('wpSmushAfterRecheckImages', function(){
GlobalStats.updateGlobalStatsFromSmushScriptData();
document.addEventListener('backgroundBulkSmushOnScanCompleted', function(){
if ( ! BackgroundSmush ) {
BackgroundSmush.initState();