This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,62 @@
import '../scss/app.scss';
/**
* Admin modules
*/
const WP_Smush = WP_Smush || {};
window.WP_Smush = WP_Smush;
/**
* IE polyfill for includes.
*
* @since 3.1.0
* @param {string} search
* @param {number} start
* @return {boolean} Returns true if searchString appears as a substring of the result of converting this
* object to a String, at one or more positions that are
* greater than or equal to position; otherwise, returns false.
*/
if ( ! String.prototype.includes ) {
String.prototype.includes = function( search, start ) {
if ( typeof start !== 'number' ) {
start = 0;
}
if ( start + search.length > this.length ) {
return false;
}
return this.indexOf( search, start ) !== -1;
};
}
require( './modules/helpers' );
require( './modules/admin' );
require( './modules/admin-common' );
require( './modules/bulk-smush' );
require( './modules/nextgen-bulk' );
require( './modules/background-process' );
require( './common/media-library-scanner' );
require( './modules/media-library-scanner-on-bulk-smush' );
require( './modules/media-library-scanner-on-dashboard' );
require( './modules/onboarding' );
require( './modules/directory-smush' );
require( './smush/cdn' );
require( './smush/webp' );
require( './smush/lazy-load' );
require( './modules/bulk-restore' );
require( './smush/settings' );
require( './smush/product-analytics' );
/**
* Notice scripts.
*
* Notices are used in the following functions:
*
* @used-by \Smush\Core\Modules\Smush::smush_updated()
* @used-by \Smush\Core\Integrations\S3::3_support_required_notice()
* @used-by \Smush\App\Abstract_Page::installation_notice()
*
* TODO: should this be moved out in a separate file like common.scss?
*/
require( './modules/notice' );

View File

@ -0,0 +1,222 @@
/* global WP_Smush */
export const UpsellManger = ( () => {
return {
maybeShowCDNActivationNotice() {
if ( ! wp_smush_msgs.smush_cdn_activation_notice ) {
return;
}
WP_Smush.helpers.renderActivationCDNNotice( wp_smush_msgs.smush_cdn_activation_notice );
},
maybeShowCDNUpsellForPreSiteOnStart() {
const upsellCdn = document.querySelector( '.wp-smush-upsell-cdn' );
if ( upsellCdn ) {
upsellCdn.querySelector( 'p' ).innerHTML = wp_smush_msgs.processing_cdn_for_free;
upsellCdn.classList.remove( 'sui-hidden' );
}
},
maybeShowCDNUpsellForPreSiteOnCompleted() {
const upsellCdn = document.querySelector( '.wp-smush-upsell-cdn' );
if ( upsellCdn ) {
upsellCdn.querySelector( 'p' ).innerHTML = wp_smush_msgs.processed_cdn_for_free;
upsellCdn.classList.remove( 'sui-hidden' );
}
}
};
} )();
export const GlobalStats = ( () => {
const $ = document.querySelector.bind( document );
const summarySmush = $( '.sui-summary-smush-metabox' );
if ( ! summarySmush ) {
return {};
}
// Cache initial stats.
let boStats = window.wp_smushit_data.bo_stats;
let globalStats = {
count_images: 0,
count_total: 0,
count_resize: 0,
count_skipped: 0,
count_smushed: 0,
savings_bytes: 0,
savings_resize: 0,
size_after: 0,
size_before: 0,
savings_percent: 0,
percent_grade: 'sui-grade-dismissed',
percent_metric: 0,
percent_optimized: 0,
remaining_count: 0,
human_bytes: '',
savings_conversion_human: '',
savings_conversion: 0,
};
const imageScore = $( '#smush-image-score' );
const logBulk = $( '.smush-final-log .smush-bulk-errors' );
const bulkSmushCountContent = $( '#wp-smush-bulk-content' );
let allErrors = {};
const generateGlobalStatsFromSmushData = ( smushScriptData ) => {
window.wp_smushit_data = Object.assign( window.wp_smushit_data, smushScriptData || {} );
globalStats = Object.keys( globalStats ).reduce( function( newStats, key ) {
if ( key in window.wp_smushit_data ) {
newStats[ key ] = window.wp_smushit_data[ key ];
}
return newStats;
}, {} );
}
generateGlobalStatsFromSmushData( window.wp_smushit_data );
return {
isChangedStats( newBoStats ) {
const primaryKeys = [ 'total_items', 'processed_items', 'failed_items', 'is_cancelled', 'is_completed' ];
return primaryKeys.some( ( key ) => {
return newBoStats[ key ] !== boStats[ key ];
} );
},
setBoStats( newBoStats ) {
boStats = Object.assign( boStats, newBoStats || {} );
return this;
},
getBoStats() {
return boStats;
},
setGlobalStats( newGlobalStats ) {
globalStats = Object.assign( globalStats, newGlobalStats || {} );
return this;
},
getGlobalStats() {
return globalStats;
},
/**
* Circle progress bar.
*/
renderScoreProgress() {
imageScore.className = imageScore.className.replace( /(^|\s)sui-grade-\S+/g, '' );
imageScore.classList.add( globalStats.percent_grade );
imageScore.dataset.score = globalStats.percent_optimized;
imageScore.querySelector( '.sui-circle-score-label' ).innerHTML = globalStats.percent_optimized;
imageScore
.querySelector( 'circle:last-child' )
.setAttribute( 'style', '--metric-array:' + ( 2.63893782902 * globalStats.percent_metric ) + ' ' + ( 263.893782902 - globalStats.percent_metric ) );
},
/**
* Summary detail - center meta box.
*/
renderSummaryDetail() {
this.renderTotalStats();
this.renderResizedStats();
this.renderConversionSavings();
},
renderTotalStats() {
// Total savings.
summarySmush.querySelector( '.sui-summary-large.wp-smush-stats-human' ).innerHTML = globalStats.human_bytes;
// Update the savings percent.
summarySmush.querySelector( '.wp-smush-savings .wp-smush-stats-percent' ).innerHTML = globalStats.savings_percent;
// To total smushed images files.
summarySmush.querySelector( '.wp-smush-count-total .wp-smush-total-optimised' ).innerHTML = globalStats.count_images;
},
renderResizedStats() {
const resizeCountElement = summarySmush.querySelector( '.wp-smush-count-resize-total' );
if ( ! resizeCountElement ) {
return;
}
if ( globalStats.count_resize > 0 ) {
resizeCountElement.classList.remove( 'sui-hidden' );
} else {
resizeCountElement.classList.add( 'sui-hidden' );
}
resizeCountElement.querySelector( '.wp-smush-total-optimised' ).innerHTML = globalStats.count_resize;
},
renderConversionSavings() {
// PNG2JPG Savings.
const conversionSavingsElement = summarySmush.querySelector( '.smush-conversion-savings .wp-smush-stats' );
if ( ! conversionSavingsElement ) {
return;
}
conversionSavingsElement.innerHTML = globalStats.savings_conversion_human;
if ( globalStats.savings_conversion > 0 ) {
conversionSavingsElement.parentElement.classList.remove( 'sui-hidden' );
} else {
conversionSavingsElement.parentElement.classList.add( 'sui-hidden' );
}
},
renderBoxSummary() {
// Circle core progress.
this.renderScoreProgress();
// Summary detail.
this.renderSummaryDetail();
},
setErrors( newErrors ) {
allErrors = newErrors || {};
},
getErrors() {
return allErrors;
},
renderErrors() {
if ( ! Object.keys( allErrors ).length || ! boStats.is_completed ) {
return;
}
const errors = [];
const errorKeys = Object.keys( allErrors );
// Cache error code to avoid double upsell notice.
let showAnimatedUpsell = false;
errorKeys.map( ( image_id, index ) => {
const upsellErrorCode = allErrors[ image_id ].error_code;
if ( index < 5 && 'animated' === upsellErrorCode ) {
showAnimatedUpsell = true;
}
errors.push( WP_Smush.helpers.prepareBulkSmushErrorRow(
allErrors[ image_id ].error_message,
allErrors[ image_id ].file_name,
allErrors[ image_id ].thumbnail,
image_id,
'media',
allErrors[ image_id ].error_code,
) );
}
);
logBulk.innerHTML = errors.join( '' );
logBulk.parentElement.classList.remove( 'sui-hidden' );
logBulk.parentElement.style.display = null;
// Show view all.
if ( errorKeys.length > 1 ) {
$( '.smush-bulk-errors-actions' ).classList.remove( 'sui-hidden' );
}
// Show animated upsell CDN if user disabled CDN and found an animated error in first 5 errors.
if ( showAnimatedUpsell ) {
UpsellManger.maybeShowCDNActivationNotice();
}
},
resetAndHideBulkErrors() {
if ( ! logBulk ) {
return;
}
this.resetErrors();
logBulk.parentElement.classList.add( 'sui-hidden' );
logBulk.innerHTML = '';
},
resetErrors() {
allErrors = {};
},
renderStats() {
// Render Smush box summary.
this.renderBoxSummary();
// Render Errors.
this.renderErrors();
},
maybeUpdateBulkSmushCountContent( newContent ) {
if ( newContent && bulkSmushCountContent ) {
bulkSmushCountContent.innerHTML = newContent;
}
},
updateGlobalStatsFromSmushScriptData( smushScriptData ) {
this.maybeUpdateBulkSmushCountContent( smushScriptData?.content );
generateGlobalStatsFromSmushData( smushScriptData );
return this;
},
};
} )();

View File

@ -0,0 +1,267 @@
/* global WP_Smush */
/**
* Abstract Media Library Scanner.
*
*/
import Fetcher from '../utils/fetcher';
import { scanProgressBar } from './progressbar';
import { GlobalStats } from './globalStats';
const { __ } = wp.i18n;
export default class MediaLibraryScanner {
constructor() {
this.autoSyncDuration = 1500;
this.progressTimeoutId = 0;
this.scanProgress = scanProgressBar( this.autoSyncDuration );
}
startScan( optimizeOnScanCompleted = false ) {
this.onStart();
return Fetcher.scanMediaLibrary.start( optimizeOnScanCompleted ).then( ( response ) => {
if ( ! response?.success ) {
this.onStartFailure( response );
return;
}
this.showProgressBar().autoSyncStatus();
} );
}
onStart() {
// Do nothing at the moment.
}
onStartFailure( response ) {
WP_Smush.helpers.showNotice( response, {
showdismiss: true,
autoclose: false,
} );
}
showProgressBar() {
this.onShowProgressBar();
this.scanProgress.reset().setOnCancelCallback( this.showStopScanningModal.bind( this ) ).open();
return this;
}
onShowProgressBar() {
// Do nothing at the moment.
}
showStopScanningModal() {
if ( ! window.SUI ) {
return;
}
this.onShowStopScanningModal();
window.SUI.openModal(
'smush-stop-scanning-dialog',
'wpbody-content',
undefined,
false
);
}
onShowStopScanningModal() {
this.registerCancelProcessEvent();
}
registerCancelProcessEvent() {
const stopScanButton = document.querySelector( '.smush-stop-scanning-dialog-button' );
if ( ! stopScanButton ) {
return;
}
stopScanButton.addEventListener( 'click', this.cancelProgress.bind( this ) );
}
closeStopScanningModal() {
if ( ! window.SUI ) {
return;
}
const stopScanningElement = document.querySelector( '#smush-stop-scanning-dialog' );
const isModalClosed = ( ! stopScanningElement ) || ! stopScanningElement.classList.contains( 'sui-content-fade-in' );
if ( isModalClosed ) {
return;
}
window.SUI.closeModal( 'smush-stop-scanning-dialog' );
}
closeProgressBar() {
this.onCloseProgressBar();
this.scanProgress.close();
}
onCloseProgressBar() {
// Do nothing at the moment.
}
updateProgress( stats ) {
const totalItems = this.getTotalItems( stats );
const processedItems = this.getProcessedItems( stats );
return this.scanProgress.update( processedItems, totalItems );
}
getProcessedItems( stats ) {
return stats?.processed_items || 0;
}
getTotalItems( stats ) {
return stats?.total_items || 0;
}
cancelProgress() {
this.scanProgress.setCancelButtonOnCancelling();
return Fetcher.scanMediaLibrary.cancel().then( ( response ) => {
if ( ! response?.success ) {
this.onCancelFailure( response );
return;
}
this.onCancelled( response.data );
} );
}
onCancelFailure( response ) {
WP_Smush.helpers.showNotice( response, {
showdismiss: true,
autoclose: false,
} );
this.scanProgress.resetCancelButtonOnFailure();
}
getErrorProgressMessage() {
return __( 'Unfortunately the scan hit an error due to limited resources on your site, we have adjusted the scan to use fewer resources the next time.', 'wp-smushit' );
}
onDead( stats ) {
this.clearProgressTimeout();
this.closeProgressBar();
this.closeStopScanningModal();
this.showRetryScanModal();
}
showRetryScanModal() {
const retryScanModalElement = document.getElementById( 'smush-retry-scan-notice' );
if ( ! window.SUI || ! retryScanModalElement ) {
return;
}
retryScanModalElement.querySelector('.smush-retry-scan-notice-button').onclick = (e) => {
window.SUI.closeModal( 'smush-retry-scan-notice' );
const recheckImagesBtn = document.querySelector( '.wp-smush-scan' );
if ( ! recheckImagesBtn ) {
return;
}
e.preventDefault();
recheckImagesBtn.click();
}
window.SUI.openModal(
'smush-retry-scan-notice',
'wpbody-content',
undefined,
false
);
}
onCompleted( stats ) {
this.onFinish( stats );
}
onCancelled( stats ) {
this.onFinish( stats );
}
onFinish( stats ) {
this.clearProgressTimeout();
const globalStats = stats?.global_stats;
this.updateGlobalStatsAndBulkContent( globalStats );
this.closeProgressBar();
this.closeStopScanningModal();
}
clearProgressTimeout() {
if ( this.progressTimeoutId ) {
clearTimeout( this.progressTimeoutId );
}
}
updateGlobalStatsAndBulkContent( globalStats ) {
if ( ! globalStats ) {
return;
}
GlobalStats.updateGlobalStatsFromSmushScriptData( globalStats );
GlobalStats.renderStats();
}
getStatus() {
return Fetcher.scanMediaLibrary.getScanStatus();
}
autoSyncStatus() {
const startTime = new Date().getTime();
this.getStatus().then( ( response ) => {
if ( ! response?.success ) {
return;
}
const stats = response.data;
if ( stats.is_dead ) {
this.onDead( response.data );
return;
}
this.beforeUpdateStatus( stats );
this.updateProgress( stats ).then( () => {
this.scanProgress.increaseDurationToHaveChangeOnProgress( new Date().getTime() - startTime );
const isCompleted = stats?.is_completed;
if ( isCompleted ) {
this.onCompleted( stats );
return;
}
const isCancelled = stats?.is_cancelled;
if ( isCancelled ) {
this.onCancelled( stats );
return;
}
this.progressTimeoutId = setTimeout( () => this.autoSyncStatus(), this.autoSyncDuration );
} );
} );
}
beforeUpdateStatus() {
// Do nothing at the moment.
}
setInnerText( element, newText ) {
if ( ! element ) {
return;
}
element.dataset.originalText = element.dataset.originalText || element.innerText.trim();
element.innerText = newText;
}
revertInnerText( element ) {
if ( ! element || ! element.dataset.originalText ) {
return;
}
element.innerText = element.dataset.originalText.trim();
}
hideAnElement( element ) {
if ( element ) {
element.classList.add( 'sui-hidden' );
}
}
showAnElement( element ) {
if ( element ) {
element.classList.remove( 'sui-hidden' );
}
}
}

View File

@ -0,0 +1,302 @@
/* global WP_Smush */
/**
* SmushProgressBar
* TODO: Update progressbar for free version.
*
* @param autoSyncDuration
*/
export const scanProgressBar = ( autoSyncDuration ) => {
const { __, _n } = wp.i18n;
const scanProgressBar = document.querySelector( '.wp-smush-scan-progress-bar-wrapper' );
const percentElement = scanProgressBar.querySelector( '.wp-smush-progress-percent' );
const progressElement = scanProgressBar.querySelector( '.wp-smush-progress-inner' );
const remainingTimeElement = scanProgressBar.querySelector( '.wp-smush-remaining-time' );
const cancelBtn = scanProgressBar.querySelector( '.wp-smush-cancel-scan-progress-btn' );
const holdOnNoticeElement = scanProgressBar.querySelector( '.wp-smush-scan-hold-on-notice' );
let onCancelCallback = () => {};
let intervalProgressAnimation = 0;
// It should be smaller than autoSyncDuration.
const progressTransitionDuration = autoSyncDuration - 300;//1200
scanProgressBar.style.setProperty( '--progress-transition-duration', progressTransitionDuration / 1000 + 's' );
let prevProcessedItems = window.wp_smushit_data?.media_library_scan?.processed_items || 0;
const cacheProcessTimePerItem = [];
let durationToHaveChangeOnProgress = autoSyncDuration;
let timeLimitToShowNotice = autoSyncDuration * 10;// 15s.
return {
update( processedItems, totalItems ) {
this.updateRemainingTime( processedItems, totalItems );
let width = ( totalItems && Math.floor( processedItems / totalItems * 100 ) ) || 0;
width = Math.min( width, 100 );
let currentWidth = progressElement.style.width;
currentWidth = ( currentWidth && currentWidth.replace( '%', '' ) ) || 0;
progressElement.style.width = width + '%';
return this.animateProgressBar( currentWidth, width );
},
animateProgressBar( currentWidth, width ) {
if ( intervalProgressAnimation ) {
clearInterval( intervalProgressAnimation );
}
return new Promise( ( resolve ) => {
const delayTime = progressTransitionDuration / ( width - currentWidth );
intervalProgressAnimation = setInterval( () => {
// Progress bar label.
percentElement.innerHTML = currentWidth + '%';
currentWidth++;
if ( currentWidth > width ) {
resolve();
clearInterval( intervalProgressAnimation );
}
}, delayTime );
} );
},
updateRemainingTime( processedItems, totalItems ) {
if ( ! remainingTimeElement ) {
return;
}
const processTimePerItem = this.calcProcessTimePerItem( processedItems ) || 500;
const remainingTime = processTimePerItem * ( totalItems - processedItems );
remainingTimeElement.innerText = this.formatTime( remainingTime );
},
calcProcessTimePerItem( processedItems ) {
if ( ! processedItems ) {
return;
}
prevProcessedItems = prevProcessedItems <= processedItems ? prevProcessedItems : 0;
if ( prevProcessedItems != processedItems ) {
const processTimePerItem = Math.floor( durationToHaveChangeOnProgress / ( processedItems - prevProcessedItems ) );
prevProcessedItems = processedItems;
cacheProcessTimePerItem.push( processTimePerItem );
this.resetDurationToHaveChangeOnProgress();
} else {
this.increaseDurationToHaveChangeOnProgress( autoSyncDuration );
}
if ( ! cacheProcessTimePerItem.length ) {
return;
}
return cacheProcessTimePerItem.reduce(
( accumulator, processTime ) => accumulator + processTime, 0
) / cacheProcessTimePerItem.length;
},
increaseDurationToHaveChangeOnProgress( increaseTime ) {
durationToHaveChangeOnProgress += increaseTime;
if ( durationToHaveChangeOnProgress > timeLimitToShowNotice ) {
this.showHoldOnNotice();
}
},
showHoldOnNotice() {
holdOnNoticeElement.classList.remove( 'sui-hidden' );
timeLimitToShowNotice = 100000000;
},
resetHoldOnNoticeVisibility() {
holdOnNoticeElement.classList.add( 'sui-hidden' );
},
resetDurationToHaveChangeOnProgress() {
durationToHaveChangeOnProgress = autoSyncDuration;
},
formatTime( totalMilliSeconds ) {
const totalSeconds = Math.floor( ( totalMilliSeconds + progressTransitionDuration ) / 1000 );
const seconds = totalSeconds % 60;
const minutes = Math.floor( totalSeconds / 60 );
let timeString = '';
if ( minutes ) {
timeString += minutes + ' ' + _n( 'minute', 'minutes', minutes, 'wp-smushit' );
}
timeString += ' ' + seconds + ' ' + _n( 'second', 'seconds', seconds, 'wp-smushit' );
return timeString.trim();
},
reset() {
progressElement.style.width = '0%';
percentElement.innerHTML = '0%';
this.resetCancelButton();
this.resetHoldOnNoticeVisibility();
return this;
},
open() {
cancelBtn.onclick = onCancelCallback;
scanProgressBar.classList.remove( 'sui-hidden' );
},
close() {
scanProgressBar.classList.add( 'sui-hidden' );
this.reset();
},
setOnCancelCallback( callBack ) {
if ( 'function' !== typeof callBack ) {
return;
}
onCancelCallback = callBack;
return this;
},
setCancelButtonLabel( textContent ) {
cancelBtn.textContent = textContent;
return this;
},
setCancelButtonOnCancelling() {
this.setCancelButtonLabel( wp_smush_msgs.cancelling );
this.setOnCancelCallback( () => false );
cancelBtn.setAttribute( 'disabled', true );
},
resetCancelButton() {
this.setOnCancelCallback( () => {} );
this.resetCancelButtonLabel();
cancelBtn.removeAttribute( 'disabled' );
},
resetCancelButtonLabel() {
this.setCancelButtonLabel( __( 'Cancel Scan', 'wp-smushit' ) );
},
resetCancelButtonOnFailure() {
this.resetCancelButtonLabel();
cancelBtn.removeAttribute( 'disabled' );
}
};
};
const SmushProgressBar = () => {
'use strict';
const progressBar = document.querySelector( '.wp-smush-bulk-progress-bar-wrapper' );
if ( ! progressBar ) {
return {
isEmptyObject: true,
};
}
const cancelBtn = progressBar.querySelector( '.wp-smush-cancel-btn' );
const bulkSmushDescription = document.querySelector( '.wp-smush-bulk-wrapper' );
const bulkRunningNotice = progressBar.querySelector( '#wp-smush-running-notice' );
const bulkSmushAllDone = document.querySelector( '.wp-smush-all-done' );
let isStateHidden = false;
let onCancelCallback = () => {};
return {
/**
* Update progress bar.
*
* @param {number} processedItems
* @param {number} totalItems
*/
update( processedItems, totalItems ) {
let width = totalItems && Math.floor( processedItems / totalItems * 100 ) || 0;
width = Math.min( width, 100 );
// Progress bar label.
progressBar.querySelector( '.wp-smush-images-percent' ).innerHTML = width + '%';
// Progress bar.
progressBar.querySelector( '.wp-smush-progress-inner' ).style.width = width + '%';
// Update processed/total.
const processStateStats = progressBar.querySelector( '.sui-progress-state-text' );
processStateStats.firstElementChild.innerHTML = processedItems;
processStateStats.lastElementChild.innerHTML = totalItems;
return this;
},
close() {
progressBar.classList.add( 'sui-hidden' );
this.setCancelButtonLabel( window.wp_smush_msgs.cancel )
.setOnCancelCallback( () => {} )
.update( 0, 0 );
this.resetOriginalNotice();
return this;
},
show() {
// Show progress bar.
cancelBtn.onclick = onCancelCallback;
progressBar.classList.remove( 'sui-hidden' );
this.hideBulkSmushDescription();
this.hideBulkSmushAllDone();
this.hideRecheckImagesNotice();
},
setCancelButtonLabel( textContent ) {
cancelBtn.textContent = textContent;
return this;
},
showBulkSmushDescription() {
bulkSmushDescription.classList.remove( 'sui-hidden' );
},
hideBulkSmushDescription() {
bulkSmushDescription.classList.add( 'sui-hidden' );
},
showBulkSmushAllDone() {
bulkSmushAllDone.classList.remove( 'sui-hidden' );
},
hideBulkSmushAllDone() {
bulkSmushAllDone.classList.add( 'sui-hidden' );
},
hideState() {
if ( isStateHidden ) {
return this;
}
isStateHidden = true;
progressBar.querySelector( '.sui-progress-state' ).classList.add( 'sui-hidden' );
return this;
},
showState() {
if ( ! isStateHidden ) {
return this;
}
isStateHidden = false;
progressBar.querySelector( '.sui-progress-state' ).classList.remove( 'sui-hidden' );
return this;
},
setNotice( inProcessNotice ) {
const progressMessage = bulkRunningNotice.querySelector( '.sui-notice-message p' );
this.cacheOriginalNotice( progressMessage );
progressMessage.innerHTML = inProcessNotice;
return this;
},
cacheOriginalNotice( progressMessage ) {
if ( bulkRunningNotice.dataset.progressMessage ) {
return;
}
bulkRunningNotice.dataset.progressMessage = progressMessage.innerHTML;
},
resetOriginalNotice() {
if ( ! bulkRunningNotice.dataset.progressMessage ) {
return;
}
const progressMessage = bulkRunningNotice.querySelector( '.sui-notice-message p' );
progressMessage.innerHTML = bulkRunningNotice.dataset.progressMessage;
},
hideBulkProcessingNotice() {
bulkRunningNotice.classList.add( 'sui-hidden' );
return this;
},
showBulkProcessingNotice() {
bulkRunningNotice.classList.remove( 'sui-hidden' );
return this;
},
setCountUnitText( unitText ) {
const progressUnit = progressBar.querySelector( '.sui-progress-state-unit' );
progressUnit.innerHTML = unitText;
},
setOnCancelCallback( callBack ) {
if ( 'function' !== typeof callBack ) {
return;
}
onCancelCallback = callBack;
return this;
},
disableExceedLimitMode() {
progressBar.classList.remove( 'wp-smush-exceed-limit' );
progressBar.querySelector( '#bulk-smush-resume-button' ).classList.add( 'sui-hidden' );
},
hideRecheckImagesNotice() {
const recheckImagesNoticeElement = document.querySelector( '.wp-smush-recheck-images-notice-box' );
if ( recheckImagesNoticeElement ) {
recheckImagesNoticeElement.classList.add( 'sui-hidden' );
}
}
};
};
export default new SmushProgressBar();

View File

@ -0,0 +1,11 @@
import lazySizes from 'lazysizes';
import 'lazysizes/plugins/native-loading/ls.native-loading';
lazySizes.cfg.nativeLoading = {
setLoadingAttribute: true,
disableListeners: {
scroll: true,
},
};
lazySizes.init();

View File

@ -0,0 +1,3 @@
import lazySizes from 'lazysizes';
lazySizes.init();

View File

@ -0,0 +1,362 @@
import '../../scss/resize-detection.scss';
/**
* Image resize detection (IRD).
*
* Show all wrongly scaled images with a highlighted border and resize box.
*
* Made in pure JS.
* DO NOT ADD JQUERY SUPPORT!!!
*
* @since 2.9
*/
( function() {
'use strict';
const SmushIRS = {
bar: document.getElementById( 'smush-image-bar' ),
toggle: document.getElementById( 'smush-image-bar-toggle' ),
images: {
bigger: [],
smaller: [],
},
strings: window.wp_smush_resize_vars,
/**
* Init scripts.
*/
init() {
/**
* Make sure these are set, before we proceed.
*/
if ( ! this.bar ) {
this.bar = document.getElementById( 'smush-image-bar' );
}
if ( ! this.toggle ) {
this.toggle = document.getElementById(
'smush-image-bar-toggle'
);
}
this.process();
// Register the event handler after everything is done.
this.toggle.addEventListener(
'click',
this.handleToggleClick.bind( this )
);
},
/**
* Do image processing.
*/
process() {
const icon = this.toggle.querySelector( 'i' );
icon.classList.add( 'sui-icon-loader' );
icon.classList.remove( 'sui-icon-info' );
this.detectImages();
if ( ! this.images.bigger.length && ! this.images.smaller.length ) {
this.toggle.classList.add( 'smush-toggle-success' );
document.getElementById(
'smush-image-bar-notice'
).style.display = 'block';
document.getElementById(
'smush-image-bar-notice-desc'
).style.display = 'none';
} else {
this.toggle.classList.remove( 'smush-toggle-success' );
document.getElementById(
'smush-image-bar-notice'
).style.display = 'none';
document.getElementById(
'smush-image-bar-notice-desc'
).style.display = 'block';
this.generateMarkup( 'bigger' );
this.generateMarkup( 'smaller' );
}
this.toggleDivs();
icon.classList.remove( 'sui-icon-loader' );
icon.classList.add( 'sui-icon-info' );
},
/**
* Various checks to see if the image should be processed.
*
* @param {Object} image
* @return {boolean} Should skip image or not.
*/
shouldSkipImage( image ) {
// Skip avatars.
if ( image.classList.contains( 'avatar' ) ) {
return true;
}
// Skip images from Smush CDN with auto-resize feature.
if (
'string' === typeof image.getAttribute( 'no-resize-detection' )
) {
return true;
}
// Skip 1x1px images.
if (
image.clientWidth === image.clientHeight &&
1 === image.clientWidth
) {
return true;
}
// Skip 1x1px placeholders.
if (
image.naturalWidth === image.naturalHeight &&
1 === image.naturalWidth
) {
return true;
}
// If width attribute is not set, do not continue.
return null === image.clientWidth || null === image.clientHeight;
},
/**
* Get tooltip text.
*
* @param {Object} props
* @return {string} Tooltip.
*/
getTooltipText( props ) {
let tooltipText = '';
if ( props.bigger_width || props.bigger_height ) {
/** @param {string} strings.large_image */
tooltipText = this.strings.large_image;
} else if ( props.smaller_width || props.smaller_height ) {
/** @param {string} strings.small_image */
tooltipText = this.strings.small_image;
}
return tooltipText
.replace( 'width', props.real_width )
.replace( 'height', props.real_height );
},
/**
* Generate markup.
*
* @param {string} type Accepts: 'bigger' or 'smaller'.
*/
generateMarkup( type ) {
this.images[ type ].forEach( ( image, index ) => {
const item = document.createElement( 'div' ),
tooltip = this.getTooltipText( image.props );
item.setAttribute(
'class',
'smush-resize-box smush-tooltip smush-tooltip-constrained'
);
item.setAttribute( 'data-tooltip', tooltip );
item.setAttribute( 'data-image', image.class );
item.addEventListener( 'click', ( e ) =>
this.highlightImage( e )
);
item.innerHTML = `
<div class="smush-image-info">
<span>${ index + 1 }</span>
<span class="smush-tag">${ image.props.computed_width } x ${ image.props.computed_height }px</span>
<i class="smush-front-icons smush-front-icon-arrows-in" aria-hidden="true">&nbsp;</i>
<span class="smush-tag smush-tag-success">${ image.props.real_width } × ${ image.props.real_height }px</span>
</div>
<div class="smush-image-description">${ tooltip }</div>
`;
document
.getElementById( 'smush-image-bar-items-' + type )
.appendChild( item );
} );
},
/**
* Show/hide sections based on images.
*/
toggleDivs() {
const types = [ 'bigger', 'smaller' ];
types.forEach( ( type ) => {
const div = document.getElementById(
'smush-image-bar-items-' + type
);
if ( 0 === this.images[ type ].length ) {
div.style.display = 'none';
} else {
div.style.display = 'block';
}
} );
},
/**
* Scroll the selected image into view and highlight it.
*
* @param {Object} e
*/
highlightImage( e ) {
this.removeSelection();
const el = document.getElementsByClassName(
e.currentTarget.dataset.image
);
if ( 'undefined' !== typeof el[ 0 ] ) {
// Display description box.
e.currentTarget.classList.toggle( 'show-description' );
// Scroll and flash image.
el[ 0 ].scrollIntoView( {
behavior: 'smooth',
block: 'center',
inline: 'nearest',
} );
el[ 0 ].style.opacity = '0.5';
setTimeout( () => {
el[ 0 ].style.opacity = '1';
}, 1000 );
}
},
/**
* Handle click on the toggle item.
*/
handleToggleClick() {
this.bar.classList.toggle( 'closed' );
this.toggle.classList.toggle( 'closed' );
this.removeSelection();
},
/**
* Remove selected items.
*/
removeSelection() {
const items = document.getElementsByClassName( 'show-description' );
if ( items.length > 0 ) {
Array.from( items ).forEach( ( div ) =>
div.classList.remove( 'show-description' )
);
}
},
/**
* Function to highlight all scaled images.
*
* Add yellow border and then show one small box to
* resize the images as per the required size, on fly.
*/
detectImages() {
const images = document.getElementsByTagName( 'img' );
for ( const image of images ) {
if ( this.shouldSkipImage( image ) ) {
continue;
}
// Get defined width and height.
const props = {
real_width: image.clientWidth,
real_height: image.clientHeight,
computed_width: image.naturalWidth,
computed_height: image.naturalHeight,
bigger_width: image.clientWidth * 1.5 < image.naturalWidth,
bigger_height:
image.clientHeight * 1.5 < image.naturalHeight,
smaller_width: image.clientWidth > image.naturalWidth,
smaller_height: image.clientHeight > image.naturalHeight,
};
// In case image is in correct size, do not continue.
if (
! props.bigger_width &&
! props.bigger_height &&
! props.smaller_width &&
! props.smaller_height
) {
continue;
}
const imgType =
props.bigger_width || props.bigger_height
? 'bigger'
: 'smaller',
imageClass =
'smush-image-' + ( this.images[ imgType ].length + 1 );
// Fill the images arrays.
this.images[ imgType ].push( {
src: image,
props,
class: imageClass,
} );
/**
* Add class to original image.
* Can't add two classes in single add(), because no support in IE11.
* image.classList.add('smush-detected-img', imageClass);
*/
image.classList.add( 'smush-detected-img' );
image.classList.add( imageClass );
}
}, // End detectImages()
/**
* Allows refreshing the list. A good way is to refresh on lazyload actions.
*
* @since 3.6.0
*/
refresh() {
// Clear out classes on DOM.
for ( let id in this.images.bigger ) {
if ( this.images.bigger.hasOwnProperty( id ) ) {
this.images.bigger[ id ].src.classList.remove(
'smush-detected-img'
);
this.images.bigger[ id ].src.classList.remove(
'smush-image-' + ++id
);
}
}
for ( let id in this.images.smaller ) {
if ( this.images.smaller.hasOwnProperty( id ) ) {
this.images.smaller[ id ].src.classList.remove(
'smush-detected-img'
);
this.images.smaller[ id ].src.classList.remove(
'smush-image-' + ++id
);
}
}
this.images = {
bigger: [],
smaller: [],
};
// This might be overkill - there will probably never be a situation when there are less images than on
// initial page load.
const elements = document.getElementsByClassName(
'smush-resize-box'
);
while ( elements.length > 0 ) {
elements[ 0 ].remove();
}
this.process();
},
}; // End WP_Smush_IRS
/**
* After page load, initialize toggle event.
*/
window.addEventListener( 'DOMContentLoaded', () => SmushIRS.init() );
window.addEventListener( 'lazyloaded', () => SmushIRS.refresh() );
}() );

View File

@ -0,0 +1,83 @@
import '../scss/common.scss';
/* global ajaxurl */
document.addEventListener('DOMContentLoaded', function () {
const dismissNoticeButton = document.querySelectorAll(
'.smush-dismissible-notice .smush-dismiss-notice-button'
);
dismissNoticeButton.forEach((button) => {
button.addEventListener('click', handleDismissNotice);
});
function handleDismissNotice(event) {
event.preventDefault();
const button = event.target;
const notice = button.closest('.smush-dismissible-notice');
const key = notice.getAttribute('data-key');
dismissNotice( key, notice );
}
function dismissNotice( key, notice ) {
const xhr = new XMLHttpRequest();
xhr.open(
'POST',
ajaxurl + '?action=smush_dismiss_notice&key=' + key + '&_ajax_nonce=' + smush_global.nonce,
true
);
xhr.onload = () => {
if (notice) {
notice.querySelector('button.notice-dismiss').dispatchEvent(new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
}));
}
};
xhr.send();
}
// Show header notices.
const handleHeaderNotice = () => {
const headerNotice = document.querySelector('.wp-smush-dismissible-header-notice');
if ( ! headerNotice || ! headerNotice.id ) {
return;
}
const { dismissKey, message } = headerNotice.dataset;
if ( ! dismissKey || ! message ) {
return;
}
headerNotice.onclick = (e) => {
const classList = e.target.classList;
const isDismissButton = classList && ( classList.contains('sui-icon-check') || classList.contains('sui-button-icon') );
if ( ! isDismissButton ) {
return;
}
dismissNotice( dismissKey );
}
const noticeOptions = {
type: 'warning',
icon: 'info',
dismiss: {
show: true,
label: wp_smush_msgs.noticeDismiss,
tooltip: wp_smush_msgs.noticeDismissTooltip,
},
};
window.SUI.openNotice(
headerNotice.id,
message,
noticeOptions
);
}
handleHeaderNotice();
});

View File

@ -0,0 +1,75 @@
/* global wp_smush_mixpanel */
import mixpanel from "mixpanel-browser";
class MixPanel {
constructor() {
// Opt out event is not triggered via js so only initial mixpanel when it's enabled.
this.mixpanelInstance = this.allowToTrack() && mixpanel.init(wp_smush_mixpanel.token, {
opt_out_tracking_by_default: ! this.allowToTrack(),
loaded: (mixpanel) => {
mixpanel.identify(wp_smush_mixpanel.unique_id);
mixpanel.register(wp_smush_mixpanel.super_properties);
if (mixpanel.has_opted_in_tracking() !== this.allowToTrack()) {
// The status cached by MixPanel in the local storage is different from the settings. Clear the cache.
mixpanel.clear_opt_in_out_tracking();
}
}
}, 'smush');
}
allowToTrack() {
return !! wp_smush_mixpanel.opt_in;
}
track(event, properties = {}) {
if ( wp_smush_mixpanel.debug ) {
console.log( 'Event:', event, properties );
}
this.mixpanelInstance && this.mixpanelInstance.track(event, properties);
}
trackBulkSmushCompleted( globalStats ) {
const {
savings_bytes,
count_images,
percent_optimized,
savings_percent,
count_resize,
savings_resize
} = globalStats;
this.track('Bulk Smush Completed', {
'Total Savings': this.convertToMegabytes( savings_bytes ),
'Total Images': count_images,
'Media Optimization Percentage': parseFloat( percent_optimized ),
'Percentage of Savings': parseFloat( savings_percent ),
'Images Resized': count_resize,
'Resize Savings': this.convertToMegabytes( savings_resize )
});
}
trackBulkSmushCancel() {
this.track('Bulk Smush Cancelled');
}
convertToMegabytes( sizeInBytes ) {
const unitMB = Math.pow( 1024, 2 );
const sizeInMegabytes = sizeInBytes/unitMB;
return sizeInMegabytes && parseFloat(sizeInMegabytes.toFixed(2)) || 0;
}
}
const mixPanelInstance = () => {
let instance;
return {
getInstance: () => {
if ( ! instance ) {
instance = new MixPanel();
}
return instance;
}
}
}
export default mixPanelInstance();

View File

@ -0,0 +1,20 @@
jQuery(function ($) {
'use strict';
/**
* Handle the Smush Stats link click
*/
$('body').on('click', 'a.smush-stats-details', function (e) {
//If disabled
if ($(this).prop('disabled')) {
return false;
}
// prevent the default action
e.preventDefault();
//Replace the `+` with a `-`
const slide_symbol = $(this).find('.stats-toggle');
$(this).parents().eq(1).find('.smush-stats-wrapper').slideToggle();
slide_symbol.text(slide_symbol.text() == '+' ? '-' : '+');
});
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,395 @@
/* global WP_Smush */
/**
* Bulk Smush Background Optimization.
*
* @since 3.12.0
*/
import Fetcher from '../utils/fetcher';
import MixPanel from "../mixpanel";
import SmushProgress from '../common/progressbar';
import {GlobalStats, UpsellManger} from "../common/globalStats";
(function () {
'use strict';
if (!window.wp_smush_msgs) {
return;
}
const $ = document.querySelector.bind(document);
const NO_FREE_LIMITED = 50;
/**
* Handle Background Process.
* @returns
*/
const BackgroundProcess = () => {
return {
handle(action) {
return Fetcher.background[action]();
},
initState() {
return Fetcher.background.initState();
},
}
}
/**
* Background Optimization.
*/
const BackgroundSmush = (() => {
const startBtn = window.wp_smushit_data && window.wp_smushit_data.bo_stats && $('.wp-smush-bo-start');
if (!startBtn) {
return;
}
const mixPanel = MixPanel.getInstance();
const BO = new BackgroundProcess();
const bulkWrapper = $('.bulk-smush-wrapper');
const reScanImagesButton = $('.wp-smush-scan');
let intervalHandle = 0;
let cancellationInProgress = false;
return {
hookStatusChecks() {
if (intervalHandle) {
// Already done
return;
}
let count = 0;
let statusSyncInProgress = false;
let statSyncInProgress = false;
intervalHandle = setInterval(() => {
if (statusSyncInProgress) {
return;
}
statusSyncInProgress = true;
count++;
const statusSynced = this.syncBackgroundStatus();
if (count % 3 === 0) {
// Do a full update every nth time
statusSynced.then(() => {
if (!statSyncInProgress) {
this.syncStats().then(() => {
statSyncInProgress = false;
});
statSyncInProgress = true;
}
});
}
statusSynced.finally(() => {
statusSyncInProgress = false;
});
}, 3 * 1000);
},
resetBOStatsOnStart() {
GlobalStats.setBoStats( {
is_cancelled: false,
is_completed: false,
processed_items: 0,
failed_items: 0,
} );
},
start() {
// Reset boStats.
this.resetBOStatsOnStart();
// Disable UI.
this.onStart();
// Start BO.
BO.handle('start').then((res) => {
if (res.success) {
// Update stats.
const updatedStats = this.updateStats(res.data, false);
// Show progress bar.
this.showProgressBar();
this.hookStatusChecks();
if ( updatedStats ) {
// Render stats.
GlobalStats.renderStats();
}
} else {
WP_Smush.helpers.showNotice( res, {
'showdismiss': true,
'autoclose' : false,
} );
this.cancelBulk();
}
});
},
/**
* Initial state when load the Bulk Smush page while BO is running.
*/
initState() {
if (!GlobalStats.getBoStats().in_processing) {
return;
}
// Disable UI.
this.onStart();
// Start BO.
BO.initState().then((res) => {
if (res.success) {
// Update stats.
this.updateStats(res.data, false);
// Show progress bar.
this.showProgressBar();
this.hookStatusChecks();
// Maybe update errors.
if ( res.data.errors && ! Object.keys( GlobalStats.getErrors() ).length ) {
GlobalStats.setErrors( res.data.errors );
}
// Render stats.
GlobalStats.renderStats();
} else {
WP_Smush.helpers.showNotice( res );
}
});
},
/**
* Cancel.
*/
cancel() {
cancellationInProgress = true;
this.setCancelButtonStateToStarted();
BO.handle('cancel').then((res) => {
if (res.success) {
this.cancelBulk();
} else {
WP_Smush.helpers.showNotice( res );
}
});
},
hideProgressBar() {
// Hide progress bar.
SmushProgress.close().update(0, GlobalStats.getBoStats().total_items);
},
showProgressBar() {
// Reset progress bar.
SmushProgress.update(GlobalStats.getBoStats().processed_items, GlobalStats.getBoStats().total_items);
// Show progress bar.
SmushProgress.show();
},
/**
* Update stats.
* @param {Object} newStats Included increment stats and new BO stats.
* @param updateGlobal
*/
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 || {};
const {
global_stats,
errors,
...newBoStats
} = newStats;
if ( ! GlobalStats.isChangedStats( newBoStats ) ) {
return false;
}
// Update BO stats.
GlobalStats.setBoStats(newBoStats);
if (updateGlobal) {
// Update global stats.
GlobalStats.setGlobalStats(global_stats);
}
// Update Errors.
GlobalStats.setErrors( errors );
return true;
},
cancelBulk() {
// Sync Stats.
this.syncStats(() => {
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( {
is_completed: true
} );
this.onCompletedBulk();
} else {
// Update status of boStats.
GlobalStats.setBoStats( {
is_cancelled: true
} );
// Reset and hide progress bar.
this.onFinish();
// Bulk is cancelled, show bulk desc.
SmushProgress.showBulkSmushDescription();
}
mixPanel.trackBulkSmushCancel();
cancellationInProgress = false;
});
},
showCompletedMessage() {
// 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 );
} else {
processedWrapper.querySelector('p').innerHTML = wp_smush_msgs.all_smushed;
}
processedWrapper.classList.remove('sui-hidden');
},
isFailedAllItems() {
return GlobalStats.getBoStats().failed_items === GlobalStats.getBoStats().total_items;
},
getNoticeType() {
return this.isFailedAllItems() ? 'warning' : 'success';
},
onCompletedBulk() {
// Reset and hide progress bar.
this.onFinish();
// 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);
},
completeBulk() {
// Sync Stats.
this.syncStats(() => this.onCompletedBulk());
},
syncStats(onComplete = () => false) {
return BO.handle('getStats').then((res) => {
if ( res.success ) {
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;
}
onComplete();
} else {
WP_Smush.helpers.showNotice( res );
}
}).catch( (error) => console.log('error', error));
},
syncBackgroundStatus() {
return BO.handle('getStatus').then((res) => {
if ((res.data || {}).in_process_notice) {
SmushProgress.setNotice( res.data.in_process_notice );
}
if (res.success) {
// Update stats.
if ( this.updateStats(res.data, false) ) {
// Update progress bar.
SmushProgress.update(GlobalStats.getBoStats().processed_items, GlobalStats.getBoStats().total_items);
if (! GlobalStats.getBoStats().is_cancelled && ! GlobalStats.getBoStats().is_completed) {
// Render stats.
GlobalStats.renderStats();
}
}
if (GlobalStats.getBoStats().is_cancelled && !cancellationInProgress) {
// Cancelled on server side
this.cancelBulk();
} else if (GlobalStats.getBoStats().is_completed) {
this.completeBulk();
}
} else {
WP_Smush.helpers.showNotice( res );
}
});
},
onStart() {
// Disable btn.
startBtn.setAttribute('disabled', '');
// Disable re-check images.
reScanImagesButton && reScanImagesButton.setAttribute('disabled', '');
$('.wp-smush-restore').setAttribute('disabled', '');
// Show upsell cdn.
UpsellManger.maybeShowCDNUpsellForPreSiteOnStart();
this.setCancelButtonStateToInitial();
},
onFinish() {
// Clear interval.
if (intervalHandle) {
clearInterval(intervalHandle);
intervalHandle = 0;
}
// Disable btn.
startBtn.removeAttribute('disabled');
// Reset and hide Progress Bar.
this.hideProgressBar();
// Disable re-check images.
reScanImagesButton && reScanImagesButton.removeAttribute('disabled', '');
$('.wp-smush-restore').removeAttribute('disabled', '');
// Show upsell cdn.
UpsellManger.maybeShowCDNUpsellForPreSiteOnCompleted();
},
init() {
if (!startBtn) {
return;
}
// Start BO.
startBtn.onclick = () => {
const requiredScanMedia = startBtn.classList.contains('wp-smush-scan-and-bulk-smush');
if ( requiredScanMedia ) {
return;
}
this.start();
}
// If BO is running, initial new state.
this.initState();
},
setCancelButtonStateToInitial() {
SmushProgress.setCancelButtonLabel( wp_smush_msgs.cancel );
SmushProgress.setOnCancelCallback( this.cancel.bind(this) );
},
setCancelButtonStateToStarted() {
SmushProgress.setCancelButtonLabel( wp_smush_msgs.cancelling );
SmushProgress.setOnCancelCallback( () => false );
},
}
})();
// Run.
BackgroundSmush && BackgroundSmush.init();
/**
* For globalStats, we will need to update it on reload and after re-checking images,
* and on finish the BO.
* 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:
* window.wp_smushit_data
*/
// Update global stats after re-checking images.
document.addEventListener('wpSmushAfterRecheckImages', function(){
GlobalStats.updateGlobalStatsFromSmushScriptData();
});
document.addEventListener('backgroundBulkSmushOnScanCompleted', function(){
if ( ! BackgroundSmush ) {
return;
}
GlobalStats.setBoStats({
in_processing: true,
});
BackgroundSmush.initState();
});
})();

View File

@ -0,0 +1,276 @@
/* global WP_Smush */
/* global ajaxurl */
/* global _ */
import MixPanel from "../mixpanel";
/**
* Bulk restore JavaScript code.
*
* @since 3.2.2
*/
(function () {
'use strict';
/**
* Bulk restore modal.
*
* @since 3.2.2
*/
WP_Smush.restore = {
modal: document.getElementById('smush-restore-images-dialog'),
contentContainer: document.getElementById('smush-bulk-restore-content'),
settings: {
slide: 'start', // start, progress or finish.
success: 0,
errors: [],
},
items: [], // total items, 1 item = 1 step.
success: [], // successful items restored.
errors: [], // failed items.
currentStep: 0,
totalSteps: 0,
/**
* Init module.
*/
init() {
if (!this.modal) {
return;
}
this.settings = {
slide: 'start',
success: 0,
errors: [],
};
this.mixPanel = MixPanel.getInstance();
this.resetModalWidth();
this.renderTemplate();
// Show the modal.
window.SUI.openModal(
'smush-restore-images-dialog',
'wpbody-content',
undefined,
false
);
},
/**
* Update the template, register new listeners.
*/
renderTemplate() {
const template = WP_Smush.onboarding.template('smush-bulk-restore');
const content = template(this.settings);
if (content) {
this.contentContainer.innerHTML = content;
}
this.bindSubmit();
},
/**
* Reset modal width.
*
* @since 3.6.0
*/
resetModalWidth() {
this.modal.style.maxWidth = '460px';
this.modal.querySelector('.sui-box').style.maxWidth = '460px';
},
/**
* Catch "Finish setup wizard" button click.
*/
bindSubmit() {
const confirmButton = this.modal.querySelector(
'button[id="smush-bulk-restore-button"]'
);
const self = this;
if (confirmButton) {
confirmButton.addEventListener('click', function (e) {
e.preventDefault();
self.resetModalWidth();
self.settings = { slide: 'progress' };
self.errors = [];
self.renderTemplate();
self.initScan();
self.mixPanel.track('Bulk Restore Triggered');
});
}
},
/**
* Cancel the bulk restore.
*/
cancel() {
if (
'start' === this.settings.slide ||
'finish' === this.settings.slide
) {
// Hide the modal.
window.SUI.closeModal();
} else {
this.updateProgressBar(true);
window.location.reload();
}
},
/**
* Update progress bar during directory smush.
*
* @param {boolean} cancel Cancel status.
*/
updateProgressBar(cancel = false) {
let progress = 0;
if (0 < this.currentStep) {
progress = Math.min(
Math.round((this.currentStep * 100) / this.totalSteps),
99
);
}
if (progress > 100) {
progress = 100;
}
// Update progress bar
this.modal.querySelector('.sui-progress-text span').innerHTML =
progress + '%';
this.modal.querySelector('.sui-progress-bar span').style.width =
progress + '%';
const statusDiv = this.modal.querySelector(
'.sui-progress-state-text'
);
if (progress >= 90) {
statusDiv.innerHTML = 'Finalizing...';
} else if (cancel) {
statusDiv.innerHTML = 'Cancelling...';
} else {
statusDiv.innerHTML =
this.currentStep +
'/' +
this.totalSteps +
' ' +
'images restored';
}
},
/**
* First step in bulk restore - get the bulk attachment count.
*/
initScan() {
const self = this;
const _nonce = document.getElementById('_wpnonce');
const xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl + '?action=get_image_count', true);
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
if (200 === xhr.status) {
const res = JSON.parse(xhr.response);
if ('undefined' !== typeof res.data.items) {
self.items = res.data.items;
self.totalSteps = res.data.items.length;
self.step();
}
} else {
window.console.log(
'Request failed. Returned status of ' + xhr.status
);
}
};
xhr.send('_ajax_nonce=' + _nonce.value);
},
/**
* Execute a scan step recursively
*/
step() {
const self = this;
const _nonce = document.getElementById('_wpnonce');
if (0 < this.items.length) {
const item = this.items.pop();
const xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl + '?action=restore_step', true);
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
this.currentStep++;
if (200 === xhr.status) {
const res = JSON.parse(xhr.response);
const data = ((res || {}).data || {});
if (data.success) {
self.success.push(item);
} else {
self.errors.push({
id: item,
src: data.src || "Error",
thumb: data.thumb,
link: data.link,
});
}
}
self.updateProgressBar();
self.step();
};
xhr.send('item=' + item + '&_ajax_nonce=' + _nonce.value);
} else {
// Finish.
this.settings = {
slide: 'finish',
success: this.success.length,
errors: this.errors,
total: this.totalSteps,
};
self.renderTemplate();
if (0 < this.errors.length) {
this.modal.style.maxWidth = '660px';
this.modal.querySelector('.sui-box').style.maxWidth =
'660px';
}
}
},
};
/**
* Template function (underscores based).
*
* @type {Function}
*/
WP_Smush.restore.template = _.memoize((id) => {
let compiled;
const options = {
evaluate: /<#([\s\S]+?)#>/g,
interpolate: /{{{([\s\S]+?)}}}/g,
escape: /{{([^}]+?)}}(?!})/g,
variable: 'data',
};
return (data) => {
_.templateSettings = options;
compiled =
compiled || _.template(document.getElementById(id).innerHTML);
return compiled(data);
};
});
})();

View File

@ -0,0 +1,149 @@
/* global WP_Smush */
/* global ajaxurl */
/**
* Bulk Smush functionality.
*
* @since 2.9.0 Moved from admin.js
*/
import Smush from '../smush/smush';
import Fetcher from '../utils/fetcher';
import SmushProcess from '../common/progressbar';
( function( $ ) {
'use strict';
WP_Smush.bulk = {
init() {
this.onClickBulkSmushNow();
this.onClickIgnoreImage();
this.onClickIgnoreAllImages();
this.onScanCompleted();
},
onClickBulkSmushNow() {
/**
* Handle the Bulk Smush/Bulk re-Smush button click.
*/
$( '.wp-smush-all' ).on( 'click', function( e ) {
const bulkSmushButton = $(this);
if ( bulkSmushButton.hasClass('wp-smush-scan-and-bulk-smush') ) {
return;
}
e.preventDefault();
WP_Smush.bulk.ajaxBulkSmushStart( bulkSmushButton );
} );
},
ajaxBulkSmushStart( bulkSmushButton ) {
bulkSmushButton = bulkSmushButton || $( '#wp-smush-bulk-content .wp-smush-all' );
// Check for IDs, if there is none (unsmushed or lossless), don't call Smush function.
/** @param {Array} wp_smushit_data.unsmushed */
if (
'undefined' === typeof window.wp_smushit_data ||
( 0 === window.wp_smushit_data.unsmushed.length &&
0 === window.wp_smushit_data.resmush.length )
) {
return false;
}
// Disable re-Smush and scan button.
// TODO: refine what is disabled.
$(
'.wp-resmush.wp-smush-action, .wp-smush-scan, .wp-smush-all:not(.sui-progress-close), a.wp-smush-lossy-enable, button.wp-smush-resize-enable, button#save-settings-button'
).prop( 'disabled', true );
if ( bulkSmushButton.hasClass('wp-smush-resume-bulk-smush') && this.bulkSmush ) {
this.resumeBulkSmush();
return;
}
this.bulkSmush = new Smush( bulkSmushButton, true );
SmushProcess.setOnCancelCallback( () => {
this.bulkSmush.cancelAjax()
}).update( 0, this.bulkSmush.ids.length ).show();
// Show upsell cdn.
this.maybeShowCDNUpsellForPreSiteOnStart();
// Run bulk Smush.
this.bulkSmush.run();
},
resumeBulkSmush() {
SmushProcess.disableExceedLimitMode();
SmushProcess.hideBulkSmushDescription();
this.bulkSmush.onStart();
this.bulkSmush.callAjax();
},
onClickIgnoreImage() {
/**
* Ignore file from bulk Smush.
*
* @since 2.9.0
*/
$( 'body' ).on( 'click', '.smush-ignore-image', function( e ) {
e.preventDefault();
const self = $( this );
self.prop( 'disabled', true );
self.attr( 'data-tooltip' );
self.removeClass( 'sui-tooltip' );
$.post( ajaxurl, {
action: 'ignore_bulk_image',
id: self.attr( 'data-id' ),
_ajax_nonce: wp_smush_msgs.nonce,
} ).done( ( response ) => {
if ( self.is( 'a' ) && response.success && 'undefined' !== typeof response.data.html ) {
if( self.closest('.smush-status-links') ) {
self.closest('.smush-status-links').parent().html( response.data.html );
} else if (e.target.closest( '.smush-bulk-error-row' ) ){
self.addClass('disabled');
e.target.closest( '.smush-bulk-error-row' ).style.opacity = 0.5;
}
}
} );
} );
},
onClickIgnoreAllImages() {
/**
* Ignore file from bulk Smush.
*
* @since 3.12.0
*/
const ignoreAll = document.querySelector('.wp_smush_ignore_all_failed_items');
if ( ignoreAll ) {
ignoreAll.onclick = (e) => {
e.preventDefault();
e.target.setAttribute('disabled','');
e.target.style.cursor = 'progress';
const type = e.target.dataset.type || null;
e.target.classList.remove('sui-tooltip');
Fetcher.smush.ignoreAll(type).then((res) => {
if ( res.success ) {
window.location.reload();
} else {
e.target.style.cursor = 'pointer';
e.target.removeAttribute('disabled');
WP_Smush.helpers.showNotice( res );
}
});
}
}
},
onScanCompleted() {
document.addEventListener('ajaxBulkSmushOnScanCompleted', (e) => {
this.ajaxBulkSmushStart();
});
},
maybeShowCDNUpsellForPreSiteOnStart: () => {
// Show upsell cdn.
const upsell_cdn = document.querySelector('.wp-smush-upsell-cdn');
if ( upsell_cdn ) {
upsell_cdn.classList.remove('sui-hidden');
}
}
};
WP_Smush.bulk.init();
} )( jQuery );

View File

@ -0,0 +1,279 @@
/* global WP_Smush */
/* global ajaxurl */
/**
* Directory Smush module JavaScript code.
*
* @since 2.8.1 Separated from admin.js into dedicated file.
*/
import { createTree } from 'jquery.fancytree';
import Scanner from '../smush/directory-scanner';
( function( $ ) {
'use strict';
WP_Smush.directory = {
selected: [],
tree: [],
wp_smush_msgs: [],
triggered: false,
init() {
const self = this,
progressDialog = $( '#wp-smush-progress-dialog' );
let totalSteps = 0,
currentScanStep = 0;
// Make sure directory smush vars are set.
if ( typeof window.wp_smushit_data.dir_smush !== 'undefined' ) {
totalSteps = window.wp_smushit_data.dir_smush.totalSteps;
currentScanStep =
window.wp_smushit_data.dir_smush.currentScanStep;
}
// Init image scanner.
this.scanner = new Scanner( totalSteps, currentScanStep );
/**
* Smush translation strings.
*
* @param {Array} wp_smush_msgs
*/
this.wp_smush_msgs = window.wp_smush_msgs || {};
/**
* Open the "Select Smush directory" modal.
*/
$( 'button.wp-smush-browse, a#smush-directory-open-modal' ).on(
'click',
function( e ) {
e.preventDefault();
if ( $( e.currentTarget ).hasClass( 'wp-smush-browse' ) ) {
// Hide all the notices.
$( 'div.wp-smush-scan-result div.wp-smush-notice' ).hide();
// Remove notice.
$( 'div.wp-smush-info' ).remove();
}
window.SUI.openModal(
'wp-smush-list-dialog',
e.currentTarget,
$(
'#wp-smush-list-dialog .sui-box-header [data-modal-close]'
)[0],
true
);
//Display File tree for Directory Smush
self.initFileTree();
}
);
/**
* Smush images: Smush in Choose Directory modal clicked
*/
$( '#wp-smush-select-dir' ).on( 'click', function( e ) {
e.preventDefault();
$( 'div.wp-smush-list-dialog div.sui-box-body' ).css( {
opacity: '0.8',
} );
$( 'div.wp-smush-list-dialog div.sui-box-body a' ).off(
'click'
);
const button = $( this );
// Display the spinner.
button.addClass('sui-button-onload');
const selectedFolders = self.tree.getSelectedNodes();
const paths = [];
selectedFolders.forEach( function( folder ) {
paths.push( folder.key );
} );
// Send a ajax request to get a list of all the image files
const param = {
action: 'image_list',
smush_path: paths,
image_list_nonce: $(
'input[name="image_list_nonce"]'
).val(),
};
$.post( ajaxurl, param, function( response ) {
if ( response.success ) {
// Close the modal.
window.SUI.closeModal();
self.scanner = new Scanner( response.data, 0 );
self.showProgressDialog( response.data );
self.scanner.scan();
} else {
// Remove the spinner.
button.removeClass('sui-button-onload');
window.SUI.openNotice(
'wp-smush-ajax-notice',
response.data.message,
{ type: 'warning' }
);
}
} );
} );
/**
* Cancel scan.
*/
progressDialog.on(
'click',
'#cancel-directory-smush, #dialog-close-div, .wp-smush-cancel-dir',
function( e ) {
e.preventDefault();
// Display the spinner
$( '.wp-smush-cancel-dir' ).addClass( 'sui-button-onload' );
self.scanner
.cancel()
.done(
() =>
( window.location.href =
self.wp_smush_msgs.directory_url )
);
}
);
/**
* Continue scan.
*/
progressDialog.on(
'click',
'.sui-icon-play, .wp-smush-resume-scan',
function( e ) {
e.preventDefault();
self.scanner.resume();
}
);
/**
* Check to see if we should open the directory module.
* Used to redirect from dashboard page.
*
* @since 3.8.6
*/
const queryString = window.location.search;
const urlParams = new URLSearchParams( queryString );
if ( urlParams.has( 'start' ) && ! this.triggered ) {
this.triggered = true;
$( 'button.wp-smush-browse' ).trigger( 'click' );
}
},
/**
* Init fileTree.
*/
initFileTree() {
const self = this,
smushButton = $( 'button#wp-smush-select-dir' ),
ajaxSettings = {
type: 'GET',
url: ajaxurl,
data: {
action: 'smush_get_directory_list',
list_nonce: $( 'input[name="list_nonce"]' ).val(),
},
cache: false,
};
// Object already defined.
if ( Object.entries( self.tree ).length > 0 ) {
return;
}
self.tree = createTree( '.wp-smush-list-dialog .content', {
autoCollapse: true, // Automatically collapse all siblings, when a node is expanded
clickFolderMode: 3, // 1:activate, 2:expand, 3:activate and expand, 4:activate (dblclick expands)
checkbox: true, // Show checkboxes
debugLevel: 0, // 0:quiet, 1:errors, 2:warnings, 3:infos, 4:debug
selectMode: 3, // 1:single, 2:multi, 3:multi-hier
tabindex: '0', // Whole tree behaves as one single control
keyboard: true, // Support keyboard navigation
quicksearch: true, // Navigate to next node by typing the first letters
source: ajaxSettings,
lazyLoad: ( event, data ) => {
data.result = new Promise( function( resolve, reject ) {
ajaxSettings.data.dir = data.node.key;
$.ajax( ajaxSettings )
.done( ( response ) => resolve( response ) )
.fail( reject );
} );
},
loadChildren: ( event, data ) =>
data.node.fixSelection3AfterClick(), // Apply parent's state to new child nodes:
select: () =>
smushButton.prop(
'disabled',
! +self.tree.getSelectedNodes().length
),
init: () => smushButton.prop( 'disabled', true ),
} );
},
/**
* Show progress dialog.
*
* @param {number} items Number of items in the scan.
*/
showProgressDialog( items ) {
// Update items status and show the progress dialog..
$( '.wp-smush-progress-dialog .sui-progress-state-text' ).html(
'0/' + items + ' ' + self.wp_smush_msgs.progress_smushed
);
window.SUI.openModal(
'wp-smush-progress-dialog',
'dialog-close-div',
undefined,
false
);
},
/**
* Update progress bar during directory smush.
*
* @param {number} progress Current progress in percent.
* @param {boolean} cancel Cancel status.
*/
updateProgressBar( progress, cancel = false ) {
if ( progress > 100 ) {
progress = 100;
}
// Update progress bar
$( '.sui-progress-block .sui-progress-text span' ).text(
progress + '%'
);
$( '.sui-progress-block .sui-progress-bar span' ).width(
progress + '%'
);
if ( progress >= 90 ) {
$( '.sui-progress-state .sui-progress-state-text' ).text(
'Finalizing...'
);
}
if ( cancel ) {
$( '.sui-progress-state .sui-progress-state-text' ).text(
'Cancelling...'
);
}
},
};
WP_Smush.directory.init();
}( jQuery ) );

View File

@ -0,0 +1,298 @@
/* global WP_Smush */
/* global ajaxurl */
/* global wp_smush_msgs */
/**
* Helpers functions.
*
* @since 2.9.0 Moved from admin.js
*/
( function() {
'use strict';
WP_Smush.helpers = {
init: () => {},
cacheUpsellErrorCodes: [],
/**
* Convert bytes to human-readable form.
*
* @param {number} a Bytes
* @param {number} b Number of digits
* @return {*} Formatted Bytes
*/
formatBytes: ( a, b ) => {
const thresh = 1024,
units = [ 'KB', 'MB', 'GB', 'TB', 'PB' ];
if ( Math.abs( a ) < thresh ) {
return a + ' B';
}
let u = -1;
do {
a /= thresh;
++u;
} while ( Math.abs( a ) >= thresh && u < units.length - 1 );
return a.toFixed( b ) + ' ' + units[ u ];
},
/**
* Get size from a string.
*
* @param {string} formattedSize Formatter string
* @return {*} Formatted Bytes
*/
getSizeFromString: ( formattedSize ) => {
return formattedSize.replace( /[a-zA-Z]/g, '' ).trim();
},
/**
* Get type from formatted string.
*
* @param {string} formattedSize Formatted string
* @return {*} Formatted Bytes
*/
getFormatFromString: ( formattedSize ) => {
return formattedSize.replace( /[0-9.]/g, '' ).trim();
},
/**
* Stackoverflow: http://stackoverflow.com/questions/1726630/formatting-a-number-with-exactly-two-decimals-in-javascript
*
* @param {number} num
* @param {number} decimals
* @return {number} Number
*/
precise_round: ( num, decimals ) => {
const sign = num >= 0 ? 1 : -1;
// Keep the percentage below 100.
num = num > 100 ? 100 : num;
return (
Math.round( num * Math.pow( 10, decimals ) + sign * 0.001 ) /
Math.pow( 10, decimals )
);
},
/**
* Displays a floating error message using the #wp-smush-ajax-notice container.
*
* @since 3.8.0
*
* @param {string} message
*/
showErrorNotice: ( message ) => {
if ( 'undefined' === typeof message ) {
return;
}
const noticeMessage = `<p>${ message }</p>`,
noticeOptions = {
type: 'error',
icon: 'info',
};
SUI.openNotice( 'wp-smush-ajax-notice', noticeMessage, noticeOptions );
const loadingButton = document.querySelector( '.sui-button-onload' );
if ( loadingButton ) {
loadingButton.classList.remove( 'sui-button-onload' );
}
},
/**
* Reset settings.
*
* @since 3.2.0
*/
resetSettings: () => {
const _nonce = document.getElementById( 'wp_smush_reset' );
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=reset_settings', true );
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
if ( 200 === xhr.status ) {
const res = JSON.parse( xhr.response );
if ( 'undefined' !== typeof res.success && res.success ) {
window.location.href = wp_smush_msgs.smush_url;
}
} else {
window.console.log(
'Request failed. Returned status of ' + xhr.status
);
}
};
xhr.send( '_ajax_nonce=' + _nonce.value );
},
/**
* Prepare error row. Will only allow to hide errors for WP media attachments (not nextgen).
*
* @since 1.9.0
* @since 3.12.0 Moved from Smush.
*
* @param {string} errorMsg Error message.
* @param {string} fileName File name.
* @param {string} thumbnail Thumbnail for image (if available).
* @param {number} id Image ID.
* @param {string} type Smush type: media or netxgen.
* @param {string} errorCode Error code.
*
* @return {string} Row with error.
*/
prepareBulkSmushErrorRow: (errorMsg, fileName, thumbnail, id, type, errorCode) => {
const thumbDiv =
thumbnail && 'undefined' !== typeof thumbnail ?
`<img class="attachment-thumbnail" src="${thumbnail}" />` :
'<i class="sui-icon-photo-picture" aria-hidden="true"></i>';
const editLink = window.wp_smush_msgs.edit_link.replace('{{id}}', id);
fileName =
'undefined' === fileName || 'undefined' === typeof fileName ?
'undefined' :
fileName;
let tableDiv =
`<div class="smush-bulk-error-row" data-error-code="${errorCode}">
<div class="smush-bulk-image-data">
<div class="smush-bulk-image-title">
${ thumbDiv }
<span class="smush-image-name">
<a href="${editLink}">${fileName}</a>
</span>
</div>
<div class="smush-image-error">
${errorMsg}
</div>
</div>`;
if ('media' === type) {
tableDiv +=
`<div class="smush-bulk-image-actions">
<a href="javascript:void(0)" class="sui-tooltip sui-tooltip-constrained sui-tooltip-left smush-ignore-image" data-tooltip="${window.wp_smush_msgs.error_ignore}" data-id="${id}">
${window.wp_smush_msgs.btn_ignore}
</a>
<a class="smush-link-detail" href="${editLink}">
${window.wp_smush_msgs.view_detail}
</a>
</div>`;
}
tableDiv += '</div>';
tableDiv += WP_Smush.helpers.upsellWithError( errorCode );
return tableDiv;
},
cacheUpsellErrorCode( errorCode ) {
this.cacheUpsellErrorCodes.push( errorCode );
},
/**
* Get upsell base on error code.
* @param {string} errorCode Error code.
* @returns {string}
*/
upsellWithError(errorCode) {
if (
!errorCode
|| !window.wp_smush_msgs['error_' + errorCode]
|| this.isUpsellRendered( errorCode )
) {
return '';
}
this.cacheRenderedUpsell( errorCode );
return '<div class="smush-bulk-error-row smush-error-upsell">' +
'<div class="smush-bulk-image-title">' +
'<span class="smush-image-error">' +
window.wp_smush_msgs['error_' + errorCode] +
'</span>' +
'</div></div>';
},
// Do not use arrow function to use `this`.
isUpsellRendered( errorCode ) {
return this.cacheUpsellErrorCodes.includes( errorCode );
},
// Do not use arrow function to use `this`.
cacheRenderedUpsell( errorCode ) {
this.cacheUpsellErrorCodes.push( errorCode );
},
/**
* Get error message from Ajax response or Error.
* @param {Object} resp
*/
getErrorMessage: ( resp ) => {
return resp.message || resp.data && resp.data.message ||
resp.responseJSON && resp.responseJSON.data && resp.responseJSON.data.message ||
window.wp_smush_msgs.generic_ajax_error ||
resp.status && 'Request failed. Returned status of ' + resp.status
},
/**
* Displays a floating message from response,
* using the #wp-smush-ajax-notice container.
*
* @param {Object|string} notice
* @param {Object} noticeOptions
*/
showNotice: function( notice, noticeOptions ) {
let message;
if ( 'object' === typeof notice ) {
message = this.getErrorMessage( notice );
} else {
message = notice;
}
if ( ! message ) {
return;
}
noticeOptions = noticeOptions || {};
noticeOptions = Object.assign({
showdismiss: false,
autoclose: true,
},noticeOptions);
noticeOptions = {
type: noticeOptions.type || 'error',
icon: noticeOptions.icon || ( 'success' === noticeOptions.type ? 'check-tick' : 'info' ),
dismiss: {
show: noticeOptions.showdismiss,
label: window.wp_smush_msgs.noticeDismiss,
tooltip: window.wp_smush_msgs.noticeDismissTooltip,
},
autoclose: {
show: noticeOptions.autoclose
}
};
const noticeMessage = `<p>${ message }</p>`;
SUI.openNotice( 'wp-smush-ajax-notice', noticeMessage, noticeOptions );
return Promise.resolve( '#wp-smush-ajax-notice' );
},
closeNotice() {
window.SUI.closeNotice( 'wp-smush-ajax-notice' );
},
renderActivationCDNNotice: function( noticeMessage ) {
const animatedNotice = document.getElementById('wp-smush-animated-upsell-notice');
if ( animatedNotice ) {
return;
}
const upsellHtml = `<div class="sui-notice sui-notice-info sui-margin-top" id="wp-smush-animated-upsell-notice">
<div class="sui-notice-content">
<div class="sui-notice-message">
<i class="sui-notice-icon sui-icon-info" aria-hidden="true"></i>
<p>${noticeMessage}</p>
</div>
</div>
</div>`;
document.querySelector( '#smush-box-bulk .wp-smush-bulk-wrapper' ).outerHTML += upsellHtml;
}
};
WP_Smush.helpers.init();
}() );

View File

@ -0,0 +1,385 @@
/* global WP_Smush */
/**
* Scan Media Library.
*
*/
import SmushProgress from '../common/progressbar';
import MediaLibraryScanner from '../common/media-library-scanner';
import { GlobalStats } from '../common/globalStats';
( function() {
'use strict';
if ( ! window.wp_smush_msgs ) {
return;
}
const $ = document.querySelector.bind( document );
const existScanProgressBar = $( '.wp-smush-scan-progress-bar-wrapper' );
if ( ! existScanProgressBar ) {
return;
}
const recheckImagesBtn = $( '.wp-smush-scan' );
if ( ! recheckImagesBtn ) {
return;
}
const bulkSmushButton = $( '.wp-smush-bo-start' ) || $( '.wp-smush-bulk-wrapper .wp-smush-all' );
const { __ } = wp.i18n;
class MediaLibraryScannerOnBulkSmush extends MediaLibraryScanner {
constructor() {
super();
this.runBulkSmushOnComplete = false;
this.restoreButton = $( '.wp-smush-restore' );
this.autoBulkSmushNotification = $( '.wp-smush-auto-bulk-smush-notification' );
}
startScanThenBulkSmushOnComplete() {
this.runBulkSmushOnComplete = true;
return this.startScan( true );
}
onStart() {
this.hideRecheckNotice();
this.disableRelatedButtons();
this.setRecheckImagesButtonOnLoad();
this.toggleBulkSmushBoxContent();
return this;
}
onStartFailure( response ) {
super.onStartFailure( response );
this.revertRelatedButtons();
}
onCloseProgressBar() {
this.maybeHideAutoBulkSmushNotification();
}
disableRelatedButtons() {
this.restoreButton.setAttribute( 'disabled', true );
if ( bulkSmushButton ) {
bulkSmushButton.setAttribute( 'disabled', true );
this.setInnerText( bulkSmushButton, __( 'Waiting for Re-check to finish', 'wp-smushit' ) );
}
}
revertRelatedButtons() {
if ( bulkSmushButton ) {
bulkSmushButton.removeAttribute( 'disabled' );
this.revertInnerText( bulkSmushButton );
}
this.restoreButton.removeAttribute( 'disabled' );
this.revertRecheckImagesButton();
return this;
}
setRecheckImagesButtonOnLoad() {
// recheckImagesBtn.classList.add( 'sui-button-onload' );
this.disableRecheckImagesButton();
this.setInnerText( recheckImagesBtn.querySelector( '.wp-smush-inner-text' ), __( 'Checking Images', 'wp-smushit' ) );
}
disableRecheckImagesButton() {
recheckImagesBtn.setAttribute( 'disabled', true );
}
revertRecheckImagesButton() {
// recheckImagesBtn.classList.remove( 'sui-button-onload' );
recheckImagesBtn.removeAttribute( 'disabled' );
this.revertInnerText( recheckImagesBtn.querySelector( '.wp-smush-inner-text' ) );
}
beforeUpdateStatus( stats ) {
this.runBulkSmushOnComplete = stats?.optimize_on_scan_completed;
this.maybeShowAutoBulkSmushNotification();
}
onDead( stats ) {
super.onDead( stats );
this.revertRelatedButtons();
this.setRequiredScanForBulkSmushButton();
}
onFinish( stats ) {
const globalStats = stats.global_stats;
super.onFinish( stats );
this.revertRelatedButtons();
this.toggleBulkSmushDescription( globalStats );
if ( globalStats.is_outdated ) {
this.setRequiredScanForBulkSmushButton();
} else {
this.removeScanEventFromBulkSmushButton();
}
this.revertRecheckWarning();
}
onCompleted( stats ) {
const requiredReloadPage = ! bulkSmushButton;
if ( requiredReloadPage ) {
window.location.reload();
return;
}
this.onFinish( stats );
const globalStats = stats.global_stats;
const allImagesSmushed = globalStats.remaining_count < 1;
if ( allImagesSmushed ) {
return;
}
if ( ! this.runBulkSmushOnComplete ) {
this.showRecheckNoticeSuccess();
return;
}
this.runBulkSmushOnComplete = false;
this.triggerBulkSmushEvent( stats );
}
showNotice( stats ) {
if ( ! stats.notice ) {
return;
}
let type = 'success';
if ( 'undefined' !== typeof stats.noticeType ) {
type = stats.noticeType;
}
window.SUI.openNotice(
'wp-smush-ajax-notice',
'<p>' + stats.notice + '</p>',
{ type, icon: 'check-tick' }
);
}
showRecheckNoticeSuccess() {
const recheckNotice = $( '.wp-smush-recheck-images-notice-box' );
if ( ! recheckNotice ) {
return;
}
this.showAnElement( recheckNotice );
this.hideAnElement( recheckNotice.querySelector( '.wp-smush-recheck-images-notice-warning' ) );
this.showAnElement( recheckNotice.querySelector( '.wp-smush-recheck-images-notice-success' ) );
}
showRecheckNoticeWarning() {
const recheckNotice = $( '.wp-smush-recheck-images-notice-box' );
if ( ! recheckNotice ) {
return;
}
this.showAnElement( recheckNotice );
this.hideAnElement( recheckNotice.querySelector( '.wp-smush-recheck-images-notice-success' ) );
this.showAnElement( recheckNotice.querySelector( '.wp-smush-recheck-images-notice-warning' ) );
}
hideRecheckNotice() {
this.hideAnElement( $( '.wp-smush-recheck-images-notice-box' ) );
}
showProgressErrorNoticeOnRecheckNotice() {
const recheckWarningElement = $( '.wp-smush-recheck-images-notice-box .wp-smush-recheck-images-notice-warning' );
if ( ! recheckWarningElement ) {
return;
}
recheckWarningElement.classList.add( 'sui-notice-error' );
recheckWarningElement.classList.remove( 'sui-notice-warning' );
this.setInnerText( recheckWarningElement.querySelector( 'span' ), this.getErrorProgressMessage() );
this.showRecheckNoticeWarning();
}
revertRecheckWarning() {
const recheckWarningElement = $( '.wp-smush-recheck-images-notice-box .wp-smush-recheck-images-notice-warning' );
if ( ! recheckWarningElement ) {
return;
}
recheckWarningElement.classList.add( 'sui-notice-warning' );
recheckWarningElement.classList.remove( 'sui-notice-error' );
this.revertInnerText( recheckWarningElement.querySelector( 'span' ) );
}
triggerBulkSmushEvent( stats ) {
this.disableRecheckImagesButton();
if ( stats.enabled_background_process ) {
this.triggerBackgroundBulkSmushEvent( stats.global_stats );
} else {
this.triggerAjaxBulkSmushEvent( stats.global_stats );
}
}
toggleBulkSmushDescription( globalStats ) {
if ( SmushProgress.isEmptyObject ) {
return;
}
if ( globalStats.remaining_count < 1 ) {
SmushProgress.hideBulkSmushDescription();
SmushProgress.showBulkSmushAllDone();
} else {
SmushProgress.showBulkSmushDescription();
SmushProgress.hideBulkSmushAllDone();
}
}
setRequiredScanForBulkSmushButton() {
bulkSmushButton && bulkSmushButton.classList.add( 'wp-smush-scan-and-bulk-smush' );
}
removeScanEventFromBulkSmushButton() {
bulkSmushButton && bulkSmushButton.classList.remove( 'wp-smush-scan-and-bulk-smush' );
}
triggerBackgroundBulkSmushEvent( globalStats ) {
document.dispatchEvent(
new CustomEvent( 'backgroundBulkSmushOnScanCompleted', {
detail: globalStats
} )
);
}
triggerAjaxBulkSmushEvent( globalStats ) {
document.dispatchEvent(
new CustomEvent( 'ajaxBulkSmushOnScanCompleted', {
detail: globalStats
} )
);
}
onCancelled( stats ) {
this.onFinish( stats );
this.runBulkSmushOnComplete = false;
this.setRequiredScanForBulkSmushButton();
}
maybeShowAutoBulkSmushNotification() {
if (
! this.runBulkSmushOnComplete
) {
return;
}
this.showAnElement( this.autoBulkSmushNotification );
}
maybeHideAutoBulkSmushNotification() {
if (
! this.runBulkSmushOnComplete
) {
return;
}
this.hideAnElement( this.autoBulkSmushNotification );
}
toggleBulkSmushBoxContent() {
GlobalStats.resetAndHideBulkErrors();
this.toggleBulkSmushDescription( GlobalStats.getGlobalStats() );
}
}
const mediaLibScanner = new MediaLibraryScannerOnBulkSmush();
/**
* Event Listeners.
*/
// Background Scan Media Library.
const registerScanMediaLibraryEvent = () => {
if ( ! recheckImagesBtn ) {
return;
}
const canScanInBackground = recheckImagesBtn.classList.contains( 'wp-smush-background-scan' );
if ( ! canScanInBackground ) {
return;
}
recheckImagesBtn.addEventListener( 'click', () => mediaLibScanner.startScan() );
//Check scan is running.
if ( window.wp_smushit_data.media_library_scan?.in_processing ) {
mediaLibScanner.onStart().showProgressBar().autoSyncStatus();
return;
}
if ( window.location.search.includes( 'smush-action=start-scan-media' ) ) {
recheckImagesBtn.click();
const removeScanActionFromURLAddress = () => {
const cleanedURL = window.location.href.replace( '&smush-action=start-scan-media', '' );
window.history.pushState( null, null, cleanedURL );
};
removeScanActionFromURLAddress();
}
};
registerScanMediaLibraryEvent();
/**
* Recheck Images Notice events.
*/
const registerEventsRelatedRecheckImagesNotice = () => {
const recheckImagesNotice = $( '.wp-smush-recheck-images-notice-box' );
if ( ! recheckImagesNotice || ! recheckImagesBtn ) {
return;
}
const triggerBackgroundScanImagesLink = recheckImagesNotice.querySelector( '.wp-smush-trigger-background-scan' );
if ( triggerBackgroundScanImagesLink ) {
triggerBackgroundScanImagesLink.onclick = ( e ) => {
e.preventDefault();
recheckImagesBtn.click();
};
if ( window.wp_smushit_data.media_library_scan?.is_dead ) {
mediaLibScanner.showProgressErrorNoticeOnRecheckNotice();
} else if( window.wp_smushit_data.is_outdated ) {
mediaLibScanner.showRecheckNoticeWarning();
}
}
const triggerBulkSmush = recheckImagesNotice.querySelector( '.wp-smush-trigger-bulk-smush' );
if ( triggerBulkSmush && bulkSmushButton ) {
triggerBulkSmush.onclick = ( e ) => {
e.preventDefault();
recheckImagesNotice.classList.add( 'sui-hidden' );
bulkSmushButton.click();
};
}
const dismissNotices = recheckImagesNotice.querySelectorAll( 'button.sui-button-icon' );
if ( dismissNotices ) {
dismissNotices.forEach( ( dismissNotice ) => {
dismissNotice.onclick = ( e ) => {
dismissNotice.closest( '.sui-recheck-images-notice' ).classList.add( 'sui-hidden' );
};
} );
}
document.addEventListener( 'onSavedSmushSettings', function( e ) {
if ( ! e?.detail?.is_outdated_stats ) {
return;
}
mediaLibScanner.setRequiredScanForBulkSmushButton();
recheckImagesNotice.classList.remove( 'sui-hidden' );
recheckImagesNotice.querySelector( '.wp-smush-recheck-images-notice-success' ).classList.add( 'sui-hidden' );
recheckImagesNotice.querySelector( '.wp-smush-recheck-images-notice-warning' ).classList.remove( 'sui-hidden' );
} );
};
registerEventsRelatedRecheckImagesNotice();
// Scan and Bulk Smush.
const registerScanAndBulkSmushEvent = () => {
if ( ! bulkSmushButton ) {
return;
}
const handleScanAndBulkSmush = ( e ) => {
const shouldRunScan = bulkSmushButton.classList.contains( 'wp-smush-scan-and-bulk-smush' );
if ( ! shouldRunScan ) {
return;
}
e.preventDefault();
mediaLibScanner.startScanThenBulkSmushOnComplete();
};
bulkSmushButton.addEventListener( 'click', handleScanAndBulkSmush );
};
registerScanAndBulkSmushEvent();
}() );

View File

@ -0,0 +1,63 @@
/* global WP_Smush */
/**
* Scan Media Library.
*
*/
import MediaLibraryScanner from '../common/media-library-scanner';
( function() {
'use strict';
if ( ! window.wp_smush_msgs ) {
return;
}
const $ = document.querySelector.bind( document );
const existScanProgressBar = $( '.wp-smush-scan-progress-bar-wrapper' );
if ( ! existScanProgressBar ) {
return;
}
const recheckImagesBtn = $( '.wp-smush-scan' );
if ( recheckImagesBtn ) {
return;
}
//Check scan is running.
const is_scan_running = window.wp_smushit_data.media_library_scan?.in_processing;
if ( ! is_scan_running ) {
return;
}
const { __ } = wp.i18n;
class mediaLibraryScannerOnDashboard extends MediaLibraryScanner {
constructor() {
super();
this.bulkSmushLink = $( '.wp-smush-bulk-smush-link' );
}
onShowProgressBar() {
this.disableBulkSmushLink();
}
onCloseProgressBar() {
this.revertBulkSmushLink();
}
disableBulkSmushLink() {
if ( ! this.bulkSmushLink ) {
return;
}
this.bulkSmushLink.setAttribute( 'disabled', true );
this.setInnerText( this.bulkSmushLink, __( 'Waiting for Re-check to finish', 'wp-smushit' ) );
}
revertBulkSmushLink() {
if ( ! this.bulkSmushLink ) {
return;
}
this.bulkSmushLink.removeAttribute( 'disabled' );
this.revertInnerText( this.bulkSmushLink );
}
}
( new mediaLibraryScannerOnDashboard() ).showProgressBar().autoSyncStatus();
}() );

View File

@ -0,0 +1,52 @@
import Smush from '../smush/smush';
import SmushProcess from '../common/progressbar';
(function($) {
$(function() {
/** Handle NextGen Gallery smush button click **/
$('body').on('click', '.wp-smush-nextgen-send', function (e) {
// prevent the default action
e.preventDefault();
new Smush($(this), false, 'nextgen');
});
/** Handle NextGen Gallery Bulk smush button click **/
$('body').on('click', '.wp-smush-nextgen-bulk', function (e) {
// prevent the default action
e.preventDefault();
// Remove existing Re-Smush notices.
// TODO: REMOVE re-smush-notice since no longer used.
$('.wp-smush-resmush-notice').remove();
//Check for ids, if there is none (Unsmushed or lossless), don't call smush function
if (
'undefined' === typeof wp_smushit_data ||
(wp_smushit_data.unsmushed.length === 0 &&
wp_smushit_data.resmush.length === 0)
) {
return false;
}
const bulkSmush = new Smush( $(this), true, 'nextgen' );
SmushProcess.setOnCancelCallback( () => {
bulkSmush.cancelAjax();
}).update( 0, bulkSmush.ids.length ).show();
jQuery('.wp-smush-all, .wp-smush-scan').prop('disabled', true);
$('.wp-smush-notice.wp-smush-remaining').hide();
// Run bulk Smush.
bulkSmush.run();
})
.on('click', '.wp-smush-trigger-nextgen-bulk', function(e){
e.preventDefault();
const bulkSmushButton = $('.wp-smush-nextgen-bulk');
if ( bulkSmushButton.length ) {
bulkSmushButton.trigger('click');
SUI.closeNotice( 'wp-smush-ajax-notice' );
}
});
});
}(window.jQuery));

View File

@ -0,0 +1,81 @@
/* global ajaxurl */
/* global wp_smush_msgs */
( function( $ ) {
'use strict';
const s3alert = $( '#wp-smush-s3support-alert' );
/**
* S3 support alert.
*
* @since 3.6.2 Moved from class-s3.php
*/
if ( s3alert.length ) {
const noticeOptions = {
type: 'warning',
icon: 'info',
dismiss: {
show: true,
label: wp_smush_msgs.noticeDismiss,
tooltip: wp_smush_msgs.noticeDismissTooltip,
},
};
window.SUI.openNotice(
'wp-smush-s3support-alert',
s3alert.data( 'message' ),
noticeOptions
);
}
// Dismiss S3 support alert.
s3alert.on( 'click', 'button', () => {
$.post( ajaxurl,
{
action: 'dismiss_s3support_alert',
_ajax_nonce: window.wp_smush_msgs.nonce,
}
);
} );
// Remove API message.
$( '#wp-smush-api-message button.sui-button-icon' ).on( 'click', function( e ) {
e.preventDefault();
const notice = $( '#wp-smush-api-message' );
notice.slideUp( 'slow', function() {
notice.remove();
} );
$.post( ajaxurl,
{
action: 'hide_api_message',
_ajax_nonce: window.wp_smush_msgs.nonce,
}
);
} );
// Hide the notice after a CTA button was clicked
function removeNotice( e ) {
const $notice = $( e.currentTarget ).closest( '.smush-notice' );
$notice.fadeTo( 100, 0, () =>
$notice.slideUp( 100, () => $notice.remove() )
);
}
// Only used for the Dashboard notification for now.
$( '.smush-notice .smush-notice-act' ).on( 'click', ( e ) => {
removeNotice( e );
} );
// Dismiss the update notice.
$( '.wp-smush-update-info' ).on( 'click', '.notice-dismiss', ( e ) => {
e.preventDefault();
removeNotice( e );
$.post( ajaxurl,
{
action: 'dismiss_update_info',
_ajax_nonce: window.wp_smush_msgs.nonce,
}
);
} );
}( jQuery ) );

View File

@ -0,0 +1,378 @@
/* global WP_Smush */
/* global ajaxurl */
/**
* Modals JavaScript code.
*/
( function() {
'use strict';
/**
* Onboarding modal.
*
* @since 3.1
*/
WP_Smush.onboarding = {
membership: 'free', // Assume free by default.
onboardingModal: document.getElementById( 'smush-onboarding-dialog' ),
first_slide: 'usage',
settings: {
first: true,
last: false,
slide: 'usage',
value: false,
},
selection: {
usage: false,
auto: true,
lossy: true,
strip_exif: true,
original: false,
lazy_load: true,
},
contentContainer: document.getElementById( 'smush-onboarding-content' ),
onboardingSlides: [
'usage',
'auto',
'lossy',
'strip_exif',
'original',
'lazy_load',
],
touchX: null,
touchY: null,
recheckImagesLink: '',
/**
* Init module.
*/
init() {
if ( ! this.onboardingModal ) {
return;
}
const dialog = document.getElementById( 'smush-onboarding' );
this.membership = dialog.dataset.type;
this.recheckImagesLink = dialog.dataset.ctaUrl;
if ( 'pro' !== this.membership ) {
this.onboardingSlides = [
'usage',
'auto',
'lossy',
'strip_exif',
'lazy_load',
];
}
if ( 'false' === dialog.dataset.tracking ) {
this.onboardingSlides.pop();
}
this.renderTemplate();
// Skip setup.
const skipButton = this.onboardingModal.querySelector(
'.smush-onboarding-skip-link'
);
if ( skipButton ) {
skipButton.addEventListener( 'click', this.skipSetup.bind( this ) );
}
// Show the modal.
window.SUI.openModal(
'smush-onboarding-dialog',
'wpcontent',
undefined,
false
);
},
/**
* Get swipe coordinates.
*
* @param {Object} e
*/
handleTouchStart( e ) {
const firstTouch = e.touches[ 0 ];
this.touchX = firstTouch.clientX;
this.touchY = firstTouch.clientY;
},
/**
* Process swipe left/right.
*
* @param {Object} e
*/
handleTouchMove( e ) {
if ( ! this.touchX || ! this.touchY ) {
return;
}
const xUp = e.touches[ 0 ].clientX,
yUp = e.touches[ 0 ].clientY,
xDiff = this.touchX - xUp,
yDiff = this.touchY - yUp;
if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {
if ( xDiff > 0 ) {
if ( false === WP_Smush.onboarding.settings.last ) {
WP_Smush.onboarding.next( null, 'next' );
}
} else if ( false === WP_Smush.onboarding.settings.first ) {
WP_Smush.onboarding.next( null, 'prev' );
}
}
this.touchX = null;
this.touchY = null;
},
/**
* Update the template, register new listeners.
*
* @param {string} directionClass Accepts: fadeInRight, fadeInLeft, none.
*/
renderTemplate( directionClass = 'none' ) {
// Grab the selected value.
const input = this.onboardingModal.querySelector(
'input[type="checkbox"]'
);
if ( input ) {
this.selection[ input.id ] = input.checked;
}
const template = WP_Smush.onboarding.template( 'smush-onboarding' );
const content = template( this.settings );
if ( content ) {
this.contentContainer.innerHTML = content;
if ( 'none' === directionClass ) {
this.contentContainer.classList.add( 'loaded' );
} else {
this.contentContainer.classList.remove( 'loaded' );
this.contentContainer.classList.add( directionClass );
setTimeout( () => {
this.contentContainer.classList.add( 'loaded' );
this.contentContainer.classList.remove(
directionClass
);
}, 600 );
}
}
this.onboardingModal.addEventListener(
'touchstart',
this.handleTouchStart,
false
);
this.onboardingModal.addEventListener(
'touchmove',
this.handleTouchMove,
false
);
this.bindSubmit();
},
/**
* Catch "Finish setup wizard" button click.
*/
bindSubmit() {
const submitButton = this.onboardingModal.querySelector(
'button[type="submit"]'
);
const self = this;
if ( submitButton ) {
submitButton.addEventListener( 'click', function( e ) {
e.preventDefault();
// Because we are not rendering the template, we need to update the last element value.
const input = self.onboardingModal.querySelector(
'input[type="checkbox"]'
);
if ( input ) {
self.selection[ input.id ] = input.checked;
}
const _nonce = document.getElementById(
'smush_quick_setup_nonce'
);
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=smush_setup', true );
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
if ( 200 === xhr.status ) {
self.onFinishingSetup();
} else {
window.console.log(
'Request failed. Returned status of ' +
xhr.status
);
}
};
xhr.send(
'smush_settings=' +
JSON.stringify( self.selection ) +
'&_ajax_nonce=' +
_nonce.value
);
} );
}
},
onFinishingSetup() {
this.onFinish();
this.startRecheckImages();
},
onFinish() {
window.SUI.closeModal();
},
startRecheckImages() {
if ( ! this.recheckImagesLink ) {
return;
}
window.location.href = this.recheckImagesLink;
},
/**
* Handle navigation.
*
* @param {Object} e
* @param {null|string} whereTo
*/
next( e, whereTo = null ) {
const index = this.onboardingSlides.indexOf( this.settings.slide );
let newIndex = 0;
if ( ! whereTo ) {
newIndex =
null !== e && e.classList.contains( 'next' )
? index + 1
: index - 1;
} else {
newIndex = 'next' === whereTo ? index + 1 : index - 1;
}
const directionClass =
null !== e && e.classList.contains( 'next' )
? 'fadeInRight'
: 'fadeInLeft';
this.settings = {
first: 0 === newIndex,
last: newIndex + 1 === this.onboardingSlides.length, // length !== index
slide: this.onboardingSlides[ newIndex ],
value: this.selection[ this.onboardingSlides[ newIndex ] ],
};
this.renderTemplate( directionClass );
},
/**
* Handle circle navigation.
*
* @param {string} target
*/
goTo( target ) {
const newIndex = this.onboardingSlides.indexOf( target );
this.settings = {
first: 0 === newIndex,
last: newIndex + 1 === this.onboardingSlides.length, // length !== index
slide: target,
value: this.selection[ target ],
};
this.renderTemplate();
},
/**
* Skip onboarding experience.
*/
skipSetup() {
const _nonce = document.getElementById( 'smush_quick_setup_nonce' );
const xhr = new XMLHttpRequest();
xhr.open(
'POST',
ajaxurl + '?action=skip_smush_setup&_ajax_nonce=' + _nonce.value
);
xhr.onload = () => {
if ( 200 === xhr.status ) {
this.onSkipSetup();
} else {
window.console.log(
'Request failed. Returned status of ' + xhr.status
);
}
};
xhr.send();
},
onSkipSetup() {
this.onFinish();
},
/**
* Hide new features modal.
* @since 3.7.0
* @since 3.12.2 Add a new parameter redirectUrl
*/
hideUpgradeModal: ( e, button ) => {
e.preventDefault();
button.classList.add( 'wp-smush-link-in-progress' );
const redirectUrl = button?.href;
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=hide_new_features&_ajax_nonce=' + window.wp_smush_msgs.nonce );
xhr.onload = () => {
window.SUI.closeModal();
button.classList.remove( 'wp-smush-link-in-progress' );
if ( 200 === xhr.status ) {
if ( redirectUrl ) {
window.location.href = redirectUrl;
}
} else {
window.console.log(
'Request failed. Returned status of ' + xhr.status
);
}
};
xhr.send();
},
};
/**
* Template function (underscores based).
*
* @type {Function}
*/
WP_Smush.onboarding.template = _.memoize( ( id ) => {
let compiled;
const options = {
evaluate: /<#([\s\S]+?)#>/g,
interpolate: /{{{([\s\S]+?)}}}/g,
escape: /{{([^}]+?)}}(?!})/g,
variable: 'data',
};
return ( data ) => {
_.templateSettings = options;
compiled =
compiled ||
_.template( document.getElementById( id ).innerHTML );
data.first_slide = WP_Smush.onboarding.first_slide;
return compiled( data );
};
} );
window.addEventListener( 'load', () => WP_Smush.onboarding.init() );
}() );

View File

@ -0,0 +1,13 @@
/**
* Shared UI JS libraries. Use only what we need to keep the vendor file size smaller.
*
* @package
*/
require( '@wpmudev/shared-ui/dist/js/_src/code-snippet' );
require( '@wpmudev/shared-ui/dist/js/_src/modal-dialog' );
require( '@wpmudev/shared-ui/dist/js/_src/notifications' );
require( '@wpmudev/shared-ui/dist/js/_src/select2.full' );
require( '@wpmudev/shared-ui/dist/js/_src/select2' );
require( '@wpmudev/shared-ui/dist/js/_src/tabs' );
require( '@wpmudev/shared-ui/dist/js/_src/upload' ); // Used on lazy load page (since 3.2.2).
require( '@wpmudev/shared-ui/dist/js/_src/reviews' );

View File

@ -0,0 +1,147 @@
/**
* BLOCK: extend image block
*/
const { createHigherOrderComponent } = wp.compose,
{ Fragment } = wp.element,
{ InspectorControls } = wp.blockEditor,
{ PanelBody } = wp.components;
/**
* Transform bytes to human readable format.
*
* @param {number} bytes
* @return {string} Readable size string.
*/
function humanFileSize( bytes ) {
const thresh = 1024,
units = [ 'kB', 'MB', 'GB', 'TB' ];
if ( Math.abs( bytes ) < thresh ) {
return bytes + ' B';
}
let u = -1;
do {
bytes /= thresh;
++u;
} while ( Math.abs( bytes ) >= thresh && u < units.length - 1 );
return bytes.toFixed( 1 ) + ' ' + units[ u ];
}
/**
* Generate Smush stats table.
*
* @param {number} id
* @param {Object} stats
* @return {*} Smush stats.
*/
export function smushStats( id, stats ) {
if ( 'undefined' === typeof stats ) {
return window.smush_vars.strings.gb.select_image;
} else if ( 'string' === typeof stats ) {
return stats;
}
return (
<div
id="smush-stats"
className="sui-smush-media smush-stats-wrapper hidden"
style={ { display: 'block' } }
>
<table className="wp-smush-stats-holder">
<thead>
<tr>
<th className="smush-stats-header">
{ window.smush_vars.strings.gb.size }
</th>
<th className="smush-stats-header">
{ window.smush_vars.strings.gb.savings }
</th>
</tr>
</thead>
<tbody>
{ Object.keys( stats.sizes )
.filter( ( item ) => 0 < stats.sizes[ item ].percent )
.map( ( item, i ) => (
<tr key={ i }>
<td>{ item.toUpperCase() }</td>
<td>
{ humanFileSize(
stats.sizes[ item ].bytes
) }{ ' ' }
( { stats.sizes[ item ].percent }% )
</td>
</tr>
) ) }
</tbody>
</table>
</div>
);
}
/**
* Fetch image data. If image is Smushing, update in 3 seconds.
*
* TODO: this could be optimized not to query so much.
*
* @param {Object} props
*/
export function fetchProps( props ) {
const image = new wp.api.models.Media( { id: props.attributes.id } ),
smushData = props.attributes.smush;
image.fetch( { attribute: 'smush' } ).done( function( img ) {
if ( 'string' === typeof img.smush ) {
props.setAttributes( { smush: img.smush } );
//setTimeout( () => fetch( props ), 3000 );
} else if (
'undefined' !== typeof img.smush &&
( 'undefined' === typeof smushData ||
JSON.stringify( smushData ) !== JSON.stringify( img.smush ) )
) {
props.setAttributes( { smush: img.smush } );
}
} );
}
/**
* Modify the blocks edit component.
* Receives the original block BlockEdit component and returns a new wrapped component.
*/
const smushStatsControl = createHigherOrderComponent( ( BlockEdit ) => {
return ( props ) => {
// If not image block or not selected, return unmodified block.
if (
'core/image' !== props.name ||
! props.isSelected ||
'undefined' === typeof props.attributes.id
) {
return (
<Fragment>
<BlockEdit { ...props } />
</Fragment>
);
}
const smushData = props.attributes.smush;
fetchProps( props );
return (
<Fragment>
<BlockEdit { ...props } />
<InspectorControls>
<PanelBody title={ window.smush_vars.strings.gb.stats }>
{ smushStats( props.attributes.id, smushData ) }
</PanelBody>
</InspectorControls>
</Fragment>
);
};
}, 'withInspectorControl' );
wp.hooks.addFilter(
'editor.BlockEdit',
'wp-smush/smush-data-control',
smushStatsControl
);

View File

@ -0,0 +1,138 @@
/* global WP_Smush */
/* global ajaxurl */
/**
* CDN functionality.
*
* @since 3.0
*/
( function() {
'use strict';
WP_Smush.CDN = {
cdnEnableButton: document.getElementById( 'smush-enable-cdn' ),
cdnDisableButton: document.getElementById( 'smush-cancel-cdn' ),
cdnStatsBox: document.querySelector( '.smush-cdn-stats' ),
init() {
/**
* Handle "Get Started" button click on disabled CDN page.
*/
if ( this.cdnEnableButton ) {
this.cdnEnableButton.addEventListener( 'click', ( e ) => {
e.preventDefault();
e.currentTarget.classList.add( 'sui-button-onload' );
this.toggle_cdn( true );
} );
}
/**
* Handle "Deactivate' button click on CDN page.
*/
if ( this.cdnDisableButton ) {
this.cdnDisableButton.addEventListener( 'click', ( e ) => {
e.preventDefault();
e.currentTarget.classList.add( 'sui-button-onload' );
this.toggle_cdn( false );
} );
}
this.updateStatsBox();
},
/**
* Toggle CDN.
*
* @since 3.0
*
* @param {boolean} enable
*/
toggle_cdn( enable ) {
const nonceField = document.getElementsByName(
'wp_smush_options_nonce'
);
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=smush_toggle_cdn', true );
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
if ( 200 === xhr.status ) {
const res = JSON.parse( xhr.response );
if ( 'undefined' !== typeof res.success && res.success ) {
window.location.search = 'page=smush-cdn';
} else if ( 'undefined' !== typeof res.data.message ) {
WP_Smush.helpers.showErrorNotice( res.data.message );
}
} else {
WP_Smush.helpers.showErrorNotice( 'Request failed. Returned status of ' + xhr.status );
}
};
xhr.send(
'param=' + enable + '&_ajax_nonce=' + nonceField[ 0 ].value
);
},
/**
* Update the CDN stats box in summary meta box. Only fetch new data when on CDN page.
*
* @since 3.0
*/
updateStatsBox() {
if (
'undefined' === typeof this.cdnStatsBox ||
! this.cdnStatsBox
) {
return;
}
// Only fetch the new stats, when user is on CDN page.
if ( ! window.location.search.includes( 'page=smush-cdn' ) ) {
return;
}
this.toggleElements();
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=get_cdn_stats', true );
xhr.onload = () => {
if ( 200 === xhr.status ) {
const res = JSON.parse( xhr.response );
if ( 'undefined' !== typeof res.success && res.success ) {
this.toggleElements();
} else if ( 'undefined' !== typeof res.data.message ) {
WP_Smush.helpers.showErrorNotice( res.data.message );
}
} else {
WP_Smush.helpers.showErrorNotice( 'Request failed. Returned status of ' + xhr.status );
}
};
xhr.send();
},
/**
* Show/hide elements during status update in the updateStatsBox()
*
* @since 3.1 Moved out from updateStatsBox()
*/
toggleElements() {
const spinner = this.cdnStatsBox.querySelector(
'.sui-icon-loader'
);
const elements = this.cdnStatsBox.querySelectorAll(
'.wp-smush-stats > :not(.sui-icon-loader)'
);
for ( let i = 0; i < elements.length; i++ ) {
elements[ i ].classList.toggle( 'sui-hidden' );
}
spinner.classList.toggle( 'sui-hidden' );
},
};
WP_Smush.CDN.init();
} )();

View File

@ -0,0 +1,208 @@
/* global WP_Smush */
/* global ajaxurl */
/**
* Directory scanner module that will Smush images in the Directory Smush modal.
*
* @since 2.8.1
*
* @param {string|number} totalSteps
* @param {string|number} currentStep
* @return {Object} Scan object.
* @class
*/
const DirectoryScanner = ( totalSteps, currentStep ) => {
totalSteps = parseInt( totalSteps );
currentStep = parseInt( currentStep );
let cancelling = false,
failedItems = 0,
skippedItems = 0;
const obj = {
scan() {
const remainingSteps = totalSteps - currentStep;
if ( currentStep !== 0 ) {
// Scan started on a previous page load.
step( remainingSteps ).fail( this.showScanError );
} else {
jQuery
.post( ajaxurl, {
action: 'directory_smush_start',
_ajax_nonce: window.wp_smush_msgs.nonce
}, () =>
step( remainingSteps ).fail( this.showScanError )
)
.fail( this.showScanError );
}
},
cancel() {
cancelling = true;
return jQuery.post( ajaxurl, {
action: 'directory_smush_cancel',
_ajax_nonce: window.wp_smush_msgs.nonce
} );
},
getProgress() {
if ( cancelling ) {
return 0;
}
// O M G ... Logic at it's finest!
const remainingSteps = totalSteps - currentStep;
return Math.min(
Math.round(
( parseInt( totalSteps - remainingSteps ) * 100 ) /
totalSteps
),
99
);
},
onFinishStep( progress ) {
jQuery( '.wp-smush-progress-dialog .sui-progress-state-text' ).html(
currentStep -
failedItems +
'/' +
totalSteps +
' ' +
window.wp_smush_msgs.progress_smushed
);
WP_Smush.directory.updateProgressBar( progress );
},
onFinish() {
WP_Smush.directory.updateProgressBar( 100 );
window.location.href =
window.wp_smush_msgs.directory_url + '&scan=done';
},
/**
* Displays an error when the scan request fails.
*
* @param {Object} res XHR object.
*/
showScanError( res ) {
const dialog = jQuery( '#wp-smush-progress-dialog' );
// Add the error class to show/hide elements in the dialog.
dialog
.removeClass( 'wp-smush-exceed-limit' )
.addClass( 'wp-smush-scan-error' );
// Add the error status and description to the error message.
dialog
.find( '#smush-scan-error' )
.text( `${ res.status } ${ res.statusText }` );
// Show/hide the 403 error specific instructions.
const forbiddenMessage = dialog.find( '.smush-403-error-message' );
if ( 403 !== res.status ) {
forbiddenMessage.addClass( 'sui-hidden' );
} else {
forbiddenMessage.removeClass( 'sui-hidden' );
}
},
limitReached() {
const dialog = jQuery( '#wp-smush-progress-dialog' );
dialog.addClass( 'wp-smush-exceed-limit' );
dialog
.find( '#cancel-directory-smush' )
.attr( 'data-tooltip', window.wp_smush_msgs.bulk_resume );
dialog
.find( '.sui-box-body .sui-icon-close' )
.removeClass( 'sui-icon-close' )
.addClass( 'sui-icon-play' );
dialog
.find( '#cancel-directory-smush' )
.attr( 'id', 'cancel-directory-smush-disabled' );
},
resume() {
const dialog = jQuery( '#wp-smush-progress-dialog' );
const resume = dialog.find( '#cancel-directory-smush-disabled' );
dialog.removeClass( 'wp-smush-exceed-limit' );
dialog
.find( '.sui-box-body .sui-icon-play' )
.removeClass( 'sui-icon-play' )
.addClass( 'sui-icon-close' );
resume.attr( 'data-tooltip', 'Cancel' );
resume.attr( 'id', 'cancel-directory-smush' );
obj.scan();
},
};
/**
* Execute a scan step recursively
*
* Private to avoid overriding
*
* @param {number} remainingSteps
*/
const step = function( remainingSteps ) {
if ( remainingSteps >= 0 ) {
currentStep = totalSteps - remainingSteps;
return jQuery.post(
ajaxurl,
{
action: 'directory_smush_check_step',
_ajax_nonce: window.wp_smush_msgs.nonce,
step: currentStep,
},
( response ) => {
// We're good - continue on.
if (
'undefined' !== typeof response.success &&
response.success
) {
if (
'undefined' !== typeof response.data &&
'undefined' !== typeof response.data.skipped &&
true === response.data.skipped
) {
skippedItems++;
}
currentStep++;
remainingSteps = remainingSteps - 1;
obj.onFinishStep( obj.getProgress() );
step( remainingSteps ).fail( obj.showScanError );
} else if (
'undefined' !== typeof response.data.error &&
'dir_smush_limit_exceeded' === response.data.error
) {
// Limit reached. Stop.
obj.limitReached();
} else {
// Error? never mind, continue, but count them.
failedItems++;
currentStep++;
remainingSteps = remainingSteps - 1;
obj.onFinishStep( obj.getProgress() );
step( remainingSteps ).fail( obj.showScanError );
}
}
);
}
return jQuery.post(
ajaxurl,
{
action: 'directory_smush_finish',
_ajax_nonce: window.wp_smush_msgs.nonce,
items: totalSteps - ( failedItems + skippedItems ),
failed: failedItems,
skipped: skippedItems,
},
( response ) => obj.onFinish( response )
);
};
return obj;
};
export default DirectoryScanner;

View File

@ -0,0 +1,287 @@
/* global WP_Smush */
/* global ajaxurl */
/**
* Lazy loading functionality.
*
* @since 3.0
*/
( function() {
'use strict';
WP_Smush.Lazyload = {
lazyloadEnableButton: document.getElementById(
'smush-enable-lazyload'
),
lazyloadDisableButton: document.getElementById(
'smush-cancel-lazyload'
),
init() {
const self = this;
/**
* Handle "Activate" button click on disabled Lazy load page.
*/
if ( this.lazyloadEnableButton ) {
this.lazyloadEnableButton.addEventListener( 'click', ( e ) => {
e.preventDefault();
e.currentTarget.classList.add( 'sui-button-onload' );
this.toggle_lazy_load( true );
} );
}
/**
* Handle "Deactivate' button click on Lazy load page.
*/
if ( this.lazyloadDisableButton ) {
this.lazyloadDisableButton.addEventListener( 'click', ( e ) => {
e.preventDefault();
e.currentTarget.classList.add( 'sui-button-onload' );
this.toggle_lazy_load( false );
} );
}
/**
* Handle "Remove icon" button click on Lazy load page.
*
* This removes the image from the upload placeholder.
*
* @since 3.2.2
*/
const removeSpinner = document.getElementById(
'smush-remove-spinner'
);
if ( removeSpinner ) {
removeSpinner.addEventListener( 'click', ( e ) => {
e.preventDefault();
this.removeLoaderIcon();
} );
}
const removePlaceholder = document.getElementById(
'smush-remove-placeholder'
);
if ( removePlaceholder ) {
removePlaceholder.addEventListener( 'click', ( e ) => {
e.preventDefault();
this.removeLoaderIcon( 'placeholder' );
} );
}
/**
* Handle "Remove" icon click.
*
* This removes the select icon from the list (not same as above functions).
*
* @since 3.2.2
*/
const items = document.querySelectorAll( '.smush-ll-remove' );
if ( items && 0 < items.length ) {
items.forEach( function( el ) {
el.addEventListener( 'click', ( e ) => {
e.preventDefault();
e.target.closest( 'li' ).style.display = 'none';
self.remove(
e.target.dataset.id,
e.target.dataset.type
);
} );
} );
}
this.handlePredefinedPlaceholders();
},
/**
* Handle background color changes for the two predefined placeholders.
*
* @since 3.7.1
*/
handlePredefinedPlaceholders() {
const pl1 = document.getElementById( 'placeholder-icon-1' );
if ( pl1 ) {
pl1.addEventListener( 'click', () => this.changeColor( '#F3F3F3' ) );
}
const pl2 = document.getElementById( 'placeholder-icon-2' );
if ( pl2 ) {
pl2.addEventListener( 'click', () => this.changeColor( '#333333' ) );
}
},
/**
* Set color.
*
* @since 3.7.1
* @param {string} color
*/
changeColor( color ) {
document.getElementById( 'smush-color-picker' ).value = color;
document.querySelector( '.sui-colorpicker-hex .sui-colorpicker-value > span > span' ).style.backgroundColor = color;
document.querySelector( '.sui-colorpicker-hex .sui-colorpicker-value > input' ).value = color;
},
/**
* Toggle lazy loading.
*
* @since 3.2.0
*
* @param {string} enable
*/
toggle_lazy_load( enable ) {
const nonceField = document.getElementsByName(
'wp_smush_options_nonce'
);
const xhr = new XMLHttpRequest();
xhr.open(
'POST',
ajaxurl + '?action=smush_toggle_lazy_load',
true
);
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
if ( 200 === xhr.status ) {
const res = JSON.parse( xhr.response );
if ( 'undefined' !== typeof res.success && res.success ) {
window.location.search = 'page=smush-lazy-load';
} else if ( 'undefined' !== typeof res.data.message ) {
WP_Smush.helpers.showErrorNotice( res.data.message );
document.querySelector( '.sui-button-onload' ).classList.remove( 'sui-button-onload' );
}
} else {
WP_Smush.helpers.showErrorNotice( 'Request failed. Returned status of ' + xhr.status );
document.querySelector( '.sui-button-onload' ).classList.remove( 'sui-button-onload' );
}
};
xhr.send(
'param=' + enable + '&_ajax_nonce=' + nonceField[ 0 ].value
);
},
/**
* Add lazy load spinner icon.
*
* @since 3.2.2
* @param {string} type Accepts: spinner, placeholder.
*/
addLoaderIcon( type = 'spinner' ) {
let frame;
// If the media frame already exists, reopen it.
if ( frame ) {
frame.open();
return;
}
// Create a new media frame
frame = wp.media( {
title: 'Select or upload an icon',
button: {
text: 'Select icon',
},
multiple: false, // Set to true to allow multiple files to be selected
} );
// When an image is selected in the media frame...
frame.on( 'select', function() {
// Get media attachment details from the frame state
const attachment = frame
.state()
.get( 'selection' )
.first()
.toJSON();
// Send the attachment URL to our custom image input field.
const imageIcon = document.getElementById(
'smush-' + type + '-icon-preview'
);
imageIcon.style.backgroundImage =
'url("' + attachment.url + '")';
imageIcon.style.display = 'block';
// Send the attachment id to our hidden input
document
.getElementById( 'smush-' + type + '-icon-file' )
.setAttribute( 'value', attachment.id );
// Hide the add image link
document.getElementById(
'smush-upload-' + type
).style.display = 'none';
// Unhide the remove image link
const removeDiv = document.getElementById(
'smush-remove-' + type
);
removeDiv.querySelector( 'span' ).innerHTML =
attachment.filename;
removeDiv.style.display = 'block';
} );
// Finally, open the modal on click
frame.open();
},
/**
* Remove lazy load spinner icon.
*
* @since 3.2.2
* @param {string} type Accepts: spinner, placeholder.
*/
removeLoaderIcon: ( type = 'spinner' ) => {
// Clear out the preview image
const imageIcon = document.getElementById(
'smush-' + type + '-icon-preview'
);
imageIcon.style.backgroundImage = '';
imageIcon.style.display = 'none';
// Un-hide the add image link
document.getElementById( 'smush-upload-' + type ).style.display =
'block';
// Hide the delete image link
document.getElementById( 'smush-remove-' + type ).style.display =
'none';
// Delete the image id from the hidden input
document
.getElementById( 'smush-' + type + '-icon-file' )
.setAttribute( 'value', '' );
},
/**
* Remove item.
*
* @param {number} id Image ID.
* @param {string} type Accepts: spinner, placeholder.
*/
remove: ( id, type = 'spinner' ) => {
const nonceField = document.getElementsByName(
'wp_smush_options_nonce'
);
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=smush_remove_icon', true );
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.send(
'id=' +
id +
'&type=' +
type +
'&_ajax_nonce=' +
nonceField[ 0 ].value
);
},
};
WP_Smush.Lazyload.init();
} )();

View File

@ -0,0 +1,180 @@
/* global smush_vars */
/* global _ */
/**
* Adds a Smush Now button and displays stats in Media Attachment Details Screen
*/
(function ($, _) {
'use strict';
// Local reference to the WordPress media namespace.
const smushMedia = wp.media,
sharedTemplate =
"<span class='setting smush-stats' data-setting='smush'>" +
"<span class='name'><%= label %></span>" +
"<span class='value'><%= value %></span>" +
'</span>',
template = _.template(sharedTemplate);
/**
* Create the template.
*
* @param {string} smushHTML
* @return {Object} Template object
*/
const prepareTemplate = function (smushHTML) {
/**
* @param {Array} smush_vars.strings Localization strings.
* @param {Object} smush_vars Object from wp_localize_script()
*/
return template({
label: smush_vars.strings.stats_label,
value: smushHTML,
});
};
if (
'undefined' !== typeof smushMedia.view &&
'undefined' !== typeof smushMedia.view.Attachment.Details.TwoColumn
) {
// Local instance of the Attachment Details TwoColumn used in the edit attachment modal view
const smushMediaTwoColumn =
smushMedia.view.Attachment.Details.TwoColumn;
/**
* Add Smush details to attachment.
*
* A similar view to media.view.Attachment.Details
* for use in the Edit Attachment modal.
*
* @see wp-includes/js/media-grid.js
*/
smushMedia.view.Attachment.Details.TwoColumn = smushMediaTwoColumn.extend(
{
initialize() {
smushMediaTwoColumn.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'change:smush', this.render);
},
render() {
// Ensure that the main attachment fields are rendered.
smushMedia.view.Attachment.prototype.render.apply(
this,
arguments
);
const smushHTML = this.model.get('smush');
if (typeof smushHTML === 'undefined') {
return this;
}
this.model.fetch();
/**
* Detach the views, append our custom fields, make sure that our data is fully updated
* and re-render the updated view.
*/
this.views.detach();
this.$el
.find('.settings')
.append(prepareTemplate(smushHTML));
this.views.render();
return this;
},
}
);
}
// Local instance of the Attachment Details TwoColumn used in the edit attachment modal view
const smushAttachmentDetails = smushMedia.view.Attachment.Details;
/**
* Add Smush details to attachment.
*/
smushMedia.view.Attachment.Details = smushAttachmentDetails.extend({
initialize() {
smushAttachmentDetails.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'change:smush', this.render);
},
render() {
// Ensure that the main attachment fields are rendered.
smushMedia.view.Attachment.prototype.render.apply(this, arguments);
const smushHTML = this.model.get('smush');
if (typeof smushHTML === 'undefined') {
return this;
}
this.model.fetch();
/**
* Detach the views, append our custom fields, make sure that our data is fully updated
* and re-render the updated view.
*/
this.views.detach();
this.$el.append(prepareTemplate(smushHTML));
return this;
},
});
/**
* Create a new MediaLibraryTaxonomyFilter we later will instantiate
*
* @since 3.0
*/
const MediaLibraryTaxonomyFilter = wp.media.view.AttachmentFilters.extend({
id: 'media-attachment-smush-filter',
createFilters() {
this.filters = {
all: {
text: smush_vars.strings.filter_all,
props: { stats: 'all' },
priority: 10,
},
unsmushed: {
text: smush_vars.strings.filter_not_processed,
props: { stats: 'unsmushed' },
priority: 20,
},
excluded: {
text: smush_vars.strings.filter_excl,
props: { stats: 'excluded' },
priority: 30,
},
failed: {
text: smush_vars.strings.filter_failed,
props: { stats: 'failed_processing' },
priority: 40,
},
};
},
});
/**
* Extend and override wp.media.view.AttachmentsBrowser to include our new filter.
*
* @since 3.0
*/
const AttachmentsBrowser = wp.media.view.AttachmentsBrowser;
wp.media.view.AttachmentsBrowser = wp.media.view.AttachmentsBrowser.extend({
createToolbar() {
// Make sure to load the original toolbar
AttachmentsBrowser.prototype.createToolbar.call(this);
this.toolbar.set(
'MediaLibraryTaxonomyFilter',
new MediaLibraryTaxonomyFilter({
controller: this.controller,
model: this.collection.props,
priority: -75,
}).render()
);
},
});
})(jQuery, _);

View File

@ -0,0 +1,45 @@
import MixPanel from "../mixpanel";
class ProductAnalytics {
init() {
this.trackUltraLinks();
}
trackUltraLinks() {
const ultraUpsellLinks = document.querySelectorAll( '.wp-smush-upsell-ultra-compression' );
if ( ! ultraUpsellLinks ) {
return;
}
const getLocation = ( ultraLink ) => {
const locations = {
'settings': 'bulksmush_settings',
'dashboard': 'dash_summary',
'bulk': 'bulksmush_summary',
'directory': 'directory_summary',
'lazy-load': 'lazy_summary',
'cdn': 'cdn_summary',
'webp': 'webp_summary',
};
const locationId = ultraLink.classList.contains( 'wp-smush-ultra-compression-link' ) ? 'settings' : this.getCurrentPageSlug();
return locations[locationId] || 'bulksmush_settings';
}
ultraUpsellLinks.forEach( ( ultraLink ) => {
const eventName = 'ultra_upsell_modal';
ultraLink.addEventListener( 'click', (e) => {
MixPanel.getInstance().track( eventName, {
'Location': getLocation( e.target ),
'Modal Action': 'direct_cta',
});
});
});
}
getCurrentPageSlug(){
const searchParams = new URLSearchParams(document.location.search);
const pageSlug = searchParams.get("page");
return 'smush' === pageSlug ? 'dashboard' : pageSlug.replace( 'smush-', '' );
}
}
( new ProductAnalytics() ).init();

View File

@ -0,0 +1,173 @@
/* global ajaxurl */
/* global wp_smush_msgs */
/* global WP_Smush */
/* global SUI */
( function( $ ) {
'use strict';
/**
* Bulk compress page.
*/
$( 'form#smush-bulk-form' ).on( 'submit', function( e ) {
e.preventDefault();
$( '#save-settings-button' ).addClass( 'sui-button-onload' );
saveSettings( $( this ).serialize(), 'bulk' );
// runReCheck();
} );
/**
* Lazy load page.
*/
$( 'form#smush-lazy-load-form' ).on( 'submit', function( e ) {
e.preventDefault();
$( '#save-settings-button' ).addClass( 'sui-button-onload-text' );
saveSettings( $( this ).serialize(), 'lazy-load' );
} );
/**
* CDN page.
*/
$( 'form#smush-cdn-form' ).on( 'submit', function( e ) {
e.preventDefault();
$( '#save-settings-button' ).addClass( 'sui-button-onload-text' );
saveSettings( $( this ).serialize(), 'cdn' );
} );
/**
* Integrations page.
*/
$( 'form#smush-integrations-form' ).on( 'submit', function( e ) {
e.preventDefault();
$( '#save-settings-button' ).addClass( 'sui-button-onload-text' );
saveSettings( $( this ).serialize(), 'integrations' );
} );
/**
* Settings page.
*/
$( 'form#smush-settings-form' ).on( 'submit', function( e ) {
e.preventDefault();
$( '#save-settings-button' ).addClass( 'sui-button-onload-text' );
saveSettings( $( this ).serialize(), 'settings' );
} );
/**
* Save settings.
*
* @param {string} settings JSON string of settings.
* @param {string} page Settings page.
*/
function saveSettings( settings, page ) {
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=smush_save_settings', true );
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
$( '#save-settings-button' ).removeClass(
'sui-button-onload-text sui-button-onload'
);
if ( 200 === xhr.status ) {
const res = JSON.parse( xhr.response );
if ( 'undefined' !== typeof res.success && res.success ) {
showSuccessNotice( wp_smush_msgs.settingsUpdated );
triggerSavedSmushSettingsEvent( res.data );
} else if ( res.data && res.data.message ) {
WP_Smush.helpers.showErrorNotice( res.data.message );
} else {
WP_Smush.helpers.showErrorNotice( 'Request failed.' );
}
} else {
WP_Smush.helpers.showErrorNotice( 'Request failed. Returned status of ' + xhr.status );
}
};
xhr.send( 'page=' + page + '&' + settings + '&_ajax_nonce=' + wp_smush_msgs.nonce );
}
function triggerSavedSmushSettingsEvent( status ) {
document.dispatchEvent(
new CustomEvent( 'onSavedSmushSettings', {
detail: status
} )
);
}
/**
* Show successful update notice.
*
* @param {string} msg Notice message.
*/
function showSuccessNotice( msg ) {
const noticeMessage = `<p>${ msg }</p>`,
noticeOptions = {
type: 'success',
icon: 'check',
};
SUI.openNotice( 'wp-smush-ajax-notice', noticeMessage, noticeOptions );
const loadingButton = document.querySelector( '.sui-button-onload' );
if ( loadingButton ) {
loadingButton.classList.remove( 'sui-button-onload' );
}
}
/**
* Re-check images from bulk smush and integrations pages.
*/
function runReCheck() {
$( '#save-settings-button' ).addClass( 'sui-button-onload' );
const param = {
action: 'scan_for_resmush',
wp_smush_options_nonce: $( '#wp_smush_options_nonce' ).val(),
type: 'media',
};
// Send ajax, Update Settings, And Check For resmush.
$.post( ajaxurl, $.param( param ) ).done( function() {
$( '#save-settings-button' ).removeClass( 'sui-button-onload' );
} );
}
/**
* Parse remove data change.
*/
$( 'input[name=keep_data]' ).on( 'change', function( e ) {
const otherClass =
'keep_data-true' === e.target.id
? 'keep_data-false'
: 'keep_data-true';
e.target.parentNode.classList.add( 'active' );
document
.getElementById( otherClass )
.parentNode.classList.remove( 'active' );
} );
/**
* Handle auto-detect checkbox toggle, to show/hide highlighting notice.
*/
$( 'input#detection' ).on( 'click', function() {
const noticeDiv = $( '.smush-highlighting-notice' );
const warningDiv = $( '.smush-highlighting-warning' );
// Setting enabled.
if ( $( this ).is( ':checked' ) ) {
// Highlighting is already active and setting not saved.
if ( noticeDiv.length > 0 ) {
noticeDiv.show();
} else {
warningDiv.show();
}
} else {
noticeDiv.hide();
warningDiv.hide();
}
} );
}( jQuery ) );

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,311 @@
/* global WP_Smush */
/* global ajaxurl */
/**
* WebP functionality.
*
* @since 3.8.0
*/
(function () {
'use strict';
WP_Smush.WebP = {
nonceField: document.getElementsByName('wp_smush_options_nonce'),
toggleModuleButton: document.getElementById('smush-toggle-webp-button'),
recheckStatusButton: document.getElementById('smush-webp-recheck'),
recheckStatusLink: document.getElementById('smush-webp-recheck-link'),
showWizardButton: document.getElementById('smush-webp-toggle-wizard'),
init() {
this.maybeShowDeleteAllSuccessNotice();
/**
* Handles the "Deactivate" and "Get Started" buttons on the WebP page.
*/
if (this.toggleModuleButton) {
this.toggleModuleButton.addEventListener('click', (e) =>
this.toggleWebp(e)
);
}
/**
* Handle "RE-CHECK STATUS' button click on WebP page.
*/
if (this.recheckStatusButton) {
this.recheckStatusButton.addEventListener('click', (e) => {
e.preventDefault();
this.recheckStatus();
});
}
/**
* Handle "RE-CHECK STATUS' link click on WebP page.
*/
if (this.recheckStatusLink) {
this.recheckStatusLink.addEventListener('click', (e) => {
e.preventDefault();
this.recheckStatus();
});
}
/**
* Handles the "Delete WebP images" button.
*/
if (document.getElementById('wp-smush-webp-delete-all')) {
document
.getElementById('wp-smush-webp-delete-all')
.addEventListener('click', (e) => this.deleteAll(e));
}
if (this.showWizardButton) {
this.showWizardButton.addEventListener(
'click',
this.toggleWizard
);
}
},
/**
* Toggle WebP module.
*
* @param {Event} e
*/
toggleWebp(e) {
e.preventDefault();
const button = e.currentTarget,
doEnable = 'enable' === button.dataset.action;
button.classList.add('sui-button-onload');
const xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl + '?action=smush_webp_toggle', true);
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
const res = JSON.parse(xhr.response);
if (200 === xhr.status) {
if ('undefined' !== typeof res.success && res.success) {
const scanPromise = this.runScan();
scanPromise.onload = () => {
window.location.href =
window.wp_smush_msgs.localWebpURL;
};
} else if ('undefined' !== typeof res.data.message) {
this.showNotice(res.data.message);
button.classList.remove('sui-button-onload');
}
} else {
let message = window.wp_smush_msgs.generic_ajax_error;
if (res && 'undefined' !== typeof res.data.message) {
message = res.data.message;
}
this.showNotice(message);
button.classList.remove('sui-button-onload');
}
};
xhr.send(
'param=' + doEnable + '&_ajax_nonce=' + this.nonceField[0].value
);
},
/**
* re-check server configuration for WebP.
*/
recheckStatus() {
this.recheckStatusButton.classList.add('sui-button-onload');
const xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl + '?action=smush_webp_get_status', true);
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
this.recheckStatusButton.classList.remove('sui-button-onload');
let message = false;
const res = JSON.parse(xhr.response);
if (200 === xhr.status) {
const isConfigured = res.success ? '1' : '0';
if (
isConfigured !==
this.recheckStatusButton.dataset.isConfigured
) {
// Reload the page when the configuration status changed.
location.reload();
}
} else {
message = window.wp_smush_msgs.generic_ajax_error;
}
if (res && res.data) {
message = res.data;
}
if (message) {
this.showNotice(message);
}
};
xhr.send('_ajax_nonce=' + window.wp_smush_msgs.webp_nonce);
},
deleteAll(e) {
const button = e.currentTarget;
button.classList.add('sui-button-onload');
let message = false;
const xhr = new XMLHttpRequest();
xhr.open('POST', ajaxurl + '?action=smush_webp_delete_all', true);
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.onload = () => {
const res = JSON.parse(xhr.response);
if (200 === xhr.status) {
if ('undefined' !== typeof res.success && res.success) {
const scanPromise = this.runScan();
scanPromise.onload = () => {
location.search =
location.search + '&notice=webp-deleted';
};
} else {
message = window.wp_smush_msgs.generic_ajax_error;
}
} else {
message = window.wp_smush_msgs.generic_ajax_error;
}
if (res && res.data && res.data.message) {
message = res.data.message;
}
if (message) {
button.classList.remove('sui-button-onload');
const noticeMessage = `<p style="text-align: left;">${message}</p>`;
const noticeOptions = {
type: 'error',
icon: 'info',
autoclose: {
show: false,
},
};
window.SUI.openNotice(
'wp-smush-webp-delete-all-error-notice',
noticeMessage,
noticeOptions
);
}
};
xhr.send('_ajax_nonce=' + this.nonceField[0].value);
},
toggleWizard(e) {
e.currentTarget.classList.add('sui-button-onload');
const xhr = new XMLHttpRequest();
xhr.open(
'GET',
ajaxurl +
'?action=smush_toggle_webp_wizard&_ajax_nonce=' +
window.wp_smush_msgs.webp_nonce,
true
);
xhr.onload = () => location.reload();
xhr.send();
},
/**
* Triggers the scanning of images for updating the images to re-smush.
*
* @since 3.8.0
*/
runScan() {
const xhr = new XMLHttpRequest(),
nonceField = document.getElementsByName(
'wp_smush_options_nonce'
);
xhr.open('POST', ajaxurl + '?action=scan_for_resmush', true);
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.send('_ajax_nonce=' + nonceField[0].value);
return xhr;
},
/**
* Show message (notice).
*
* @param {string} message
* @param {string} type
*/
showNotice(message, type) {
if ('undefined' === typeof message) {
return;
}
const noticeMessage = `<p>${message}</p>`;
const noticeOptions = {
type: type || 'error',
icon: 'info',
dismiss: {
show: true,
label: window.wp_smush_msgs.noticeDismiss,
tooltip: window.wp_smush_msgs.noticeDismissTooltip,
},
autoclose: {
show: false,
},
};
window.SUI.openNotice(
'wp-smush-ajax-notice',
noticeMessage,
noticeOptions
);
},
/**
* Show delete all webp success notice.
*/
maybeShowDeleteAllSuccessNotice() {
if (!document.getElementById('wp-smush-webp-delete-all-notice')) {
return;
}
const noticeMessage = `<p>${
document.getElementById('wp-smush-webp-delete-all-notice')
.dataset.message
}</p>`;
const noticeOptions = {
type: 'success',
icon: 'check-tick',
dismiss: {
show: true,
},
};
window.SUI.openNotice(
'wp-smush-webp-delete-all-notice',
noticeMessage,
noticeOptions
);
},
};
WP_Smush.WebP.init();
})();

View File

@ -0,0 +1,96 @@
/* global ajaxurl */
/**
* External dependencies
*/
import React from 'react';
import ReactDOM from 'react-dom';
/**
* WordPress dependencies
*/
import domReady from '@wordpress/dom-ready';
/**
* SUI dependencies
*/
import { TutorialsList, TutorialsSlider } from '@wpmudev/shared-tutorials';
import MixPanel from "./mixpanel"
function hideTutorials() {
const xhr = new XMLHttpRequest();
xhr.open( 'POST', ajaxurl + '?action=smush_hide_tutorials', true );
xhr.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
xhr.onload = () => {
if ( 200 === xhr.status ) {
const noticeMessage = `<p>${ window.wp_smush_msgs.tutorialsRemoved }</p>`,
noticeOptions = {
type: 'success',
icon: 'check',
};
window.SUI.openNotice(
'wp-smush-ajax-notice',
noticeMessage,
noticeOptions
);
}
};
xhr.send( '_ajax_nonce=' + window.wp_smush_msgs.nonce );
}
/**
* Render the "Tutorials List" component.
*
* @since 2.8.5
*/
domReady( function() {
// Tutorials section on Dashboard page.
const tutorialsDiv = document.getElementById( 'smush-dash-tutorials' );
if ( tutorialsDiv ) {
ReactDOM.render(
<TutorialsSlider
category="11228"
title={ window.smush_tutorials.tutorials }
viewAll={ window.smush_tutorials.tutorials_link }
onCloseClick={ hideTutorials }
/>,
tutorialsDiv
);
}
// Tutorials page.
const tutorialsPageBox = document.getElementById( 'smush-box-tutorials' );
if ( tutorialsPageBox ) {
ReactDOM.render(
<TutorialsList
category="11228"
title={ window.smush_tutorials.tutorials }
translate={ window.smush_tutorials.tutorials_strings }
/>,
tutorialsPageBox
);
}
} );
jQuery(function ($) {
$(document).on('click', '#smush-box-tutorials li > [role="link"], #smush-dash-tutorials li > [role="link"]', function () {
const $tutorial = $(this);
const isDashPage = !!$tutorial.closest('#smush-dash-tutorials').length;
const decodeHtml = (html) => {
const txt = document.createElement("textarea");
txt.innerHTML = html;
return txt.value;
};
const title = decodeHtml($tutorial.attr('title'));
MixPanel.getInstance().track('Tutorial Opened', {
'Tutorial Name': title,
'Triggered From': isDashPage ? 'Dashboard' : 'Tutorials Tab'
});
});
});

View File

@ -0,0 +1,170 @@
/* global ajaxurl */
/**
* External dependencies
*/
import assign from 'lodash/assign';
/**
* Wrapper function for ajax calls to WordPress.
*
* @since 3.12.0
*/
function SmushFetcher() {
/**
* Request ajax with a promise.
* Use FormData Object as data if you need to upload file
*
* @param {string} action
* @param {Object|FormData} data
* @param {string} method
* @return {Promise<any>} Request results.
*/
function request(action, data = {}, method = 'POST') {
const args = {
url: ajaxurl,
method,
cache: false
};
if (data instanceof FormData) {
data.append('action', action);
data.append('_ajax_nonce', window.wp_smush_msgs.nonce);
args.contentType = false;
args.processData = false;
} else {
data._ajax_nonce = data._ajax_nonce || window.wp_smush_msgs.nonce;
data.action = action;
}
args.data = data;
return new Promise((resolve, reject) => {
jQuery.ajax(args).done(resolve).fail(reject);
}).then((response) => {
if (typeof response !== 'object') {
response = JSON.parse(response);
}
return response;
}).catch((error) => {
console.error('Error:', error);
});
}
const methods = {
/**
* Manage ajax for background.
*/
background: {
/**
* Start background process.
*/
start: () => {
return request('bulk_smush_start');
},
/**
* Cancel background process.
*/
cancel: () => {
return request('bulk_smush_cancel');
},
/**
* Initial State - Get stats on the first time.
*/
initState: () => {
return request('bulk_smush_get_status');
},
/**
* Get stats.
*/
getStatus: () => {
return request('bulk_smush_get_status');
},
getStats: () => {
return request('bulk_smush_get_global_stats');
}
},
smush: {
/**
* Sync stats.
*/
syncStats: ( data ) => {
data = data || {};
return request('get_stats', data);
},
/**
* Ignore All.
*/
ignoreAll: ( type ) => {
return request('wp_smush_ignore_all_failed_items', {
type: type,
});
},
},
/**
* Manage ajax for other requests
*/
common: {
/**
* Dismiss Notice.
*
* @param {string} dismissId Notification id.
*/
dismissNotice: (dismissId) => {
return request('smush_dismiss_notice', {
key: dismissId
});
},
/**
* Hide the new features modal.
*
* @param {string} modalID Notification id.
*/
hideModal: (modalID) => request('hide_modal', {
modal_id: modalID,
}),
/**
* Custom request.
*
* @param {Object} data
*/
request: (data) => data.action && request(data.action, data),
},
scanMediaLibrary: {
start: ( optimize_on_scan_completed = false ) => {
optimize_on_scan_completed = optimize_on_scan_completed ? 1 : 0;
const _ajax_nonce = window.wp_smushit_data.media_library_scan.nonce;
return request( 'wp_smush_start_background_scan', {
optimize_on_scan_completed,
_ajax_nonce,
} );
},
cancel: () => {
const _ajax_nonce = window.wp_smushit_data.media_library_scan.nonce;
return request( 'wp_smush_cancel_background_scan', {
_ajax_nonce,
} );
},
getScanStatus: () => {
const _ajax_nonce = window.wp_smushit_data.media_library_scan.nonce;
return request( 'wp_smush_get_background_scan_status', {
_ajax_nonce,
} );
},
},
};
assign(this, methods);
}
const SmushAjax = new SmushFetcher();
export default SmushAjax;