EWWW Image Optimizer - Version 6.2.0

Version Description

  • added: PHP-based WebP Conversion via GD/Imagick in free mode when exec() is disabled
  • added: enable -sharp_yuv option for WebP conversion with the EIO_WEBP_SHARP_YUV override
  • added: WebP Conversion for CMYK images
  • added: webp-supported conditional class added to body tag when JS WebP is active
  • added: WP-CLI command can be run with --webp-only option
  • added: Lazy Load for iframes, add 'iframe' in exclusions to disable
  • added: compatibility with S3 Uploads 3.x
  • added: preserve metadata and apply lossless compression to linked versions of images via Easy IO with EIO_PRESERVE_LINKED_IMAGES constant
  • added: Easy IO rewrites URLs in existing picture elements
  • changed: JS WebP scripts moved to beginning of page footer
  • changed: native lazy loading is now enabled for right-sized PNG placeholders, override with EIO_DISABLE_NATIVE_LAZY constant
  • changed: add resume ability to Delete Originals tool
  • changed: move Easy IO check-in to wp_cron
  • fixed: empty .webp images sometimes produced when cwebp encounters an error
  • fixed: Bulk Optimizer for NextGEN loading incorrect script
  • fixed: Bulk Optimizer for NextGEN fails to verify nonce for selective optimization
  • fixed: Last Optimized times for Optimized Images table were incorrect
  • fixed: Add Missing Dimensions overwrites smaller width/height attribute if only one is set
  • fixed: replacing an existing attribute (like width) with a numeric value is broken
Download this release

Release Info

Developer nosilver4u
Plugin Icon 128x128 EWWW Image Optimizer
Version 6.2.0
Comparing to
See all releases

Code changes from version 6.1.9 to 6.2.0

aux-optimize.php CHANGED
@@ -205,7 +205,7 @@ function ewww_image_optimizer_aux_images_table() {
205
  }
206
  $output['table'] .= '</td>';
207
  $output['table'] .= "<td>$type</td>";
208
- $output['table'] .= "<td>$last_updated ({$optimized_image['updated']})</td>";
209
  $output['table'] .= "<td>$savings<br>$size_string<br>" .
210
  '<a class="ewww-remove-image" data-id="' . (int) $optimized_image['id'] . '">' . esc_html__( 'Remove from history', 'ewww-image-optimizer' ) . '</a>' .
211
  ( $optimized_image['backup'] ? '<br><a class="ewww-restore-image" data-id="' . (int) $optimized_image['id'] . '">' . esc_html__( 'Restore original', 'ewww-image-optimizer' ) . '</a>' : '' ) .
@@ -244,7 +244,6 @@ function ewww_image_optimizer_aux_images_table() {
244
  $output['table'] .= '</td>';
245
  $output['table'] .= "<td>$type</td>";
246
  $output['table'] .= "<td>$last_updated</td>";
247
- $output['table'] .= "<td>$last_updated ({$optimized_image['updated']})</td>";
248
  // Determine filepath for webp.
249
  $webpfile = $file . '.webp';
250
  $webp_size = ewww_image_optimizer_filesize( $webpfile );
@@ -298,7 +297,6 @@ function ewww_image_optimizer_aux_images_table() {
298
  $output['table'] .= '</td>';
299
  $output['table'] .= "<td>$type</td>";
300
  $output['table'] .= "<td>$last_updated</td>";
301
- $output['table'] .= "<td>$last_updated ({$optimized_image['updated']})</td>";
302
  $output['table'] .= "<td>$savings<br>$size_string<br>" .
303
  '<a class="ewww-remove-image" data-id="' . (int) $optimized_image['id'] . '">' . esc_html__( 'Remove from history', 'ewww-image-optimizer' ) . '</a>' .
304
  '</td>';
@@ -566,7 +564,7 @@ function ewww_image_optimizer_delete_webp() {
566
  }
567
  $s3_path = false;
568
  $s3_dir = false;
569
- if ( class_exists( 'S3_Uploads' ) ) {
570
  $s3_path = get_attached_file( $id );
571
  if ( 0 === strpos( $s3_path, 's3://' ) ) {
572
  ewwwio_debug_message( 'removing: ' . $s3_path . '.webp' );
@@ -662,6 +660,10 @@ function ewww_image_optimizer_ajax_delete_original() {
662
  ewwwio_ob_clean();
663
  die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) ) ) );
664
  }
 
 
 
 
665
  if ( empty( $_POST['attachment_id'] ) ) {
666
  die( wp_json_encode( array( 'error' => esc_html__( 'Missing attachment ID number.', 'ewww-image-optimizer' ) ) ) );
667
  }
@@ -675,6 +677,7 @@ function ewww_image_optimizer_ajax_delete_original() {
675
  if ( ewww_image_optimizer_iterable( $new_meta ) ) {
676
  wp_update_attachment_metadata( $id, $new_meta );
677
  }
 
678
  die( wp_json_encode( array( 'completed' => 1 ) ) );
679
  }
680
 
@@ -936,9 +939,18 @@ function ewww_image_optimizer_get_all_attachments() {
936
  ewwwio_ob_clean();
937
  die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) ) ) );
938
  }
 
939
  global $wpdb;
940
- $attachments = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE (post_type = 'attachment' OR post_type = 'ims_image') AND (post_mime_type LIKE '%%image%%' OR post_mime_type LIKE '%%pdf%%') ORDER BY ID DESC" );
 
 
 
 
 
 
 
941
  if ( empty( $attachments ) || ! is_countable( $attachments ) || 0 === count( $attachments ) ) {
 
942
  die( wp_json_encode( array( 'error' => esc_html__( 'No media uploads found.', 'ewww-image-optimizer' ) ) ) );
943
  }
944
  ewwwio_debug_message( gettype( $attachments ) );
@@ -1136,7 +1148,6 @@ function ewww_image_optimizer_image_scan( $dir, $started = 0 ) {
1136
  set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
1137
  $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
1138
  ewwwio_ob_clean();
1139
- ewww_image_optimizer_debug_log();
1140
  die(
1141
  wp_json_encode(
1142
  array(
@@ -1157,7 +1168,6 @@ function ewww_image_optimizer_image_scan( $dir, $started = 0 ) {
1157
  ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, array( '%s', '%d', '%d' ) );
1158
  }
1159
  set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
1160
- ewww_image_optimizer_debug_log();
1161
  global $ewwwio_scan_async;
1162
  $ewwwio_scan_async->data(
1163
  array(
@@ -1168,7 +1178,6 @@ function ewww_image_optimizer_image_scan( $dir, $started = 0 ) {
1168
  } elseif ( 'scheduled' === $ewww_scan && get_option( 'ewwwio_stop_scheduled_scan' ) ) {
1169
  ewwwio_debug_message( 'ending current scan iteration because of stop_scan' );
1170
  delete_option( 'ewwwio_stop_scheduled_scan' );
1171
- ewww_image_optimizer_debug_log();
1172
  die();
1173
  }
1174
  if ( $ewww_scan && 0 === $file_counter % 100 && ! ewwwio_check_memory_available( 2097000 ) ) {
@@ -1190,7 +1199,6 @@ function ewww_image_optimizer_image_scan( $dir, $started = 0 ) {
1190
  set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
1191
  $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
1192
  ewwwio_ob_clean();
1193
- ewww_image_optimizer_debug_log();
1194
  die(
1195
  wp_json_encode(
1196
  array(
@@ -1299,7 +1307,6 @@ function ewww_image_optimizer_image_scan( $dir, $started = 0 ) {
1299
  ewwwio_memory( __FUNCTION__ );
1300
  $folders_completed[] = $dir;
1301
  update_option( 'ewww_image_optimizer_aux_folders_completed', $folders_completed, false );
1302
- ewww_image_optimizer_debug_log();
1303
  }
1304
 
1305
  /**
@@ -1486,7 +1493,6 @@ function ewww_image_optimizer_aux_images_script( $hook = '' ) {
1486
  update_option( 'ewww_image_optimizer_aux_folders_completed', array(), false );
1487
  update_option( 'ewww_image_optimizer_aux_resume', '' );
1488
  update_option( 'ewww_image_optimizer_bulk_resume', '' );
1489
- ewww_image_optimizer_debug_log();
1490
  if ( wp_doing_ajax() && 'ewww-image-optimizer-auto' !== $hook && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) {
1491
  $verify_cloud = ewww_image_optimizer_cloud_verify( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ), false );
1492
  $usage = false;
@@ -1534,7 +1540,6 @@ function ewww_image_optimizer_aux_images_script( $hook = '' ) {
1534
  $ewwwio_image_background->dispatch();
1535
  update_option( 'ewww_image_optimizer_aux_resume', '', false );
1536
  }
1537
- ewww_image_optimizer_debug_log();
1538
  ewwwio_memory( __FUNCTION__ );
1539
  return $image_count;
1540
  }
@@ -1575,4 +1580,3 @@ add_action( 'wp_ajax_bulk_aux_images_webp_clean', 'ewww_image_optimizer_aux_imag
1575
  add_action( 'wp_ajax_bulk_aux_images_delete_webp', 'ewww_image_optimizer_delete_webp' );
1576
  add_action( 'wp_ajax_bulk_aux_images_delete_original', 'ewww_image_optimizer_ajax_delete_original' );
1577
  add_action( 'wp_ajax_ewwwio_get_all_attachments', 'ewww_image_optimizer_get_all_attachments' );
1578
- ?>
205
  }
206
  $output['table'] .= '</td>';
207
  $output['table'] .= "<td>$type</td>";
208
+ $output['table'] .= "<td>$last_updated</td>";
209
  $output['table'] .= "<td>$savings<br>$size_string<br>" .
210
  '<a class="ewww-remove-image" data-id="' . (int) $optimized_image['id'] . '">' . esc_html__( 'Remove from history', 'ewww-image-optimizer' ) . '</a>' .
211
  ( $optimized_image['backup'] ? '<br><a class="ewww-restore-image" data-id="' . (int) $optimized_image['id'] . '">' . esc_html__( 'Restore original', 'ewww-image-optimizer' ) . '</a>' : '' ) .
244
  $output['table'] .= '</td>';
245
  $output['table'] .= "<td>$type</td>";
246
  $output['table'] .= "<td>$last_updated</td>";
 
247
  // Determine filepath for webp.
248
  $webpfile = $file . '.webp';
249
  $webp_size = ewww_image_optimizer_filesize( $webpfile );
297
  $output['table'] .= '</td>';
298
  $output['table'] .= "<td>$type</td>";
299
  $output['table'] .= "<td>$last_updated</td>";
 
300
  $output['table'] .= "<td>$savings<br>$size_string<br>" .
301
  '<a class="ewww-remove-image" data-id="' . (int) $optimized_image['id'] . '">' . esc_html__( 'Remove from history', 'ewww-image-optimizer' ) . '</a>' .
302
  '</td>';
564
  }
565
  $s3_path = false;
566
  $s3_dir = false;
567
+ if ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) {
568
  $s3_path = get_attached_file( $id );
569
  if ( 0 === strpos( $s3_path, 's3://' ) ) {
570
  ewwwio_debug_message( 'removing: ' . $s3_path . '.webp' );
660
  ewwwio_ob_clean();
661
  die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) ) ) );
662
  }
663
+ if ( ! empty( $_POST['delete_originals_done'] ) ) {
664
+ delete_option( 'ewww_image_optimizer_delete_originals_resume' );
665
+ die( wp_json_encode( array( 'done' => 1 ) ) );
666
+ }
667
  if ( empty( $_POST['attachment_id'] ) ) {
668
  die( wp_json_encode( array( 'error' => esc_html__( 'Missing attachment ID number.', 'ewww-image-optimizer' ) ) ) );
669
  }
677
  if ( ewww_image_optimizer_iterable( $new_meta ) ) {
678
  wp_update_attachment_metadata( $id, $new_meta );
679
  }
680
+ update_option( 'ewww_image_optimizer_delete_originals_resume', $id, false );
681
  die( wp_json_encode( array( 'completed' => 1 ) ) );
682
  }
683
 
939
  ewwwio_ob_clean();
940
  die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) ) ) );
941
  }
942
+ $start_id = get_option( 'ewww_image_optimizer_delete_originals_resume', 0 );
943
  global $wpdb;
944
+ $attachments = $wpdb->get_col(
945
+ $wpdb->prepare(
946
+ "SELECT ID FROM $wpdb->posts WHERE ID > %d AND (post_type = 'attachment' OR post_type = 'ims_image') AND (post_mime_type LIKE %s OR post_mime_type LIKE %s) ORDER BY ID DESC",
947
+ (int) $start_id,
948
+ '%image%',
949
+ '%pdf%'
950
+ )
951
+ );
952
  if ( empty( $attachments ) || ! is_countable( $attachments ) || 0 === count( $attachments ) ) {
953
+ delete_option( 'ewww_image_optimizer_delete_originals_resume' );
954
  die( wp_json_encode( array( 'error' => esc_html__( 'No media uploads found.', 'ewww-image-optimizer' ) ) ) );
955
  }
956
  ewwwio_debug_message( gettype( $attachments ) );
1148
  set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
1149
  $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
1150
  ewwwio_ob_clean();
 
1151
  die(
1152
  wp_json_encode(
1153
  array(
1168
  ewww_image_optimizer_mass_insert( $wpdb->ewwwio_images, $images, array( '%s', '%d', '%d' ) );
1169
  }
1170
  set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
 
1171
  global $ewwwio_scan_async;
1172
  $ewwwio_scan_async->data(
1173
  array(
1178
  } elseif ( 'scheduled' === $ewww_scan && get_option( 'ewwwio_stop_scheduled_scan' ) ) {
1179
  ewwwio_debug_message( 'ending current scan iteration because of stop_scan' );
1180
  delete_option( 'ewwwio_stop_scheduled_scan' );
 
1181
  die();
1182
  }
1183
  if ( $ewww_scan && 0 === $file_counter % 100 && ! ewwwio_check_memory_available( 2097000 ) ) {
1199
  set_transient( 'ewww_image_optimizer_aux_iterator', $file_counter - 20, 300 ); // Keep track of where we left off, minus 20 to be safe.
1200
  $loading_image = plugins_url( '/images/wpspin.gif', __FILE__ );
1201
  ewwwio_ob_clean();
 
1202
  die(
1203
  wp_json_encode(
1204
  array(
1307
  ewwwio_memory( __FUNCTION__ );
1308
  $folders_completed[] = $dir;
1309
  update_option( 'ewww_image_optimizer_aux_folders_completed', $folders_completed, false );
 
1310
  }
1311
 
1312
  /**
1493
  update_option( 'ewww_image_optimizer_aux_folders_completed', array(), false );
1494
  update_option( 'ewww_image_optimizer_aux_resume', '' );
1495
  update_option( 'ewww_image_optimizer_bulk_resume', '' );
 
1496
  if ( wp_doing_ajax() && 'ewww-image-optimizer-auto' !== $hook && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) {
1497
  $verify_cloud = ewww_image_optimizer_cloud_verify( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ), false );
1498
  $usage = false;
1540
  $ewwwio_image_background->dispatch();
1541
  update_option( 'ewww_image_optimizer_aux_resume', '', false );
1542
  }
 
1543
  ewwwio_memory( __FUNCTION__ );
1544
  return $image_count;
1545
  }
1580
  add_action( 'wp_ajax_bulk_aux_images_delete_webp', 'ewww_image_optimizer_delete_webp' );
1581
  add_action( 'wp_ajax_bulk_aux_images_delete_original', 'ewww_image_optimizer_ajax_delete_original' );
1582
  add_action( 'wp_ajax_ewwwio_get_all_attachments', 'ewww_image_optimizer_get_all_attachments' );
 
bulk.php CHANGED
@@ -33,7 +33,7 @@ function ewww_image_optimizer_display_tools() {
33
  update_option( 'ewwwio_stop_scheduled_scan', true, false );
34
  }
35
  echo "<div class='wrap'>\n";
36
- echo "<h1>EWWW Image Optimizer</h1>\n";
37
 
38
  // Find out if the auxiliary image table has anything in it.
39
  $already_optimized = ewww_image_optimizer_aux_images_table_count();
@@ -125,7 +125,7 @@ function ewww_image_optimizer_display_tools() {
125
  $as3cf_remove = true;
126
  }
127
  }
128
- if ( ! class_exists( 'S3_Uploads' ) && ! function_exists( 'ud_get_stateless_media' ) && ! $as3cf_remove ) {
129
  echo '<hr class="ewww-tool-divider">';
130
  echo "<div>\n<p id='ewww-clean-table-info' class='ewww-tool-info'>" .
131
  esc_html__( 'Older sites may have duplicate records or references to deleted files. Use the cleanup tool to remove such records.', 'ewww-image-optimizer' ) . '<br>' .
@@ -967,13 +967,11 @@ function ewww_image_optimizer_media_scan( $hook = '' ) {
967
  $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
968
  if ( 'ewww-image-optimizer-cli' !== $hook && empty( $_REQUEST['ewww_scan'] ) ) {
969
  ewwwio_debug_message( 'bailing no cli' );
970
- ewww_image_optimizer_debug_log();
971
  ewwwio_ob_clean();
972
  die( wp_json_encode( array( 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ) ) ) );
973
  }
974
  if ( ! empty( $_REQUEST['ewww_scan'] ) && ( empty( $_REQUEST['ewww_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['ewww_wpnonce'] ), 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) ) {
975
  ewwwio_debug_message( 'bailing no nonce' );
976
- ewww_image_optimizer_debug_log();
977
  ewwwio_ob_clean();
978
  die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) ) ) );
979
  }
@@ -1187,6 +1185,7 @@ function ewww_image_optimizer_media_scan( $hook = '' ) {
1187
  class_exists( 'WindowsAzureStorageUtil' ) ||
1188
  class_exists( 'Amazon_S3_And_CloudFront' ) ||
1189
  class_exists( 'S3_Uploads' ) ||
 
1190
  class_exists( 'wpCloud\StatelessMedia\EWWW' )
1191
  )
1192
  ) {
@@ -1197,12 +1196,19 @@ function ewww_image_optimizer_media_scan( $hook = '' ) {
1197
  $s3_uploads = S3_Uploads::get_instance();
1198
  remove_filter( 'upload_dir', array( $s3_uploads, 'filter_upload_dir' ) );
1199
  }
 
 
 
 
1200
  if ( ewww_image_optimizer_stream_wrapped( $file_path ) || 0 === strpos( $file_path, 'http' ) ) {
1201
  $file_path = get_attached_file( $selected_id, true );
1202
  }
1203
  if ( class_exists( 'S3_Uploads' ) && method_exists( 'S3_Uploads', 'filter_upload_dir' ) ) {
1204
  add_filter( 'upload_dir', array( $s3_uploads, 'filter_upload_dir' ) );
1205
  }
 
 
 
1206
  ewwwio_debug_message( "remote file possible: $file_path" );
1207
  if ( ! $file_path ) {
1208
  ewwwio_debug_message( 'no file found on remote storage, bailing' );
@@ -1880,7 +1886,9 @@ function ewww_image_optimizer_bulk_loop( $hook = '', $delay = 0 ) {
1880
  $ewww_force_smart = false;
1881
  delete_transient( 'ewww_image_optimizer_smart_reopt' );
1882
  }
1883
- $ewww_webp_only = false;
 
 
1884
  if ( ! empty( $_REQUEST['ewww_webp_only'] ) ) {
1885
  $ewww_webp_only = true;
1886
  }
@@ -1893,7 +1901,7 @@ function ewww_image_optimizer_bulk_loop( $hook = '', $delay = 0 ) {
1893
  $output['new_nonce'] = '';
1894
  }
1895
  }
1896
- $batch_image_limit = ( empty( $_REQUEST['ewww_batch_limit'] ) && ! class_exists( 'S3_Uploads' ) ? 999 : 1 );
1897
  // Get the 'bulk attachments' with a list of IDs remaining.
1898
  $attachments = ewww_image_optimizer_get_queued_attachments( 'media', $batch_image_limit );
1899
  if ( ! empty( $attachments ) && is_array( $attachments ) ) {
@@ -2011,11 +2019,14 @@ function ewww_image_optimizer_bulk_loop( $hook = '', $delay = 0 ) {
2011
 
2012
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
2013
  WP_CLI::line( __( 'Optimized', 'ewww-image-optimizer' ) . ' ' . $image->file );
2014
- WP_CLI::line( str_replace( '&nbsp;', '', $msg ) );
2015
  }
2016
  $output['results'] .= sprintf( '<p>' . esc_html__( 'Optimized', 'ewww-image-optimizer' ) . ' <strong>%s</strong><br>', esc_html( $image->file ) );
2017
  if ( ! empty( $ewwwio_resize_status ) ) {
2018
  $output['results'] .= esc_html( $ewwwio_resize_status ) . '<br>';
 
 
 
2019
  }
2020
  $output['results'] .= "$msg</p>";
2021
 
@@ -2038,7 +2049,7 @@ function ewww_image_optimizer_bulk_loop( $hook = '', $delay = 0 ) {
2038
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
2039
  ewwwio_debug_message( 'saving attachment meta' );
2040
  $meta = wp_get_attachment_metadata( $image->attachment_id );
2041
- if ( class_exists( 'S3_Uploads' ) ) {
2042
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
2043
  ewww_image_optimizer_remote_push( $meta, $image->attachment_id );
2044
  }
@@ -2143,7 +2154,7 @@ function ewww_image_optimizer_bulk_update_meta() {
2143
  $attachment_id = (int) $_REQUEST['attachment_id'];
2144
  ewwwio_debug_message( "saving attachment meta for $attachment_id" );
2145
  $meta = wp_get_attachment_metadata( $attachment_id );
2146
- if ( class_exists( 'S3_Uploads' ) ) {
2147
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
2148
  ewww_image_optimizer_remote_push( $meta, $attachment_id );
2149
  }
33
  update_option( 'ewwwio_stop_scheduled_scan', true, false );
34
  }
35
  echo "<div class='wrap'>\n";
36
+ echo "<h1 id='ewwwio-tools-header'>EWWW Image Optimizer</h1>\n";
37
 
38
  // Find out if the auxiliary image table has anything in it.
39
  $already_optimized = ewww_image_optimizer_aux_images_table_count();
125
  $as3cf_remove = true;
126
  }
127
  }
128
+ if ( ! class_exists( 'S3_Uploads' ) && ! class_exists( 'S3_Uploads\Plugin' ) && ! function_exists( 'ud_get_stateless_media' ) && ! $as3cf_remove ) {
129
  echo '<hr class="ewww-tool-divider">';
130
  echo "<div>\n<p id='ewww-clean-table-info' class='ewww-tool-info'>" .
131
  esc_html__( 'Older sites may have duplicate records or references to deleted files. Use the cleanup tool to remove such records.', 'ewww-image-optimizer' ) . '<br>' .
967
  $permissions = apply_filters( 'ewww_image_optimizer_bulk_permissions', '' );
968
  if ( 'ewww-image-optimizer-cli' !== $hook && empty( $_REQUEST['ewww_scan'] ) ) {
969
  ewwwio_debug_message( 'bailing no cli' );
 
970
  ewwwio_ob_clean();
971
  die( wp_json_encode( array( 'error' => esc_html__( 'Access denied.', 'ewww-image-optimizer' ) ) ) );
972
  }
973
  if ( ! empty( $_REQUEST['ewww_scan'] ) && ( empty( $_REQUEST['ewww_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['ewww_wpnonce'] ), 'ewww-image-optimizer-bulk' ) || ! current_user_can( $permissions ) ) ) {
974
  ewwwio_debug_message( 'bailing no nonce' );
 
975
  ewwwio_ob_clean();
976
  die( wp_json_encode( array( 'error' => esc_html__( 'Access token has expired, please reload the page.', 'ewww-image-optimizer' ) ) ) );
977
  }
1185
  class_exists( 'WindowsAzureStorageUtil' ) ||
1186
  class_exists( 'Amazon_S3_And_CloudFront' ) ||
1187
  class_exists( 'S3_Uploads' ) ||
1188
+ class_exists( 'S3_Uploads\Plugin' ) ||
1189
  class_exists( 'wpCloud\StatelessMedia\EWWW' )
1190
  )
1191
  ) {
1196
  $s3_uploads = S3_Uploads::get_instance();
1197
  remove_filter( 'upload_dir', array( $s3_uploads, 'filter_upload_dir' ) );
1198
  }
1199
+ if ( class_exists( 'S3_Uploads\Plugin' ) && method_exists( 'S3_Uploads\Plugin', 'filter_upload_dir' ) ) {
1200
+ $s3_uploads = \S3_Uploads\Plugin::get_instance();
1201
+ remove_filter( 'upload_dir', array( $s3_uploads, 'filter_upload_dir' ) );
1202
+ }
1203
  if ( ewww_image_optimizer_stream_wrapped( $file_path ) || 0 === strpos( $file_path, 'http' ) ) {
1204
  $file_path = get_attached_file( $selected_id, true );
1205
  }
1206
  if ( class_exists( 'S3_Uploads' ) && method_exists( 'S3_Uploads', 'filter_upload_dir' ) ) {
1207
  add_filter( 'upload_dir', array( $s3_uploads, 'filter_upload_dir' ) );
1208
  }
1209
+ if ( class_exists( 'S3_Uploads\Plugin' ) && method_exists( 'S3_Uploads\Plugin', 'filter_upload_dir' ) ) {
1210
+ add_filter( 'upload_dir', array( $s3_uploads, 'filter_upload_dir' ) );
1211
+ }
1212
  ewwwio_debug_message( "remote file possible: $file_path" );
1213
  if ( ! $file_path ) {
1214
  ewwwio_debug_message( 'no file found on remote storage, bailing' );
1886
  $ewww_force_smart = false;
1887
  delete_transient( 'ewww_image_optimizer_smart_reopt' );
1888
  }
1889
+ if ( ! isset( $ewww_webp_only ) ) {
1890
+ $ewww_webp_only = false;
1891
+ }
1892
  if ( ! empty( $_REQUEST['ewww_webp_only'] ) ) {
1893
  $ewww_webp_only = true;
1894
  }
1901
  $output['new_nonce'] = '';
1902
  }
1903
  }
1904
+ $batch_image_limit = ( empty( $_REQUEST['ewww_batch_limit'] ) && ! class_exists( 'S3_Uploads' ) && ! class_exists( 'S3_Uploads\Plugin' ) ? 999 : 1 );
1905
  // Get the 'bulk attachments' with a list of IDs remaining.
1906
  $attachments = ewww_image_optimizer_get_queued_attachments( 'media', $batch_image_limit );
1907
  if ( ! empty( $attachments ) && is_array( $attachments ) ) {
2019
 
2020
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
2021
  WP_CLI::line( __( 'Optimized', 'ewww-image-optimizer' ) . ' ' . $image->file );
2022
+ WP_CLI::line( str_replace( array( '&nbsp;', '<br>' ), array( '', "\n" ), $msg ) );
2023
  }
2024
  $output['results'] .= sprintf( '<p>' . esc_html__( 'Optimized', 'ewww-image-optimizer' ) . ' <strong>%s</strong><br>', esc_html( $image->file ) );
2025
  if ( ! empty( $ewwwio_resize_status ) ) {
2026
  $output['results'] .= esc_html( $ewwwio_resize_status ) . '<br>';
2027
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
2028
+ WP_CLI::line( $ewwwio_resize_status );
2029
+ }
2030
  }
2031
  $output['results'] .= "$msg</p>";
2032
 
2049
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
2050
  ewwwio_debug_message( 'saving attachment meta' );
2051
  $meta = wp_get_attachment_metadata( $image->attachment_id );
2052
+ if ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) {
2053
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
2054
  ewww_image_optimizer_remote_push( $meta, $image->attachment_id );
2055
  }
2154
  $attachment_id = (int) $_REQUEST['attachment_id'];
2155
  ewwwio_debug_message( "saving attachment meta for $attachment_id" );
2156
  $meta = wp_get_attachment_metadata( $attachment_id );
2157
+ if ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) {
2158
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
2159
  ewww_image_optimizer_remote_push( $meta, $attachment_id );
2160
  }
changelog.txt CHANGED
@@ -1,3 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  = 6.1.9 =
2
  * fixed: Easy IO's Include All Resources compat with Oxygen Builder and Beaver Builder
3
  * fixed: regex to detect SVG images in use elements caused excessive backtracking
1
+ = 6.2.0 =
2
+ * added: PHP-based WebP Conversion via GD/Imagick in free mode when exec() is disabled
3
+ * added: enable -sharp_yuv option for WebP conversion with the EIO_WEBP_SHARP_YUV override
4
+ * added: WebP Conversion for CMYK images
5
+ * added: webp-supported conditional class added to body tag when JS WebP is active
6
+ * added: WP-CLI command can be run with --webp-only option
7
+ * added: Lazy Load for iframes, add 'iframe' in exclusions to disable
8
+ * added: compatibility with S3 Uploads 3.x
9
+ * added: preserve metadata and apply lossless compression to linked versions of images via Easy IO with EIO_PRESERVE_LINKED_IMAGES constant
10
+ * added: Easy IO rewrites URLs in existing picture elements
11
+ * changed: JS WebP scripts moved to beginning of page footer
12
+ * changed: native lazy loading is now enabled for right-sized PNG placeholders, override with EIO_DISABLE_NATIVE_LAZY constant
13
+ * changed: add resume ability to Delete Originals tool
14
+ * changed: move Easy IO check-in to wp_cron
15
+ * fixed: empty .webp images sometimes produced when cwebp encounters an error
16
+ * fixed: Bulk Optimizer for NextGEN loading incorrect script
17
+ * fixed: Bulk Optimizer for NextGEN fails to verify nonce for selective optimization
18
+ * fixed: Last Optimized times for Optimized Images table were incorrect
19
+ * fixed: Add Missing Dimensions overwrites smaller width/height attribute if only one is set
20
+ * fixed: replacing an existing attribute (like width) with a numeric value is broken
21
+
22
  = 6.1.9 =
23
  * fixed: Easy IO's Include All Resources compat with Oxygen Builder and Beaver Builder
24
  * fixed: regex to detect SVG images in use elements caused excessive backtracking
classes/class-eio-base.php CHANGED
@@ -703,6 +703,20 @@ if ( ! class_exists( 'EIO_Base' ) ) {
703
  $this->s3_active = $s3_domain;
704
  }
705
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
706
  if ( class_exists( 'wpCloud\StatelessMedia\EWWW' ) && function_exists( 'ud_get_stateless_media' ) ) {
707
  $sm = ud_get_stateless_media();
708
  if ( method_exists( $sm, 'get' ) && method_exists( $sm, 'get_gs_host' ) ) {
703
  $this->s3_active = $s3_domain;
704
  }
705
 
706
+ if (
707
+ class_exists( 'S3_Uploads\Plugin' ) &&
708
+ function_exists( 's3_uploads_enabled' ) && s3_uploads_enabled() &&
709
+ method_exists( 'S3_Uploads\Plugin', 'get_instance' ) && method_exists( 'S3_Uploads', 'get_s3_url\Plugin' )
710
+ ) {
711
+ $s3_uploads_instance = \S3_Uploads\Plugin::get_instance();
712
+ $s3_uploads_url = $s3_uploads_instance->get_s3_url();
713
+ $this->allowed_urls[] = $s3_uploads_url;
714
+ $this->debug_message( "found S3 URL from S3_Uploads: $s3_uploads_url" );
715
+ $s3_domain = $this->parse_url( $s3_uploads_url, PHP_URL_HOST );
716
+ $s3_scheme = $this->parse_url( $s3_uploads_url, PHP_URL_SCHEME );
717
+ $this->s3_active = $s3_domain;
718
+ }
719
+
720
  if ( class_exists( 'wpCloud\StatelessMedia\EWWW' ) && function_exists( 'ud_get_stateless_media' ) ) {
721
  $sm = ud_get_stateless_media();
722
  if ( method_exists( $sm, 'get' ) && method_exists( $sm, 'get_gs_host' ) ) {
classes/class-eio-js-webp.php ADDED
@@ -0,0 +1,959 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Implements WebP rewriting using page parsing and JS functionality.
4
+ *
5
+ * @link https://ewww.io
6
+ * @package EIO
7
+ */
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit;
11
+ }
12
+
13
+ /**
14
+ * Enables EWWW IO to filter the page content and replace img elements with WebP markup.
15
+ */
16
+ class EIO_JS_Webp extends EIO_Page_Parser {
17
+
18
+ /**
19
+ * A list of user-defined exclusions, populated by validate_user_exclusions().
20
+ *
21
+ * @access protected
22
+ * @var array $user_exclusions
23
+ */
24
+ protected $user_exclusions = array();
25
+
26
+ /**
27
+ * A list of user-defined (element-type) exclusions, populated by validate_user_exclusions().
28
+ *
29
+ * @access protected
30
+ * @var array $user_exclusions
31
+ */
32
+ protected $user_element_exclusions = array();
33
+
34
+ /**
35
+ * Base64-encoded placeholder image.
36
+ *
37
+ * @access protected
38
+ * @var string $placeholder_src
39
+ */
40
+ protected $placeholder_src = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
41
+
42
+ /**
43
+ * The 'check webp' script contents.
44
+ *
45
+ * @access private
46
+ * @var string $check_webp_script
47
+ */
48
+ private $check_webp_script = '';
49
+
50
+ /**
51
+ * The 'load webp' script contents.
52
+ *
53
+ * @access private
54
+ * @var string $load_webp_script
55
+ */
56
+ private $load_webp_script = '';
57
+
58
+ /**
59
+ * Register (once) actions and filters for JS WebP.
60
+ */
61
+ function __construct() {
62
+ global $eio_js_webp;
63
+ if ( is_object( $eio_js_webp ) ) {
64
+ return 'you are doing it wrong';
65
+ }
66
+ if ( ewww_image_optimizer_ce_webp_enabled() ) {
67
+ return false;
68
+ }
69
+ parent::__construct();
70
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
71
+
72
+ // Hook into the output buffer callback function.
73
+ add_filter( 'ewww_image_optimizer_filter_page_output', array( $this, 'filter_page_output' ), 20 );
74
+ // Filter for NextGEN image urls within JSON.
75
+ add_filter( 'ngg_pro_lightbox_images_queue', array( $this, 'ngg_pro_lightbox_images_queue' ), 11 );
76
+ // Filter for WooCommerce product variations JSON.
77
+ add_filter( 'woocommerce_pre_json_available_variations', array( $this, 'woocommerce_pre_json_available_variations' ) );
78
+
79
+ // Load up the minified check script.
80
+ $this->check_webp_script = file_get_contents( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'includes/check-webp.min.js' );
81
+ // Load up the minified script so we can inline it.
82
+ $this->load_webp_script = file_get_contents( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'includes/load-webp.min.js' );
83
+
84
+ $allowed_urls = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_paths' );
85
+ if ( $this->is_iterable( $allowed_urls ) ) {
86
+ $this->allowed_urls = array_merge( $this->allowed_urls, $allowed_urls );
87
+ }
88
+
89
+ $this->get_allowed_domains();
90
+
91
+ $this->allowed_urls = apply_filters( 'webp_allowed_urls', $this->allowed_urls );
92
+ $this->allowed_domains = apply_filters( 'webp_allowed_domains', $this->allowed_domains );
93
+ $this->debug_message( 'checking any images matching these URLs/patterns for webp: ' . implode( ',', $this->allowed_urls ) );
94
+ $this->debug_message( 'rewriting any images matching these domains to webp: ' . implode( ',', $this->allowed_domains ) );
95
+
96
+ // Load the appropriate JS, in the footer, but as early as possible.
97
+ if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
98
+ // Load the non-minified, non-inline version of the webp rewrite script.
99
+ add_action( 'wp_enqueue_scripts', array( $this, 'debug_script' ), -99 );
100
+ } elseif ( defined( 'EWWW_IMAGE_OPTIMIZER_WEBP_EXTERNAL_SCRIPT' ) && EWWW_IMAGE_OPTIMIZER_WEBP_EXTERNAL_SCRIPT ) {
101
+ // Load the minified, non-inline version of the webp rewrite script.
102
+ add_action( 'wp_enqueue_scripts', array( $this, 'min_external_script' ), -99 );
103
+ } else {
104
+ add_action( 'wp_head', array( $this, 'inline_check_script' ), -99 );
105
+ add_action( 'wp_footer', array( $this, 'inline_load_script' ), -99 );
106
+ }
107
+ $this->validate_user_exclusions();
108
+ }
109
+
110
+ /**
111
+ * Grant read-only access to allowed WebP domains.
112
+ *
113
+ * @return array A list of WebP domains.
114
+ */
115
+ function get_webp_domains() {
116
+ return $this->allowed_domains;
117
+ }
118
+
119
+ /**
120
+ * Starts an output buffer and registers the callback function to do WebP replacement.
121
+ */
122
+ function buffer_start() {
123
+ ob_start( array( $this, 'filter_page_output' ) );
124
+ }
125
+
126
+ /**
127
+ * Replaces images within a srcset attribute with their .webp derivatives.
128
+ *
129
+ * @param string $srcset A valid srcset attribute from an img element.
130
+ * @return bool|string False if no changes were made, or the new srcset if any WebP images replaced the originals.
131
+ */
132
+ function srcset_replace( $srcset ) {
133
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
134
+ $srcset_urls = explode( ' ', $srcset );
135
+ $found_webp = false;
136
+ if ( $this->is_iterable( $srcset_urls ) && count( $srcset_urls ) > 1 ) {
137
+ $this->debug_message( 'parsing srcset urls' );
138
+ foreach ( $srcset_urls as $srcurl ) {
139
+ if ( is_numeric( substr( $srcurl, 0, 1 ) ) ) {
140
+ continue;
141
+ }
142
+ $trailing = ' ';
143
+ if ( ',' === substr( $srcurl, -1 ) ) {
144
+ $trailing = ',';
145
+ $srcurl = rtrim( $srcurl, ',' );
146
+ }
147
+ $this->debug_message( "looking for $srcurl from srcset" );
148
+ if ( $this->validate_image_url( $srcurl ) ) {
149
+ $srcset = str_replace( $srcurl . $trailing, $this->generate_url( $srcurl ) . $trailing, $srcset );
150
+ $this->debug_message( "replaced $srcurl in srcset" );
151
+ $found_webp = true;
152
+ }
153
+ }
154
+ } elseif ( $this->validate_image_url( $srcset ) ) {
155
+ return $this->generate_url( $srcset );
156
+ }
157
+ if ( $found_webp ) {
158
+ return $srcset;
159
+ } else {
160
+ return false;
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Replaces images within the Jetpack data attributes with their .webp derivatives.
166
+ *
167
+ * @param string $image The full text of the img element.
168
+ * @return string The modified noscript tag.
169
+ */
170
+ function jetpack_replace( $image ) {
171
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
172
+ $data_orig_file = $this->get_attribute( $image, 'data-orig-file' );
173
+ if ( $data_orig_file ) {
174
+ $this->debug_message( "looking for data-orig-file: $data_orig_file" );
175
+ if ( $this->validate_image_url( $data_orig_file ) ) {
176
+ $this->set_attribute( $image, 'data-webp-orig-file', $this->generate_url( $data_orig_file ), true );
177
+ $this->debug_message( "replacing $data_orig_file via data-webp-orig-file" );
178
+ }
179
+ }
180
+ $data_medium_file = $this->get_attribute( $image, 'data-medium-file' );
181
+ if ( $data_medium_file ) {
182
+ $this->debug_message( "looking for data-medium-file: $data_medium_file" );
183
+ if ( $this->validate_image_url( $data_medium_file ) ) {
184
+ $this->set_attribute( $image, 'data-webp-medium-file', $this->generate_url( $data_medium_file ), true );
185
+ $this->debug_message( "replacing $data_medium_file via data-webp-medium-file" );
186
+ }
187
+ }
188
+ $data_large_file = $this->get_attribute( $image, 'data-large-file' );
189
+ if ( $data_large_file ) {
190
+ $this->debug_message( "looking for data-large-file: $data_large_file" );
191
+ if ( $this->validate_image_url( $data_large_file ) ) {
192
+ $this->set_attribute( $image, 'data-webp-large-file', $this->generate_url( $data_large_file ), true );
193
+ $this->debug_message( "replacing $data_large_file via data-webp-large-file" );
194
+ }
195
+ }
196
+ return $image;
197
+ }
198
+
199
+ /**
200
+ * Replaces images with the WooCommerce data attributes with their .webp derivatives.
201
+ *
202
+ * @param string $image The full text of the img element.
203
+ * @return string The modified noscript tag.
204
+ */
205
+ function woocommerce_replace( $image ) {
206
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
207
+ $data_large_image = $this->get_attribute( $image, 'data-large_image' );
208
+ if ( $data_large_image ) {
209
+ $this->debug_message( "looking for data-large_image: $data_large_image" );
210
+ if ( $this->validate_image_url( $data_large_image ) ) {
211
+ $this->set_attribute( $image, 'data-webp-large_image', $this->generate_url( $data_large_image ), true );
212
+ $this->debug_message( "replacing $data_large_image via data-webp-large_image" );
213
+ }
214
+ }
215
+ $data_src = $this->get_attribute( $image, 'data-src' );
216
+ if ( $data_src ) {
217
+ $this->debug_message( "looking for data-src: $data_src" );
218
+ if ( $this->validate_image_url( $data_src ) ) {
219
+ $this->set_attribute( $image, 'data-webp-src', $this->generate_url( $data_src ), true );
220
+ ewwwio_debug_message( "replacing $data_src via data-webp-src" );
221
+ }
222
+ }
223
+ return $image;
224
+ }
225
+
226
+ /**
227
+ * Search for img elements and rewrite them with noscript elements for WebP replacement.
228
+ *
229
+ * Any img elements or elements that may be used in place of img elements by JS are checked to see
230
+ * if WebP derivatives exist. The element is then wrapped within a noscript element for fallback,
231
+ * and noscript element receives a copy of the attributes from the img along with webp replacement
232
+ * values for those attributes.
233
+ *
234
+ * @param string $buffer The full HTML page generated since the output buffer was started.
235
+ * @return string The altered buffer containing the full page with WebP images inserted.
236
+ */
237
+ function filter_page_output( $buffer ) {
238
+ ewwwio_debug_message( '<b>' . __METHOD__ . '()</b>' );
239
+ // If any of this is true, don't filter the page.
240
+ $uri = add_query_arg( null, null );
241
+ $this->debug_message( "request uri is $uri" );
242
+ if (
243
+ empty( $buffer ) ||
244
+ is_admin() ||
245
+ strpos( $uri, 'cornerstone=' ) !== false ||
246
+ strpos( $uri, 'cornerstone-endpoint' ) !== false ||
247
+ did_action( 'cornerstone_boot_app' ) || did_action( 'cs_before_preview_frame' ) ||
248
+ '/print/' === substr( $uri, -7 ) ||
249
+ strpos( $uri, 'elementor-preview=' ) !== false ||
250
+ strpos( $uri, 'et_fb=' ) !== false ||
251
+ strpos( $uri, 'tatsu=' ) !== false ||
252
+ ( ! empty( $_POST['action'] ) && 'tatsu_get_concepts' === sanitize_text_field( wp_unslash( $_POST['action'] ) ) ) || // phpcs:ignore WordPress.Security.NonceVerification
253
+ is_embed() ||
254
+ is_feed() ||
255
+ is_preview() ||
256
+ is_customize_preview() ||
257
+ ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
258
+ preg_match( '/^<\?xml/', $buffer ) ||
259
+ strpos( $buffer, 'amp-boilerplate' ) ||
260
+ $this->is_amp() ||
261
+ ewww_image_optimizer_ce_webp_enabled()
262
+ ) {
263
+ ewwwio_debug_message( 'JS WebP disabled' );
264
+ return $buffer;
265
+ }
266
+
267
+ $body_tags = $this->get_elements_from_html( $buffer, 'body' );
268
+ if ( $this->is_iterable( $body_tags ) && ! empty( $body_tags[0] ) && false !== strpos( $body_tags[0], '<body' ) ) {
269
+ $body_webp_script = '<script>if(ewww_webp_supported){document.body.classList.add("webp-support");}</script>';
270
+ // Add the WebP script right after the opening tag.
271
+ $buffer = str_replace( $body_tags[0], $body_tags[0] . "\n" . $body_webp_script, $buffer );
272
+ }
273
+ $images = $this->get_images_from_html( preg_replace( '/<(picture|noscript).*?\/\1>/s', '', $buffer ), false );
274
+ if ( ! empty( $images[0] ) && $this->is_iterable( $images[0] ) ) {
275
+ foreach ( $images[0] as $index => $image ) {
276
+ if ( false !== strpos( $image, 'ewww_webp' ) ) {
277
+ continue;
278
+ }
279
+ // Ignore 0-size Pinterest schema images.
280
+ if ( strpos( $image, 'data-pin-description=' ) && strpos( $image, 'width="0" height="0"' ) ) {
281
+ continue;
282
+ }
283
+ if ( ! $this->validate_tag( $image ) ) {
284
+ continue;
285
+ }
286
+ $file = $images['img_url'][ $index ];
287
+ ewwwio_debug_message( "parsing an image: $file" );
288
+ if ( strpos( $image, 'jetpack-lazy-image' ) && $this->validate_image_url( $file ) ) {
289
+ $new_image = $image;
290
+ $new_image = $this->jetpack_replace( $new_image );
291
+ $real_file = $this->get_attribute( $new_image, 'data-lazy-src' );
292
+ ewwwio_debug_message( 'checking webp for Jetpack Lazy Load data-lazy-src' );
293
+ if ( $real_file && $this->validate_image_url( $real_file ) ) {
294
+ ewwwio_debug_message( "found webp for Lazy Load: $real_file" );
295
+ $this->set_attribute( $new_image, 'data-lazy-src-webp', $this->generate_url( $real_file ) );
296
+ }
297
+ $srcset = $this->get_attribute( $new_image, 'data-lazy-srcset' );
298
+ if ( $srcset ) {
299
+ $srcset_webp = $this->srcset_replace( $srcset );
300
+ if ( $srcset_webp ) {
301
+ $this->set_attribute( $new_image, 'data-lazy-srcset-webp', $srcset_webp );
302
+ }
303
+ }
304
+ if ( $new_image !== $image ) {
305
+ $this->set_attribute( $new_image, 'class', $this->get_attribute( $new_image, 'class' ) . ' ewww_webp_lazy_load', true );
306
+ $buffer = str_replace( $image, $new_image, $buffer );
307
+ }
308
+ } elseif ( $this->validate_image_url( $file ) && false === strpos( $image, 'lazyload' ) ) {
309
+ // If a CDN path match was found, or .webp image existence is confirmed, and this is not a lazy-load 'dummy' image.
310
+ $this->debug_message( 'found a webp image or forced path' );
311
+ $new_image = $image;
312
+ $this->set_attribute( $new_image, 'data-src-img', $file );
313
+ $this->set_attribute( $new_image, 'data-src-webp', $this->generate_url( $file ) );
314
+ $srcset = $this->get_attribute( $image, 'srcset' );
315
+ if ( $srcset ) {
316
+ $srcset_webp = $this->srcset_replace( $srcset );
317
+ if ( $srcset_webp ) {
318
+ $this->set_attribute( $new_image, 'data-srcset-webp', $srcset_webp );
319
+ }
320
+ $this->set_attribute( $new_image, 'data-srcset-img', $srcset );
321
+ $this->remove_attribute( $new_image, 'srcset' );
322
+ }
323
+ if ( $this->get_attribute( $image, 'data-orig-file' ) && $this->get_attribute( $image, 'data-medium-file' ) && $this->get_attribute( $image, 'data-large-file' ) ) {
324
+ $new_image = $this->jetpack_replace( $new_image );
325
+ }
326
+ if ( $this->get_attribute( $image, 'data-large_image' ) && $this->get_attribute( $image, 'data-src' ) ) {
327
+ $new_image = $this->woocommerce_replace( $new_image );
328
+ }
329
+ $this->set_attribute( $new_image, 'src', $this->placeholder_src, true );
330
+ if ( $new_image !== $image ) {
331
+ $this->set_attribute( $new_image, 'data-eio', 'j', true );
332
+ $this->set_attribute( $new_image, 'class', $this->get_attribute( $new_image, 'class' ) . ' ewww_webp', true );
333
+ $this->debug_message( "going to swap\n$image\nwith\n$new_image" );
334
+ $noscript = '<noscript>' . $image . '</noscript>';
335
+ $buffer = str_replace( $image, $new_image . $noscript, $buffer );
336
+ }
337
+ } elseif ( ! empty( $file ) && strpos( $image, ' data-lazy-src=' ) ) {
338
+ // BJ Lazy Load & WP Rocket.
339
+ $new_image = $image;
340
+ $real_file = $this->get_attribute( $new_image, 'data-lazy-src' );
341
+ ewwwio_debug_message( "checking webp for Lazy Load data-lazy-src: $real_file" );
342
+ if ( $this->validate_image_url( $real_file ) ) {
343
+ ewwwio_debug_message( "found webp for Lazy Load: $real_file" );
344
+ $this->set_attribute( $new_image, 'data-lazy-src-webp', $this->generate_url( $real_file ) );
345
+ }
346
+ $srcset = $this->get_attribute( $new_image, 'data-lazy-srcset' );
347
+ if ( $srcset ) {
348
+ $srcset_webp = $this->srcset_replace( $srcset );
349
+ if ( $srcset_webp ) {
350
+ $this->set_attribute( $new_image, 'data-lazy-srcset-webp', $srcset_webp );
351
+ }
352
+ }
353
+ if ( $new_image !== $image ) {
354
+ $this->set_attribute( $new_image, 'class', $this->get_attribute( $new_image, 'class' ) . ' ewww_webp_lazy_load', true );
355
+ $buffer = str_replace( $image, $new_image, $buffer );
356
+ }
357
+ } elseif ( ! empty( $file ) && strpos( $image, ' data-src=' ) && ( strpos( $image, ' data-lazy-type="image' ) || strpos( $image, 'lazyload' ) ) ) {
358
+ // a3 or EWWW IO Lazy Load.
359
+ $new_image = $image;
360
+ $real_file = $this->get_attribute( $new_image, 'data-src' );
361
+ ewwwio_debug_message( "checking webp for Lazy Load data-src: $real_file" );
362
+ if ( $this->validate_image_url( $real_file ) ) {
363
+ ewwwio_debug_message( 'found webp for Lazy Load' );
364
+ $this->set_attribute( $new_image, 'data-src-webp', $this->generate_url( $real_file ) );
365
+ }
366
+ $srcset = $this->get_attribute( $new_image, 'data-srcset' );
367
+ if ( $srcset ) {
368
+ $srcset_webp = $this->srcset_replace( $srcset );
369
+ if ( $srcset_webp ) {
370
+ $this->set_attribute( $new_image, 'data-srcset-webp', $srcset_webp );
371
+ }
372
+ }
373
+ if ( $new_image !== $image ) {
374
+ $this->set_attribute( $new_image, 'class', $this->get_attribute( $new_image, 'class' ) . ' ewww_webp_lazy_load', true );
375
+ $buffer = str_replace( $image, $new_image, $buffer );
376
+ }
377
+ } elseif ( ! empty( $file ) && strpos( $image, 'data-lazysrc=' ) && strpos( $image, '/essential-grid' ) ) {
378
+ // Essential Grid.
379
+ $new_image = $image;
380
+ $real_file = $this->get_attribute( $new_image, 'data-lazysrc' );
381
+ ewwwio_debug_message( "checking webp for EG Lazy Load data-lazysrc: $real_file" );
382
+ if ( $this->validate_image_url( $real_file ) ) {
383
+ ewwwio_debug_message( "found webp for Lazy Load: $real_file" );
384
+ $this->set_attribute( $new_image, 'data-lazysrc-webp', $this->generate_url( $real_file ) );
385
+ }
386
+ if ( $new_image !== $image ) {
387
+ $this->set_attribute( $new_image, 'class', $this->get_attribute( $new_image, 'class' ) . ' ewww_webp_lazy_load', true );
388
+ $buffer = str_replace( $image, $new_image, $buffer );
389
+ }
390
+ }
391
+ // Rev Slider data-lazyload attribute on image elements.
392
+ if ( $this->get_attribute( $image, 'data-lazyload' ) ) {
393
+ $new_image = $image;
394
+ $lazyload = $this->get_attribute( $new_image, 'data-lazyload' );
395
+ if ( $lazyload ) {
396
+ if ( $this->validate_image_url( $lazyload ) ) {
397
+ $this->set_attribute( $new_image, 'data-webp-lazyload', $this->generate_url( $lazyload ) );
398
+ ewwwio_debug_message( "replacing with webp for data-lazyload: $lazyload" );
399
+ $buffer = str_replace( $image, $new_image, $buffer );
400
+ }
401
+ }
402
+ }
403
+ } // End foreach().
404
+ } // End if().
405
+ // Now we will look for any lazy images that don't have a src attribute (this search returns ALL img elements though).
406
+ $images = $this->get_images_from_html( preg_replace( '/<(picture|noscript).*?\/\1>/s', '', $buffer ), false, false );
407
+ if ( ! empty( $images[0] ) && $this->is_iterable( $images[0] ) ) {
408
+ ewwwio_debug_message( 'parsing images without requiring src' );
409
+ foreach ( $images[0] as $index => $image ) {
410
+ if ( $this->get_attribute( $image, 'src' ) ) {
411
+ continue;
412
+ }
413
+ if ( ! $this->validate_tag( $image ) ) {
414
+ continue;
415
+ }
416
+ ewwwio_debug_message( 'found img without src' );
417
+ if ( strpos( $image, 'data-src=' ) && strpos( $image, 'data-srcset=' ) && strpos( $image, 'lazyload' ) ) {
418
+ // EWWW IO Lazy Load.
419
+ $new_image = $image;
420
+ $real_file = $this->get_attribute( $new_image, 'data-src' );
421
+ ewwwio_debug_message( "checking webp for Lazy Load data-src: $real_file" );
422
+ if ( $this->validate_image_url( $real_file ) ) {
423
+ ewwwio_debug_message( 'found webp for Lazy Load' );
424
+ $this->set_attribute( $new_image, 'data-src-webp', $this->generate_url( $real_file ) );
425
+ }
426
+ $srcset = $this->get_attribute( $new_image, 'data-srcset' );
427
+ if ( $srcset ) {
428
+ $srcset_webp = $this->srcset_replace( $srcset );
429
+ if ( $srcset_webp ) {
430
+ $this->set_attribute( $new_image, 'data-srcset-webp', $srcset_webp );
431
+ }
432
+ }
433
+ if ( $new_image !== $image ) {
434
+ $this->set_attribute( $new_image, 'class', $this->get_attribute( $new_image, 'class' ) . ' ewww_webp_lazy_load', true );
435
+ $buffer = str_replace( $image, $new_image, $buffer );
436
+ }
437
+ }
438
+ } // End foreach().
439
+ } // End if().
440
+ // Look for images to parse WP Retina Lazy Load.
441
+ if ( class_exists( 'Meow_WR2X_Core' ) && strpos( $buffer, ' lazyload' ) ) {
442
+ $images = $this->get_elements_from_html( $buffer, 'img' );
443
+ if ( $this->is_iterable( $images ) ) {
444
+ foreach ( $images as $index => $image ) {
445
+ if ( ! $this->validate_tag( $image ) ) {
446
+ continue;
447
+ }
448
+ $file = $this->get_attribute( $image, 'src' );
449
+ if ( ( empty( $file ) || strpos( $image, 'R0lGODlhAQABAIAAAAAAAP' ) ) && strpos( $image, ' data-srcset=' ) && strpos( $this->get_attribute( $image, 'class' ), 'lazyload' ) ) {
450
+ $new_image = $image;
451
+ $srcset = $this->get_attribute( $new_image, 'data-srcset' );
452
+ ewwwio_debug_message( 'checking webp for Retina Lazy Load data-src' );
453
+ if ( $srcset ) {
454
+ $srcset_webp = $this->srcset_replace( $srcset );
455
+ if ( $srcset_webp ) {
456
+ $this->set_attribute( $new_image, 'data-srcset-webp', $srcset_webp );
457
+ }
458
+ }
459
+ if ( $new_image !== $image ) {
460
+ $this->set_attribute( $new_image, 'class', $this->get_attribute( $new_image, 'class' ) . ' ewww_webp_lazy_load', true );
461
+ $buffer = str_replace( $image, $new_image, $buffer );
462
+ }
463
+ }
464
+ }
465
+ }
466
+ }
467
+ // Images listed as picture/source elements.
468
+ $pictures = $this->get_picture_tags_from_html( $buffer );
469
+ if ( $this->is_iterable( $pictures ) ) {
470
+ foreach ( $pictures as $index => $picture ) {
471
+ if ( strpos( $picture, 'image/webp' ) ) {
472
+ continue;
473
+ }
474
+ if ( ! $this->validate_tag( $picture ) ) {
475
+ continue;
476
+ }
477
+ $sources = $this->get_elements_from_html( $picture, 'source' );
478
+ if ( $this->is_iterable( $sources ) ) {
479
+ foreach ( $sources as $source ) {
480
+ $this->debug_message( "parsing a picture source: $source" );
481
+ $srcset_attr_name = 'srcset';
482
+ if ( false !== strpos( $source, 'base64,R0lGOD' ) && false !== strpos( $source, 'data-srcset=' ) ) {
483
+ $srcset_attr_name = 'data-srcset';
484
+ } elseif ( ! $this->get_attribute( $source, $srcset_attr_name ) && false !== strpos( $source, 'data-srcset=' ) ) {
485
+ $srcset_attr_name = 'data-srcset';
486
+ }
487
+ $srcset = $this->get_attribute( $source, $srcset_attr_name );
488
+ if ( $srcset ) {
489
+ $srcset_webp = $this->srcset_replace( $srcset );
490
+ if ( $srcset_webp ) {
491
+ $source_webp = str_replace( $srcset, $srcset_webp, $source );
492
+ $this->set_attribute( $source_webp, 'type', 'image/webp' );
493
+ $picture = str_replace( $source, $source_webp . $source, $picture );
494
+ }
495
+ }
496
+ }
497
+ if ( $picture !== $pictures[ $index ] ) {
498
+ $this->debug_message( 'found webp for picture element' );
499
+ $buffer = str_replace( $pictures[ $index ], $picture, $buffer );
500
+ }
501
+ }
502
+ }
503
+ }
504
+ // NextGEN slides listed as 'a' elements and LL 'a' background images.
505
+ $links = $this->get_elements_from_html( $buffer, 'a' );
506
+ if ( $this->is_iterable( $links ) ) {
507
+ foreach ( $links as $index => $link ) {
508
+ ewwwio_debug_message( "parsing a link $link" );
509
+ if ( ! $this->validate_tag( $link ) ) {
510
+ continue;
511
+ }
512
+ $file = $this->get_attribute( $link, 'data-src' );
513
+ $thumb = $this->get_attribute( $link, 'data-thumbnail' );
514
+ if ( $file && $thumb ) {
515
+ ewwwio_debug_message( "checking webp for ngg data-src: $file" );
516
+ if ( $this->validate_image_url( $file ) ) {
517
+ $this->set_attribute( $link, 'data-webp', $this->generate_url( $file ) );
518
+ ewwwio_debug_message( "found webp for ngg data-src: $file" );
519
+ }
520
+ ewwwio_debug_message( "checking webp for ngg data-thumbnail: $thumb" );
521
+ if ( $this->validate_image_url( $thumb ) ) {
522
+ $this->set_attribute( $link, 'data-webp-thumbnail', $this->generate_url( $thumb ) );
523
+ ewwwio_debug_message( "found webp for ngg data-thumbnail: $thumb" );
524
+ }
525
+ }
526
+ $bg_image = $this->get_attribute( $link, 'data-bg' );
527
+ $link_class = $this->get_attribute( $link, 'class' );
528
+ if ( $link_class && $bg_image && false !== strpos( $link_class, 'lazyload' ) ) {
529
+ ewwwio_debug_message( "checking a/link for LL data-bg: $bg_image" );
530
+ if ( $this->validate_image_url( $bg_image ) ) {
531
+ $this->set_attribute( $link, 'data-bg-webp', $this->generate_url( $bg_image ) );
532
+ ewwwio_debug_message( 'found webp for LL data-bg' );
533
+ }
534
+ }
535
+ if ( $link !== $links[ $index ] ) {
536
+ $buffer = str_replace( $links[ $index ], $link, $buffer );
537
+ }
538
+ }
539
+ }
540
+ // Revolution Slider 'li' elements and LL li backgrounds.
541
+ $listitems = $this->get_elements_from_html( $buffer, 'li' );
542
+ if ( $this->is_iterable( $listitems ) ) {
543
+ foreach ( $listitems as $index => $listitem ) {
544
+ ewwwio_debug_message( 'parsing a listitem' );
545
+ if ( ! $this->validate_tag( $listitem ) ) {
546
+ continue;
547
+ }
548
+ if ( $this->get_attribute( $listitem, 'data-title' ) === 'Slide' && ( $this->get_attribute( $listitem, 'data-lazyload' ) || $this->get_attribute( $listitem, 'data-thumb' ) ) ) {
549
+ $thumb = $this->get_attribute( $listitem, 'data-thumb' );
550
+ ewwwio_debug_message( "checking webp for revslider data-thumb: $thumb" );
551
+ if ( $this->validate_image_url( $thumb ) ) {
552
+ $this->set_attribute( $listitem, 'data-webp-thumb', $this->generate_url( $thumb ) );
553
+ ewwwio_debug_message( "found webp for revslider data-thumb: $thumb" );
554
+ }
555
+ $param_num = 1;
556
+ while ( $param_num < 11 ) {
557
+ $parameter = $this->get_attribute( $listitem, 'data-param' . $param_num );
558
+ if ( $parameter ) {
559
+ ewwwio_debug_message( "checking webp for revslider data-param$param_num: $parameter" );
560
+ if ( strpos( $parameter, 'http' ) === 0 ) {
561
+ ewwwio_debug_message( "looking for $parameter" );
562
+ if ( $this->validate_image_url( $parameter ) ) {
563
+ $this->set_attribute( $listitem, 'data-webp-param' . $param_num, $this->generate_url( $parameter ) );
564
+ ewwwio_debug_message( "found webp for data-param$param_num: $parameter" );
565
+ }
566
+ }
567
+ }
568
+ $param_num++;
569
+ }
570
+ if ( $listitem !== $listitems[ $index ] ) {
571
+ $buffer = str_replace( $listitems[ $index ], $listitem, $buffer );
572
+ }
573
+ }
574
+ $bg_image = $this->get_attribute( $listitem, 'data-bg' );
575
+ $li_class = $this->get_attribute( $listitem, 'class' );
576
+ if ( $li_class && $bg_image && false !== strpos( $li_class, 'lazyload' ) ) {
577
+ ewwwio_debug_message( "checking div for LL data-bg: $bg_image" );
578
+ if ( $this->validate_image_url( $bg_image ) ) {
579
+ $this->set_attribute( $listitem, 'data-bg-webp', $this->generate_url( $bg_image ) );
580
+ ewwwio_debug_message( 'found webp for LL data-bg' );
581
+ $buffer = str_replace( $listitems[ $index ], $listitem, $buffer );
582
+ }
583
+ }
584
+ } // End foreach().
585
+ } // End if().
586
+ // WooCommerce thumbs listed as 'div' elements and LL div backgrounds.
587
+ $divs = $this->get_elements_from_html( $buffer, 'div' );
588
+ if ( $this->is_iterable( $divs ) ) {
589
+ foreach ( $divs as $index => $div ) {
590
+ ewwwio_debug_message( 'parsing a div' );
591
+ if ( ! $this->validate_tag( $div ) ) {
592
+ continue;
593
+ }
594
+ $thumb = $this->get_attribute( $div, 'data-thumb' );
595
+ $div_class = $this->get_attribute( $div, 'class' );
596
+ if ( $div_class && $thumb && strpos( $div_class, 'woocommerce-product-gallery__image' ) !== false ) {
597
+ ewwwio_debug_message( "checking webp for WC data-thumb: $thumb" );
598
+ if ( $this->validate_image_url( $thumb ) ) {
599
+ $this->set_attribute( $div, 'data-webp-thumb', $this->generate_url( $thumb ) );
600
+ ewwwio_debug_message( 'found webp for WC data-thumb' );
601
+ $buffer = str_replace( $divs[ $index ], $div, $buffer );
602
+ }
603
+ }
604
+ $bg_image = $this->get_attribute( $div, 'data-bg' );
605
+ if ( $div_class && $bg_image && false !== strpos( $div_class, 'lazyload' ) ) {
606
+ ewwwio_debug_message( "checking div for LL data-bg: $bg_image" );
607
+ if ( $this->validate_image_url( $bg_image ) ) {
608
+ $this->set_attribute( $div, 'data-bg-webp', $this->generate_url( $bg_image ) );
609
+ ewwwio_debug_message( 'found webp for LL data-bg' );
610
+ $buffer = str_replace( $divs[ $index ], $div, $buffer );
611
+ }
612
+ }
613
+ }
614
+ }
615
+ // Look for LL 'section' elements.
616
+ $sections = $this->get_elements_from_html( $buffer, 'section' );
617
+ if ( $this->is_iterable( $sections ) ) {
618
+ foreach ( $sections as $index => $section ) {
619
+ ewwwio_debug_message( 'parsing a section' );
620
+ if ( ! $this->validate_tag( $section ) ) {
621
+ continue;
622
+ }
623
+ $class = $this->get_attribute( $section, 'class' );
624
+ $bg_image = $this->get_attribute( $section, 'data-bg' );
625
+ if ( $class && $bg_image && false !== strpos( $class, 'lazyload' ) ) {
626
+ ewwwio_debug_message( "checking section for LL data-bg: $bg_image" );
627
+ if ( $this->validate_image_url( $bg_image ) ) {
628
+ $this->set_attribute( $section, 'data-bg-webp', $this->generate_url( $bg_image ) );
629
+ ewwwio_debug_message( 'found webp for LL data-bg' );
630
+ $buffer = str_replace( $sections[ $index ], $section, $buffer );
631
+ }
632
+ }
633
+ }
634
+ }
635
+ // Look for LL 'span' elements.
636
+ $spans = $this->get_elements_from_html( $buffer, 'span' );
637
+ if ( $this->is_iterable( $spans ) ) {
638
+ foreach ( $spans as $index => $span ) {
639
+ ewwwio_debug_message( 'parsing a span' );
640
+ if ( ! $this->validate_tag( $span ) ) {
641
+ continue;
642
+ }
643
+ $class = $this->get_attribute( $span, 'class' );
644
+ $bg_image = $this->get_attribute( $span, 'data-bg' );
645
+ if ( $class && $bg_image && false !== strpos( $class, 'lazyload' ) ) {
646
+ ewwwio_debug_message( "checking span for LL data-bg: $bg_image" );
647
+ if ( $this->validate_image_url( $bg_image ) ) {
648
+ $this->set_attribute( $span, 'data-bg-webp', $this->generate_url( $bg_image ) );
649
+ ewwwio_debug_message( 'found webp for LL data-bg' );
650
+ $buffer = str_replace( $spans[ $index ], $span, $buffer );
651
+ }
652
+ }
653
+ }
654
+ }
655
+ // Video elements, looking for poster attributes that are images.
656
+ $videos = $this->get_elements_from_html( $buffer, 'video' );
657
+ if ( $this->is_iterable( $videos ) ) {
658
+ foreach ( $videos as $index => $video ) {
659
+ ewwwio_debug_message( 'parsing a video element' );
660
+ if ( ! $this->validate_tag( $video ) ) {
661
+ continue;
662
+ }
663
+ $file = $this->get_attribute( $video, 'poster' );
664
+ if ( $file ) {
665
+ ewwwio_debug_message( "checking webp for video poster: $file" );
666
+ if ( $this->validate_image_url( $file ) ) {
667
+ $this->set_attribute( $video, 'data-poster-webp', $this->generate_url( $file ) );
668
+ $this->set_attribute( $video, 'data-poster-image', $file );
669
+ $this->remove_attribute( $video, 'poster' );
670
+ ewwwio_debug_message( "found webp for video poster: $file" );
671
+ $buffer = str_replace( $videos[ $index ], $video, $buffer );
672
+ }
673
+ }
674
+ }
675
+ }
676
+ $this->debug_message( 'all done parsing page for JS WebP' );
677
+ return $buffer;
678
+ }
679
+
680
+ /**
681
+ * Handle image urls within the NextGEN pro lightbox displays.
682
+ *
683
+ * @param array $images An array of NextGEN images and associate attributes.
684
+ * @return array The array of images with WebP versions added.
685
+ */
686
+ function ngg_pro_lightbox_images_queue( $images ) {
687
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
688
+ if ( $this->is_iterable( $images ) ) {
689
+ foreach ( $images as $index => $image ) {
690
+ if ( ! empty( $image['image'] ) && $this->validate_image_url( $image['image'] ) ) {
691
+ $images[ $index ]['image-webp'] = $this->generate_url( $image['image'] );
692
+ }
693
+ if ( ! empty( $image['thumb'] ) && $this->validate_image_url( $image['thumb'] ) ) {
694
+ $images[ $index ]['thumb-webp'] = $this->generate_url( $image['thumb'] );
695
+ }
696
+ if ( ! empty( $image['full_image'] ) && $this->validate_image_url( $image['full_image'] ) ) {
697
+ $images[ $index ]['full_image_webp'] = $this->generate_url( $image['full_image'] );
698
+ }
699
+ if ( $this->is_iterable( $image['srcsets'] ) ) {
700
+ foreach ( $image['srcsets'] as $size => $srcset ) {
701
+ if ( $this->validate_image_url( $srcset ) ) {
702
+ $images[ $index ]['srcsets'][ $size . '-webp' ] = $this->generate_url( $srcset );
703
+ }
704
+ }
705
+ }
706
+ if ( $this->is_iterable( $image['full_srcsets'] ) ) {
707
+ foreach ( $image['full_srcsets'] as $size => $srcset ) {
708
+ if ( $this->validate_image_url( $srcset ) ) {
709
+ $images[ $index ]['full_srcsets'][ $size . '-webp' ] = $this->generate_url( $srcset );
710
+ }
711
+ }
712
+ }
713
+ }
714
+ }
715
+ return $images;
716
+ }
717
+
718
+ /**
719
+ * Adds WebP URLs to the product variations data before it is JSON-encoded.
720
+ *
721
+ * @param array $variations The product variations with all the associated data.
722
+ * @return array The product variations with WebP image URLs added.
723
+ */
724
+ function woocommerce_pre_json_available_variations( $variations ) {
725
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
726
+ if ( $this->is_iterable( $variations ) ) {
727
+ foreach ( $variations as $index => $variation ) {
728
+ if ( $this->is_iterable( $variation['image'] ) ) {
729
+ if ( ! empty( $variation['image']['src'] ) && $this->validate_image_url( $variation['image']['src'] ) ) {
730
+ $variations[ $index ]['image']['src_webp'] = $this->generate_url( $variation['image']['src'] );
731
+ }
732
+ if ( ! empty( $variation['image']['full_src'] ) && $this->validate_image_url( $variation['image']['full_src'] ) ) {
733
+ $variations[ $index ]['image']['full_src_webp'] = $this->generate_url( $variation['image']['full_src'] );
734
+ }
735
+ if ( ! empty( $variation['image']['gallery_thumbnail_src'] ) && $this->validate_image_url( $variation['image']['gallery_thumbnail_src'] ) ) {
736
+ $variations[ $index ]['image']['gallery_thumbnail_src_webp'] = $this->generate_url( $variation['image']['gallery_thumbnail_src'] );
737
+ }
738
+ if ( ! empty( $variation['image']['thumb_src'] ) && $this->validate_image_url( $variation['image']['thumb_src'] ) ) {
739
+ $variations[ $index ]['image']['thumb_src_webp'] = $this->generate_url( $variation['image']['thumb_src'] );
740
+ }
741
+ if ( ! empty( $variation['image']['srcset'] ) ) {
742
+ $webp_srcset = $this->srcset_replace( $variation['image']['srcset'] );
743
+ if ( $webp_srcset ) {
744
+ $variations[ $index ]['image']['srcset_webp'] = $webp_srcset;
745
+ }
746
+ }
747
+ }
748
+ }
749
+ if ( $this->function_exists( 'print_r' ) ) {
750
+ $this->debug_message( print_r( $variations, true ) );
751
+ }
752
+ }
753
+ return $variations;
754
+ }
755
+
756
+ /**
757
+ * Converts a URL to a file-system path and checks if the resulting path exists.
758
+ *
759
+ * @param string $url The URL to mangle.
760
+ * @param string $extension An optional extension to append during is_file().
761
+ * @return bool True if a local file exists correlating to the URL, false otherwise.
762
+ */
763
+ function url_to_path_exists( $url, $extension = '' ) {
764
+ return parent::url_to_path_exists( $url, '.webp' );
765
+ }
766
+
767
+ /**
768
+ * Validate the user-defined exclusions.
769
+ */
770
+ function validate_user_exclusions() {
771
+ $user_exclusions = $this->get_option( $this->prefix . 'webp_rewrite_exclude' );
772
+ $this->debug_message( $this->prefix . 'webp_rewrite_exclude' );
773
+ if ( ! empty( $user_exclusions ) ) {
774
+ if ( is_string( $user_exclusions ) ) {
775
+ $user_exclusions = array( $user_exclusions );
776
+ }
777
+ if ( is_array( $user_exclusions ) ) {
778
+ foreach ( $user_exclusions as $exclusion ) {
779
+ if ( ! is_string( $exclusion ) ) {
780
+ continue;
781
+ }
782
+ if (
783
+ 'a' === $exclusion ||
784
+ 'div' === $exclusion ||
785
+ 'li' === $exclusion ||
786
+ 'picture' === $exclusion ||
787
+ 'section' === $exclusion ||
788
+ 'span' === $exclusion ||
789
+ 'video' === $exclusion
790
+ ) {
791
+ $this->user_element_exclusions[] = $exclusion;
792
+ continue;
793
+ }
794
+ $this->user_exclusions[] = $exclusion;
795
+ }
796
+ }
797
+ }
798
+ }
799
+
800
+ /**
801
+ * Checks if the tag is allowed to be rewritten.
802
+ *
803
+ * @param string $image The HTML tag: img, span, etc.
804
+ * @return bool False if it flags a filter or exclusion, true otherwise.
805
+ */
806
+ function validate_tag( $image ) {
807
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
808
+ // Ignore 0-size Pinterest schema images.
809
+ if ( strpos( $image, 'data-pin-description=' ) && strpos( $image, 'width="0" height="0"' ) ) {
810
+ $this->debug_message( 'data-pin-description img skipped' );
811
+ return false;
812
+ }
813
+
814
+ $test_tag = ltrim( substr( $image, 0, 10 ), '<' );
815
+ foreach ( $this->user_element_exclusions as $element_exclusion ) {
816
+ if ( 0 === strpos( $test_tag, $element_exclusion ) ) {
817
+ $this->debug_message( "$element_exclusion tag skipped" );
818
+ return;
819
+ }
820
+ }
821
+
822
+ $exclusions = apply_filters(
823
+ 'ewwwio_js_webp_exclusions',
824
+ array_merge(
825
+ array(
826
+ 'timthumb.php?',
827
+ 'wpcf7_captcha/',
828
+ ),
829
+ $this->user_exclusions
830
+ ),
831
+ $image
832
+ );
833
+ foreach ( $exclusions as $exclusion ) {
834
+ if ( false !== strpos( $image, $exclusion ) ) {
835
+ $this->debug_message( "tag matched $exclusion" );
836
+ return false;
837
+ }
838
+ }
839
+ return true;
840
+ }
841
+
842
+ /**
843
+ * Checks if the path is a valid WebP image, on-disk or forced.
844
+ *
845
+ * @param string $image The image URL.
846
+ * @return bool True if the file exists or matches a forced path, false otherwise.
847
+ */
848
+ function validate_image_url( $image ) {
849
+ $this->debug_message( "webp validation for $image" );
850
+ if (
851
+ strpos( $image, 'base64,R0lGOD' ) ||
852
+ strpos( $image, 'lazy-load/images/1x1' ) ||
853
+ strpos( $image, '/assets/images/' ) ||
854
+ strpos( $image, '/lazy/placeholder' )
855
+ ) {
856
+ $this->debug_message( 'lazy load placeholder' );
857
+ return false;
858
+ }
859
+ $extension = '';
860
+ $image_path = $this->parse_url( $image, PHP_URL_PATH );
861
+ if ( ! is_null( $image_path ) && $image_path ) {
862
+ $extension = strtolower( pathinfo( $image_path, PATHINFO_EXTENSION ) );
863
+ }
864
+ if ( $extension && 'gif' === $extension && ! $this->get_option( 'ewww_image_optimizer_force_gif2webp' ) ) {
865
+ return false;
866
+ }
867
+ if ( $extension && 'svg' === $extension ) {
868
+ return false;
869
+ }
870
+ if ( $extension && 'webp' === $extension ) {
871
+ return false;
872
+ }
873
+ if ( apply_filters( 'ewww_image_optimizer_skip_webp_rewrite', false, $image ) ) {
874
+ return false;
875
+ }
876
+ if ( $this->get_option( 'ewww_image_optimizer_webp_force' ) && $this->is_iterable( $this->allowed_urls ) ) {
877
+ // Check the image for configured CDN paths.
878
+ foreach ( $this->allowed_urls as $allowed_url ) {
879
+ if ( strpos( $image, $allowed_url ) !== false ) {
880
+ $this->debug_message( 'forced cdn image' );
881
+ return true;
882
+ }
883
+ }
884
+ } elseif ( $this->allowed_urls && $this->allowed_domains ) {
885
+ if ( $this->cdn_to_local( $image ) ) {
886
+ return true;
887
+ }
888
+ }
889
+ return $this->url_to_path_exists( $image );
890
+ }
891
+
892
+ /**
893
+ * Generate a WebP url.
894
+ *
895
+ * Adds .webp to the end, or adds a webp parameter for ExactDN urls.
896
+ *
897
+ * @param string $url The image url.
898
+ * @return string The WebP version of the image url.
899
+ */
900
+ function generate_url( $url ) {
901
+ $path_parts = explode( '?', $url );
902
+ return $path_parts[0] . '.webp' . ( ! empty( $path_parts[1] ) && 'is-pending-load=1' !== $path_parts[1] ? '?' . $path_parts[1] : '' );
903
+ }
904
+
905
+ /**
906
+ * Load full WebP script when SCRIPT_DEBUG is enabled.
907
+ */
908
+ function debug_script() {
909
+ if ( $this->is_amp() ) {
910
+ return;
911
+ }
912
+ if ( ! ewww_image_optimizer_ce_webp_enabled() ) {
913
+ wp_enqueue_script( 'ewww-webp-load-script', plugins_url( '/includes/load-webp.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array(), EWWW_IMAGE_OPTIMIZER_VERSION, true );
914
+ }
915
+ }
916
+
917
+ /**
918
+ * Load minified WebP script when EWWW_IMAGE_OPTIMIZER_WEBP_EXTERNAL_SCRIPT is set.
919
+ */
920
+ function min_external_script() {
921
+ if ( $this->is_amp() ) {
922
+ return;
923
+ }
924
+ if ( ! ewww_image_optimizer_ce_webp_enabled() ) {
925
+ wp_enqueue_script( 'ewww-webp-load-script', plugins_url( '/includes/load-webp.min.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array(), EWWW_IMAGE_OPTIMIZER_VERSION, true );
926
+ }
927
+ }
928
+
929
+ /**
930
+ * Load minified inline version of check WebP script.
931
+ */
932
+ function inline_check_script() {
933
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_NO_JS' ) && EWWW_IMAGE_OPTIMIZER_NO_JS ) {
934
+ return;
935
+ }
936
+ if ( $this->is_amp() ) {
937
+ return;
938
+ }
939
+ $this->debug_message( 'inlining check webp script' );
940
+ echo '<script data-cfasync="false" type="text/javascript">' . $this->check_webp_script . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
941
+ }
942
+
943
+ /**
944
+ * Load minified inline version of load WebP script.
945
+ */
946
+ function inline_load_script() {
947
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_NO_JS' ) && EWWW_IMAGE_OPTIMIZER_NO_JS ) {
948
+ return;
949
+ }
950
+ if ( $this->is_amp() ) {
951
+ return;
952
+ }
953
+ $this->debug_message( 'inlining load webp script' );
954
+ echo '<script data-cfasync="false" type="text/javascript">' . $this->load_webp_script . '</script>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
955
+ }
956
+ }
957
+
958
+ global $eio_js_webp;
959
+ $eio_js_webp = new EIO_JS_Webp();
classes/class-eio-lazy-load.php CHANGED
@@ -285,6 +285,16 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
285
  return $buffer;
286
  }
287
 
 
 
 
 
 
 
 
 
 
 
288
  $above_the_fold = apply_filters( 'eio_lazy_fold', 0 );
289
  $images_processed = 0;
290
 
@@ -360,8 +370,6 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
360
  $lazy_source = $source;
361
  $this->set_attribute( $lazy_source, 'data-srcset', $srcset );
362
  $this->remove_attribute( $lazy_source, 'srcset' );
363
- // TODO: remove this after testing.
364
- /* $this->set_attribute( $lazy_source, 'srcset', $this->placeholder_src, true ); */
365
  $picture = str_replace( $source, $lazy_source, $picture );
366
  }
367
  }
@@ -372,21 +380,23 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
372
  }
373
  }
374
  }
375
- // Video elements, looking for poster attributes that are images.
376
- /* $videos = $this->get_elements_from_html( $buffer, 'video' ); */
377
- $videos = '';
378
- if ( $this->is_iterable( $videos ) ) {
379
- foreach ( $videos as $index => $video ) {
380
- $this->debug_message( 'parsing a video element' );
381
- $file = $this->get_attribute( $video, 'poster' );
382
- if ( $file ) {
383
- $this->debug_message( "checking webp for video poster: $file" );
384
- if ( $this->validate_image_tag( $file ) ) {
385
- $this->set_attribute( $video, 'data-poster-webp', $this->placeholder_src );
386
- $this->set_attribute( $video, 'data-poster-image', $file );
387
- $this->remove_attribute( $video, 'poster' );
388
- $this->debug_message( "found webp for video poster: $file" );
389
- $buffer = str_replace( $videos[ $index ], $video, $buffer );
 
 
390
  }
391
  }
392
  }
@@ -440,19 +450,27 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
440
  $insert_dimensions = false;
441
  if ( apply_filters( 'eio_add_missing_width_height_attrs', $this->get_option( $this->prefix . 'add_missing_dims' ) ) && ( empty( $width_attr ) || empty( $height_attr ) ) ) {
442
  $this->debug_message( 'missing width attr or height attr' );
443
- list( $width_attr, $height_attr ) = $this->get_image_dimensions_by_url( $file );
444
- if ( $width_attr && is_numeric( $width_attr ) && $height_attr && is_numeric( $height_attr ) ) {
445
- $this->debug_message( "found $width_attr and $height_attr to insert" );
446
- $physical_width = $width_attr;
447
- $physical_height = $height_attr;
 
 
 
 
 
 
 
 
 
 
 
448
  $insert_dimensions = true;
449
  }
450
  }
451
- // Check for native lazy loading images.
452
- $loading_attr = $this->get_attribute( $image, 'loading' );
453
- if ( defined( 'EIO_ENABLE_NATIVE_LAZY' ) && EIO_ENABLE_NATIVE_LAZY && ! $loading_attr && is_numeric( $width_attr ) && is_numeric( $height_attr ) ) {
454
- $this->set_attribute( $image, 'loading', 'lazy' );
455
- }
456
 
457
  $placeholder_types = array();
458
  if ( $this->parsing_exactdn && $this->allow_lqip && apply_filters( 'eio_use_lqip', $this->get_option( $this->prefix . 'use_lqip' ), $file ) ) {
@@ -485,6 +503,7 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
485
  $this->debug_message( 'using lqip, maybe' );
486
  if ( false === strpos( $file, 'nggid' ) && ! preg_match( '#\.svg(\?|$)#', $file ) && strpos( $file, $this->exactdn_domain ) ) {
487
  $placeholder_src = add_query_arg( array( 'lazy' => 1 ), $file );
 
488
  break 2;
489
  }
490
  break;
@@ -513,9 +532,11 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
513
 
514
  if ( $filename_width && $filename_height ) {
515
  $placeholder_src = $exactdn->generate_url( $this->content_url . 'lazy/placeholder-' . $filename_width . 'x' . $filename_height . '.png' );
 
516
  break 2;
517
  } else {
518
  $placeholder_src = add_query_arg( array( 'lazy' => 2 ), $file );
 
519
  break 2;
520
  }
521
  }
@@ -536,6 +557,7 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
536
  $png_placeholder_src = $this->create_piip( $filename_width, $filename_height );
537
  if ( $png_placeholder_src ) {
538
  $placeholder_src = $png_placeholder_src;
 
539
  break 2;
540
  }
541
  }
@@ -547,6 +569,13 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
547
  $this->debug_message( "current placeholder is $placeholder_src" );
548
 
549
  $placeholder_src = apply_filters( 'eio_lazy_placeholder', $placeholder_src, $image );
 
 
 
 
 
 
 
550
  if ( $srcset ) {
551
  if ( strpos( $placeholder_src, '64,R0lGOD' ) ) {
552
  $this->set_attribute( $image, 'srcset', $placeholder_src, true );
@@ -571,13 +600,14 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
571
  $this->set_attribute( $image, 'src', $placeholder_src, true );
572
  }
573
 
574
- $existing_class = trim( $this->get_attribute( $image, 'class' ) );
575
  if ( ! empty( $existing_class ) ) {
576
- $this->set_attribute( $image, 'class', $existing_class . ' lazyload', true );
577
  } else {
578
  $this->set_attribute( $image, 'class', 'lazyload', true );
579
  }
580
  if ( $insert_dimensions ) {
 
581
  $this->set_attribute( $image, 'width', $width_attr, true );
582
  $this->set_attribute( $image, 'height', $height_attr, true );
583
  }
@@ -585,6 +615,8 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
585
  $this->set_attribute( $image, 'data-eio-rwidth', $physical_width, true );
586
  $this->set_attribute( $image, 'data-eio-rheight', $physical_height, true );
587
  }
 
 
588
  return $image;
589
  }
590
 
@@ -827,6 +859,34 @@ if ( ! class_exists( 'EIO_Lazy_Load' ) ) {
827
  return true;
828
  }
829
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
830
  /**
831
  * Build a PNG inline image placeholder.
832
  *
285
  return $buffer;
286
  }
287
 
288
+ // If JS WebP isn't running, set ewww_webp_supported to false so we have something defined.
289
+ if ( ! class_exists( 'EIO_JS_Webp' ) ) {
290
+ $body_tags = $this->get_elements_from_html( $buffer, 'body' );
291
+ if ( $this->is_iterable( $body_tags ) && ! empty( $body_tags[0] ) && false !== strpos( $body_tags[0], '<body' ) ) {
292
+ $body_webp_script = '<script>var ewww_webp_supported=false;</script>';
293
+ // Add the WebP script right after the opening tag.
294
+ $buffer = str_replace( $body_tags[0], $body_tags[0] . "\n" . $body_webp_script, $buffer );
295
+ }
296
+ }
297
+
298
  $above_the_fold = apply_filters( 'eio_lazy_fold', 0 );
299
  $images_processed = 0;
300
 
370
  $lazy_source = $source;
371
  $this->set_attribute( $lazy_source, 'data-srcset', $srcset );
372
  $this->remove_attribute( $lazy_source, 'srcset' );
 
 
373
  $picture = str_replace( $source, $lazy_source, $picture );
374
  }
375
  }
380
  }
381
  }
382
  }
383
+ // Iframe elements, looking for stuff like YouTube embeds.
384
+ if ( in_array( 'iframe', $this->user_element_exclusions, true ) ) {
385
+ $frames = '';
386
+ } else {
387
+ $frames = $this->get_elements_from_html( $buffer, 'iframe' );
388
+ }
389
+ if ( $this->is_iterable( $frames ) ) {
390
+ foreach ( $frames as $index => $frame ) {
391
+ $this->debug_message( 'parsing an iframe element' );
392
+ $url = $this->get_attribute( $frame, 'src' );
393
+ if ( $url && $this->validate_iframe_tag( $frame ) ) {
394
+ $this->debug_message( "lazifying iframe for: $url" );
395
+ $this->set_attribute( $frame, 'data-src', $url );
396
+ $this->remove_attribute( $frame, 'src' );
397
+ $this->set_attribute( $frame, 'class', trim( $this->get_attribute( $frame, 'class' ) . ' lazyload' ), true );
398
+ if ( $frame !== $frames[ $index ] ) {
399
+ $buffer = str_replace( $frames[ $index ], $frame, $buffer );
400
  }
401
  }
402
  }
450
  $insert_dimensions = false;
451
  if ( apply_filters( 'eio_add_missing_width_height_attrs', $this->get_option( $this->prefix . 'add_missing_dims' ) ) && ( empty( $width_attr ) || empty( $height_attr ) ) ) {
452
  $this->debug_message( 'missing width attr or height attr' );
453
+ list( $new_width_attr, $new_height_attr ) = $this->get_image_dimensions_by_url( $file );
454
+ if ( $new_width_attr && is_numeric( $new_width_attr ) && $new_height_attr && is_numeric( $new_height_attr ) ) {
455
+ $this->debug_message( "found $width_attr and $height_attr to insert (maybe)" );
456
+ if ( $width_attr && is_numeric( $width_attr ) && $width_attr < $new_width_attr ) { // Then $height_attr is empty...
457
+ $height_attr = round( ( $new_height_attr / $new_width_attr ) * $width_attr );
458
+ $this->debug_message( "width was set to $width_attr, height was empty, but now $height_attr" );
459
+ } elseif ( $height_attr && is_numeric( $height_attr ) && $height_attr < $new_height_attr ) { // Or $width_attr is empty...
460
+ $width_attr = round( ( $new_width_attr / $new_height_attr ) * $height_attr );
461
+ $this->debug_message( "height was set to $height_attr, width was empty, but now $width_attr" );
462
+ } else {
463
+ $width_attr = $new_width_attr;
464
+ $height_attr = $new_height_attr;
465
+ $this->debug_message( 'both width and height were empty, filling for sure' );
466
+ }
467
+ $physical_width = $new_width_attr;
468
+ $physical_height = $new_height_attr;
469
  $insert_dimensions = true;
470
  }
471
  }
472
+
473
+ $use_native_lazy = false;
 
 
 
474
 
475
  $placeholder_types = array();
476
  if ( $this->parsing_exactdn && $this->allow_lqip && apply_filters( 'eio_use_lqip', $this->get_option( $this->prefix . 'use_lqip' ), $file ) ) {
503
  $this->debug_message( 'using lqip, maybe' );
504
  if ( false === strpos( $file, 'nggid' ) && ! preg_match( '#\.svg(\?|$)#', $file ) && strpos( $file, $this->exactdn_domain ) ) {
505
  $placeholder_src = add_query_arg( array( 'lazy' => 1 ), $file );
506
+ $use_native_lazy = true;
507
  break 2;
508
  }
509
  break;
532
 
533
  if ( $filename_width && $filename_height ) {
534
  $placeholder_src = $exactdn->generate_url( $this->content_url . 'lazy/placeholder-' . $filename_width . 'x' . $filename_height . '.png' );
535
+ $use_native_lazy = true;
536
  break 2;
537
  } else {
538
  $placeholder_src = add_query_arg( array( 'lazy' => 2 ), $file );
539
+ $use_native_lazy = true;
540
  break 2;
541
  }
542
  }
557
  $png_placeholder_src = $this->create_piip( $filename_width, $filename_height );
558
  if ( $png_placeholder_src ) {
559
  $placeholder_src = $png_placeholder_src;
560
+ $use_native_lazy = true;
561
  break 2;
562
  }
563
  }
569
  $this->debug_message( "current placeholder is $placeholder_src" );
570
 
571
  $placeholder_src = apply_filters( 'eio_lazy_placeholder', $placeholder_src, $image );
572
+
573
+ // Check for native lazy loading images.
574
+ $loading_attr = $this->get_attribute( $image, 'loading' );
575
+ if ( ( ! defined( 'EIO_DISABLE_NATIVE_LAZY' ) || ! EIO_DISABLE_NATIVE_LAZY ) && ! $loading_attr && $use_native_lazy ) {
576
+ $this->set_attribute( $image, 'loading', 'lazy' );
577
+ }
578
+
579
  if ( $srcset ) {
580
  if ( strpos( $placeholder_src, '64,R0lGOD' ) ) {
581
  $this->set_attribute( $image, 'srcset', $placeholder_src, true );
600
  $this->set_attribute( $image, 'src', $placeholder_src, true );
601
  }
602
 
603
+ $existing_class = $this->get_attribute( $image, 'class' );
604
  if ( ! empty( $existing_class ) ) {
605
+ $this->set_attribute( $image, 'class', trim( $existing_class . ' lazyload' ), true );
606
  } else {
607
  $this->set_attribute( $image, 'class', 'lazyload', true );
608
  }
609
  if ( $insert_dimensions ) {
610
+ $this->debug_message( "setting width=$width_attr and height=$height_attr" );
611
  $this->set_attribute( $image, 'width', $width_attr, true );
612
  $this->set_attribute( $image, 'height', $height_attr, true );
613
  }
615
  $this->set_attribute( $image, 'data-eio-rwidth', $physical_width, true );
616
  $this->set_attribute( $image, 'data-eio-rheight', $physical_height, true );
617
  }
618
+ $this->debug_message( 'lazified img element:' );
619
+ $this->debug_message( trim( $image ) );
620
  return $image;
621
  }
622
 
859
  return true;
860
  }
861
 
862
+ /**
863
+ * Checks if an iframe tag is allowed to be lazy loaded.
864
+ *
865
+ * @param string $tag The tag.
866
+ * @return bool True if the tag is allowed, false otherwise.
867
+ */
868
+ function validate_iframe_tag( $tag ) {
869
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
870
+ $exclusions = apply_filters(
871
+ 'eio_lazy_iframe_exclusions',
872
+ array_merge(
873
+ array(
874
+ 'data-no-lazy=',
875
+ 'lazyload',
876
+ 'skip-lazy',
877
+ ),
878
+ $this->user_exclusions
879
+ ),
880
+ $tag
881
+ );
882
+ foreach ( $exclusions as $exclusion ) {
883
+ if ( false !== strpos( $tag, $exclusion ) ) {
884
+ return false;
885
+ }
886
+ }
887
+ return true;
888
+ }
889
+
890
  /**
891
  * Build a PNG inline image placeholder.
892
  *
classes/class-eio-page-parser.php CHANGED
@@ -29,6 +29,7 @@ if ( ! class_exists( 'EIO_Page_Parser' ) ) {
29
  'jpe',
30
  'png',
31
  'svg',
 
32
  );
33
 
34
  /**
@@ -53,7 +54,7 @@ if ( ! class_exists( 'EIO_Page_Parser' ) ) {
53
  if ( $hyperlinks ) {
54
  $this->debug_message( 'using figure+hyperlink(a) patterns with src required' );
55
  $search_pattern = '#(?:<figure[^>]*?\s+?class\s*=\s*["\'](?P<figure_class>[\w\s-]+?)["\'][^>]*?>\s*)?(?:<a[^>]*?\s+?href\s*=\s*["\'](?P<link_url>[^\s]+?)["\'][^>]*?>\s*)?(?P<img_tag><img[^>]*?\s+?src\s*=\s*("|\')(?P<img_url>(?!\4).+?)\4[^>]*?>){1}(?:\s*</a>)?#is';
56
- $unquoted_pattern = '#(?:<figure[^>]*?\s+?class\s*=\s*(?P<figure_class>[\w-]+)[^>]*?>\s*)?(?:<a[^>]*?\s+?href\s*=\s*(?P<link_url>[^"\'\\\\][^\s>]+)[^>]*?>\s*)?(?P<img_tag><img[^>]*?\s+?src\s*=\s*(?P<img_url>[^"\'\\\\][^\s>]+)[^>]*?>){1}(?:\s*</a>)?#is';
57
  } elseif ( $src_required ) {
58
  $this->debug_message( 'using plain img pattern, src still required' );
59
  $search_pattern = '#(?P<img_tag><img[^>]*?\s+?src\s*=\s*("|\')(?P<img_url>(?!\2).+?)\2[^>]*?>)#is';
@@ -231,7 +232,7 @@ if ( ! class_exists( 'EIO_Page_Parser' ) ) {
231
  $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
232
  $this->debug_message( "getting dimensions for $url" );
233
 
234
- list( $width, $height ) = $this->get_dimensions_from_filename( $url );
235
  if ( empty( $width ) || empty( $height ) ) {
236
  // Couldn't get it from the URL directly, see if we can get the actual filename.
237
  $file = false;
@@ -362,7 +363,7 @@ if ( ! class_exists( 'EIO_Page_Parser' ) ) {
362
  $value = trim( $value );
363
  if ( $replace ) {
364
  // Don't forget, back references cannot be used in character classes.
365
- $new_element = preg_replace( '#\s' . $name . '\s*=\s*("|\')(?!\1).*?\1#is', " $name=$1$value$1", $element );
366
  if ( strpos( $new_element, "$name=" ) && $new_element !== $element ) {
367
  $element = $new_element;
368
  return;
29
  'jpe',
30
  'png',
31
  'svg',
32
+ 'webp',
33
  );
34
 
35
  /**
54
  if ( $hyperlinks ) {
55
  $this->debug_message( 'using figure+hyperlink(a) patterns with src required' );
56
  $search_pattern = '#(?:<figure[^>]*?\s+?class\s*=\s*["\'](?P<figure_class>[\w\s-]+?)["\'][^>]*?>\s*)?(?:<a[^>]*?\s+?href\s*=\s*["\'](?P<link_url>[^\s]+?)["\'][^>]*?>\s*)?(?P<img_tag><img[^>]*?\s+?src\s*=\s*("|\')(?P<img_url>(?!\4).+?)\4[^>]*?>){1}(?:\s*</a>)?#is';
57
+ $unquoted_pattern = '#(?:<figure[^>]*?\s+?class\s*=\s*(?P<figure_class>[\w-]+)[^>]*?>\s*)?(?:<a[^>]*?\s+?href\s*=\s*(?P<link_url>[^"\'\\\\<>][^\s<>]+)[^>]*?>\s*)?(?P<img_tag><img[^>]*?\s+?src\s*=\s*(?P<img_url>[^"\'\\\\<>][^\s<>]+)[^>]*?>){1}(?:\s*</a>)?#is';
58
  } elseif ( $src_required ) {
59
  $this->debug_message( 'using plain img pattern, src still required' );
60
  $search_pattern = '#(?P<img_tag><img[^>]*?\s+?src\s*=\s*("|\')(?P<img_url>(?!\2).+?)\2[^>]*?>)#is';
232
  $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
233
  $this->debug_message( "getting dimensions for $url" );
234
 
235
+ list( $width, $height ) = $this->get_dimensions_from_filename( $url, ! empty( $this->parsing_exactdn ) );
236
  if ( empty( $width ) || empty( $height ) ) {
237
  // Couldn't get it from the URL directly, see if we can get the actual filename.
238
  $file = false;
363
  $value = trim( $value );
364
  if ( $replace ) {
365
  // Don't forget, back references cannot be used in character classes.
366
+ $new_element = preg_replace( '#\s' . $name . '\s*=\s*("|\')(?!\1).*?\1#is', ' ' . $name . '=${1}' . $value . '${1}', $element );
367
  if ( strpos( $new_element, "$name=" ) && $new_element !== $element ) {
368
  $element = $new_element;
369
  return;
classes/class-ewww-flag.php CHANGED
@@ -299,7 +299,7 @@ if ( ! class_exists( 'EWWW_Flag' ) ) {
299
  // Store the IDs to optimize in the options table of the db.
300
  update_option( 'ewww_image_optimizer_bulk_flag_attachments', $ids );
301
  // Add the EWWW IO javascript.
302
- wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
303
  // Add the styling for the progressbar.
304
  wp_enqueue_style( 'jquery-ui-progressbar', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) );
305
  // Prepare a few variables to be used by the javascript code.
299
  // Store the IDs to optimize in the options table of the db.
300
  update_option( 'ewww_image_optimizer_bulk_flag_attachments', $ids );
301
  // Add the EWWW IO javascript.
302
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio-bulk.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
303
  // Add the styling for the progressbar.
304
  wp_enqueue_style( 'jquery-ui-progressbar', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) );
305
  // Prepare a few variables to be used by the javascript code.
classes/class-ewww-nextcellent.php CHANGED
@@ -506,7 +506,7 @@ if ( ! class_exists( 'EWWW_Nextcellent' ) ) {
506
  // Store the image IDs to process in the db.
507
  update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
508
  // Add the EWWW IO script.
509
- wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
510
  // Replacing the built-in nextgen styling rules for progressbar.
511
  wp_register_style( 'ngg-jqueryui', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) );
512
  // Enqueue the progressbar styling.
506
  // Store the image IDs to process in the db.
507
  update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
508
  // Add the EWWW IO script.
509
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio-bulk.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
510
  // Replacing the built-in nextgen styling rules for progressbar.
511
  wp_register_style( 'ngg-jqueryui', plugins_url( '/includes/jquery-ui-1.10.1.custom.css', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ) );
512
  // Enqueue the progressbar styling.
classes/class-ewww-nextgen.php CHANGED
@@ -229,6 +229,7 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
229
  * @param object $size The name of the size generated.
230
  */
231
  function ewww_ngg_generated_image( $image, $size ) {
 
232
  global $ewww_image;
233
  // Creating the 'registry' object for working with nextgen.
234
  $registry = C_Component_Registry::get_instance();
@@ -250,6 +251,7 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
250
  * Manually process an image from the NextGEN Gallery.
251
  */
252
  function ewww_ngg_manual() {
 
253
  // Check permission of current user.
254
  $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
255
  if ( false === current_user_can( $permissions ) ) {
@@ -319,6 +321,7 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
319
  * Restore an image from the NextGEN Gallery.
320
  */
321
  function ewww_ngg_cloud_restore() {
 
322
  // Check permission of current user.
323
  $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
324
  if ( false === current_user_can( $permissions ) ) {
@@ -629,8 +632,15 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
629
  */
630
  function ewww_ngg_bulk_preview() {
631
  if ( ! empty( $_REQUEST['doaction'] ) ) {
632
- if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'ngg_bulkgallery' ) ) {
633
- return;
 
 
 
 
 
 
 
634
  }
635
  // If there is no requested bulk action, do nothing.
636
  if ( empty( $_REQUEST['bulkaction'] ) ) {
@@ -653,7 +663,7 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
653
  <?php
654
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
655
  ewww_image_optimizer_cloud_verify( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) );
656
- echo '<a id="ewww-bulk-credits-available" target="_blank" class="page-title-action" style="float:right;" href="https://ewww.io/my-account/">' . esc_html__( 'Image credits available:', 'ewww-image-optimizer' ) . ' ' . esc_html( ewww_image_optimizer_cloud_quota() ) . '</a>';
657
  }
658
  echo '<div id="ewww-bulk-warning" class="ewww-bulk-info notice notice-warning"><p>' . esc_html__( 'Bulk Optimization will alter your original images and cannot be undone. Please be sure you have a backup of your images before proceeding.', 'ewww-image-optimizer' ) . '</p></div>';
659
  // Retrieve the value of the 'bulk resume' option and set the button text for the form to use.
@@ -824,7 +834,7 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
824
  // Store the image IDs to process in the db.
825
  update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
826
  // Add the EWWW IO script.
827
- wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
828
  // Replacing the built-in nextgen styling rules for progressbar, partially because the bulk optimize page doesn't work without them.
829
  wp_deregister_style( 'ngg-jqueryui' );
830
  wp_deregister_style( 'ngg-jquery-ui' );
@@ -1037,7 +1047,14 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
1037
  */
1038
  function ewww_ngg_bulk_action_handler() {
1039
  ewwwio_debug_message( '<b>' . __METHOD__ . '()</b>' );
1040
- if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'ngg_bulkgallery' ) ) {
 
 
 
 
 
 
 
1041
  return;
1042
  }
1043
  // If the requested page is blank, or not a bulk_optimize, do nothing.
@@ -1064,7 +1081,7 @@ if ( ! class_exists( 'EWWW_Nextgen' ) ) {
1064
  '_wpnonce' => sanitize_key( $_REQUEST['_wpnonce'] ),
1065
  'bulk_type' => $type,
1066
  'bulkaction' => 'bulk_optimize',
1067
- 'doaction' => sanitize_key( $_REQUEST['doaction'] ),
1068
  ),
1069
  admin_url( 'admin.php' )
1070
  )
229
  * @param object $size The name of the size generated.
230
  */
231
  function ewww_ngg_generated_image( $image, $size ) {
232
+ ewwwio_debug_message( '<b>' . __METHOD__ . '()</b>' );
233
  global $ewww_image;
234
  // Creating the 'registry' object for working with nextgen.
235
  $registry = C_Component_Registry::get_instance();
251
  * Manually process an image from the NextGEN Gallery.
252
  */
253
  function ewww_ngg_manual() {
254
+ ewwwio_debug_message( '<b>' . __METHOD__ . '()</b>' );
255
  // Check permission of current user.
256
  $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
257
  if ( false === current_user_can( $permissions ) ) {
321
  * Restore an image from the NextGEN Gallery.
322
  */
323
  function ewww_ngg_cloud_restore() {
324
+ ewwwio_debug_message( '<b>' . __METHOD__ . '()</b>' );
325
  // Check permission of current user.
326
  $permissions = apply_filters( 'ewww_image_optimizer_manual_permissions', '' );
327
  if ( false === current_user_can( $permissions ) ) {
632
  */
633
  function ewww_ngg_bulk_preview() {
634
  if ( ! empty( $_REQUEST['doaction'] ) ) {
635
+ if (
636
+ empty( $_REQUEST['_wpnonce'] ) ||
637
+ (
638
+ ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'ngg_bulkgallery' ) &&
639
+ ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'ngg_updategallery' )
640
+ )
641
+ ) {
642
+ ewwwio_debug_message( 'nonce verify failed' );
643
+ return;
644
  }
645
  // If there is no requested bulk action, do nothing.
646
  if ( empty( $_REQUEST['bulkaction'] ) ) {
663
  <?php
664
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
665
  ewww_image_optimizer_cloud_verify( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) );
666
+ echo '<span id="ewww-bulk-credits-available">' . esc_html__( 'Image credits available:', 'ewww-image-optimizer' ) . ' ' . wp_kses_post( ewww_image_optimizer_cloud_quota() ) . '</span>';
667
  }
668
  echo '<div id="ewww-bulk-warning" class="ewww-bulk-info notice notice-warning"><p>' . esc_html__( 'Bulk Optimization will alter your original images and cannot be undone. Please be sure you have a backup of your images before proceeding.', 'ewww-image-optimizer' ) . '</p></div>';
669
  // Retrieve the value of the 'bulk resume' option and set the button text for the form to use.
834
  // Store the image IDs to process in the db.
835
  update_option( 'ewww_image_optimizer_bulk_ngg_attachments', $images, false );
836
  // Add the EWWW IO script.
837
+ wp_enqueue_script( 'ewwwbulkscript', plugins_url( '/includes/eio-bulk.js', EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE ), array( 'jquery', 'jquery-ui-progressbar', 'jquery-ui-slider', 'postbox', 'dashboard' ), EWWW_IMAGE_OPTIMIZER_VERSION );
838
  // Replacing the built-in nextgen styling rules for progressbar, partially because the bulk optimize page doesn't work without them.
839
  wp_deregister_style( 'ngg-jqueryui' );
840
  wp_deregister_style( 'ngg-jquery-ui' );
1047
  */
1048
  function ewww_ngg_bulk_action_handler() {
1049
  ewwwio_debug_message( '<b>' . __METHOD__ . '()</b>' );
1050
+ if (
1051
+ empty( $_REQUEST['_wpnonce'] ) ||
1052
+ (
1053
+ ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'ngg_bulkgallery' ) &&
1054
+ ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'ngg_updategallery' )
1055
+ )
1056
+ ) {
1057
+ ewwwio_debug_message( 'nonce verify failed' );
1058
  return;
1059
  }
1060
  // If the requested page is blank, or not a bulk_optimize, do nothing.
1081
  '_wpnonce' => sanitize_key( $_REQUEST['_wpnonce'] ),
1082
  'bulk_type' => $type,
1083
  'bulkaction' => 'bulk_optimize',
1084
+ 'doaction' => array_map( 'intval', wp_unslash( $_REQUEST['doaction'] ) ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1085
  ),
1086
  admin_url( 'admin.php' )
1087
  )
classes/class-ewwwio-cli.php CHANGED
@@ -32,17 +32,21 @@ class EWWWIO_CLI extends WP_CLI_Command {
32
  *
33
  * <force>
34
  * : optional, should the plugin re-optimize images that have already been processed.
35
- * * <reset>
 
36
  * : optional, start the optimizer back at the beginning instead of resuming from last position
37
  *
 
 
 
38
  * <noprompt>
39
  * : do not prompt, just start optimizing
40
  *
41
  * ## EXAMPLES
42
  *
43
- * wp-cli ewwwio optimize media 5 --force --reset --noprompt
44
  *
45
- * @synopsis <library> [<delay>] [--force] [--reset] [--noprompt]
46
  *
47
  * @global bool $ewww_defer Gets set to false to make sure optimization happens inline.
48
  *
@@ -52,6 +56,8 @@ class EWWWIO_CLI extends WP_CLI_Command {
52
  function optimize( $args, $assoc_args ) {
53
  global $ewww_defer;
54
  $ewww_defer = false;
 
 
55
  // because NextGEN hasn't flushed it's buffers...
56
  while ( @ob_end_flush() ) {
57
  }
@@ -71,6 +77,13 @@ class EWWWIO_CLI extends WP_CLI_Command {
71
  global $ewww_force;
72
  $ewww_force = 1;
73
  }
 
 
 
 
 
 
 
74
  /* translators: 1: type of images, like media, or nextgen 2: number of seconds */
75
  WP_CLI::line( sprintf( _x( 'Optimizing %1$s with a %2$d second pause between images.', 'string will be something like "media" or "nextgen"', 'ewww-image-optimizer' ), $library, $delay ) );
76
  // Let's get started, shall we?
32
  *
33
  * <force>
34
  * : optional, should the plugin re-optimize images that have already been processed.
35
+ *
36
+ * <reset>
37
  * : optional, start the optimizer back at the beginning instead of resuming from last position
38
  *
39
+ * <webp-only>
40
+ * : optional, only do WebP Conversion, skip all other operations
41
+ *
42
  * <noprompt>
43
  * : do not prompt, just start optimizing
44
  *
45
  * ## EXAMPLES
46
  *
47
+ * wp-cli ewwwio optimize media 5 --force --reset --webp-only --noprompt
48
  *
49
+ * @synopsis <library> [<delay>] [--force] [--reset] [--webp-only] [--noprompt]
50
  *
51
  * @global bool $ewww_defer Gets set to false to make sure optimization happens inline.
52
  *
56
  function optimize( $args, $assoc_args ) {
57
  global $ewww_defer;
58
  $ewww_defer = false;
59
+ global $ewww_webp_only;
60
+ $ewww_webp_only = false;
61
  // because NextGEN hasn't flushed it's buffers...
62
  while ( @ob_end_flush() ) {
63
  }
77
  global $ewww_force;
78
  $ewww_force = 1;
79
  }
80
+ if ( ! empty( $assoc_args['webp-only'] ) ) {
81
+ if ( empty( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ) ) {
82
+ WP_CLI::error( __( 'WebP Conversion is not enabled.', 'ewww-image-optimizer' ) );
83
+ }
84
+ WP_CLI::line( __( 'Running WebP conversion only.', 'ewww-image-optimizer' ) );
85
+ $ewww_webp_only = true;
86
+ }
87
  /* translators: 1: type of images, like media, or nextgen 2: number of seconds */
88
  WP_CLI::line( sprintf( _x( 'Optimizing %1$s with a %2$d second pause between images.', 'string will be something like "media" or "nextgen"', 'ewww-image-optimizer' ), $library, $delay ) );
89
  // Let's get started, shall we?
classes/class-ewwwio-gd-editor.php CHANGED
@@ -240,7 +240,7 @@ if ( class_exists( 'Bbpp_Animated_Gif' ) ) {
240
 
241
  $resized = $this->_resize( $max_w, $max_h, $crop );
242
 
243
- if ( is_resource( $resized ) ) {
244
  imagedestroy( $this->image );
245
  $this->image = $resized;
246
  return true;
@@ -359,7 +359,7 @@ if ( class_exists( 'Bbpp_Animated_Gif' ) ) {
359
  $duplicate = ( (int) $orig_size['width'] === (int) $size_data['width'] && (int) $orig_size['height'] === (int) $size_data['height'] );
360
 
361
  if ( ! is_wp_error( $image ) && ! $duplicate ) {
362
- if ( is_resource( $image ) ) {
363
  $resized = $this->_save( $image );
364
  imagedestroy( $image );
365
  } elseif ( is_string( $image ) ) {
240
 
241
  $resized = $this->_resize( $max_w, $max_h, $crop );
242
 
243
+ if ( is_resource( $resized ) || ( is_object( $resized ) && $resized instanceof GdImage ) ) {
244
  imagedestroy( $this->image );
245
  $this->image = $resized;
246
  return true;
359
  $duplicate = ( (int) $orig_size['width'] === (int) $size_data['width'] && (int) $orig_size['height'] === (int) $size_data['height'] );
360
 
361
  if ( ! is_wp_error( $image ) && ! $duplicate ) {
362
+ if ( is_resource( $image ) || ( is_object( $image ) && $image instanceof GdImage ) ) {
363
  $resized = $this->_save( $image );
364
  imagedestroy( $image );
365
  } elseif ( is_string( $image ) ) {
classes/class-ewwwio-gmagick-editor.php CHANGED
@@ -79,7 +79,6 @@ if ( class_exists( 'WP_Image_Editor_Gmagick' ) ) {
79
  }
80
  */
81
  }
82
- ewww_image_optimizer_debug_log();
83
  }
84
  ewwwio_memory( __FUNCTION__ );
85
  return $saved;
79
  }
80
  */
81
  }
 
82
  }
83
  ewwwio_memory( __FUNCTION__ );
84
  return $saved;
classes/class-ewwwio-imagick-editor.php CHANGED
@@ -181,7 +181,6 @@ if ( class_exists( 'WP_Thumb_Image_Editor_Imagick' ) ) {
181
  $image_size = ewww_image_optimizer_filesize( $filename );
182
  ewwwio_debug_message( "image editor size: $image_size" );
183
  }
184
- ewww_image_optimizer_debug_log();
185
  }
186
  ewwwio_memory( __FUNCTION__ );
187
  return $saved;
181
  $image_size = ewww_image_optimizer_filesize( $filename );
182
  ewwwio_debug_message( "image editor size: $image_size" );
183
  }
 
184
  }
185
  ewwwio_memory( __FUNCTION__ );
186
  return $saved;
classes/class-exactdn.php CHANGED
@@ -162,6 +162,8 @@ if ( ! class_exists( 'ExactDN' ) ) {
162
  if ( ! $this->setup() ) {
163
  return;
164
  }
 
 
165
 
166
  // Images in post content and galleries.
167
  add_filter( 'the_content', array( $this, 'filter_the_content' ), 999999 );
@@ -274,9 +276,11 @@ if ( ! class_exists( 'ExactDN' ) ) {
274
  $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
275
  // If we don't have a domain yet, go grab one.
276
  $this->plan_id = $this->get_exactdn_option( 'plan_id' );
 
277
  if ( ! $this->get_exactdn_domain() ) {
278
  $this->debug_message( 'attempting to activate exactDN' );
279
  $exactdn_domain = $this->activate_site();
 
280
  } else {
281
  $this->debug_message( 'grabbing existing exactDN domain' );
282
  $exactdn_domain = $this->get_exactdn_domain();
@@ -284,11 +288,19 @@ if ( ! class_exists( 'ExactDN' ) ) {
284
  if ( ! $exactdn_domain ) {
285
  delete_option( $this->prefix . 'exactdn' );
286
  delete_site_option( $this->prefix . 'exactdn' );
 
287
  return false;
288
  }
 
289
  // If we have a domain, verify it.
290
- if ( $this->verify_domain( $exactdn_domain ) ) {
291
- $this->debug_message( 'verified existing exactDN domain' );
 
 
 
 
 
 
292
  $this->exactdn_domain = $exactdn_domain;
293
  $this->debug_message( 'exactdn_domain: ' . $exactdn_domain );
294
  $this->debug_message( 'exactdn_plan_id: ' . $this->plan_id );
@@ -298,9 +310,47 @@ if ( ! class_exists( 'ExactDN' ) ) {
298
  delete_option( $this->prefix . 'exactdn_verified' );
299
  delete_site_option( $this->prefix . 'exactdn_domain' );
300
  delete_site_option( $this->prefix . 'exactdn_verified' );
 
301
  return false;
302
  }
303
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  /**
305
  * Use the Site URL to get the zone domain.
306
  */
@@ -384,6 +434,15 @@ if ( ! class_exists( 'ExactDN' ) ) {
384
  return false;
385
  }
386
 
 
 
 
 
 
 
 
 
 
387
  /**
388
  * Verify the ExactDN domain.
389
  *
@@ -403,7 +462,7 @@ if ( ! class_exists( 'ExactDN' ) ) {
403
  }
404
 
405
  $this->check_verify_method();
406
- $this->set_exactdn_option( 'checkin', time() + DAY_IN_SECONDS );
407
 
408
  // Set a default error.
409
  global $exactdn_activate_error;
@@ -426,8 +485,9 @@ if ( ! class_exists( 'ExactDN' ) ) {
426
  array(
427
  'timeout' => 10,
428
  'body' => array(
429
- 'alias' => $domain,
430
- 'url' => $test_url,
 
431
  ),
432
  )
433
  );
@@ -472,7 +532,8 @@ if ( ! class_exists( 'ExactDN' ) ) {
472
  array(
473
  'timeout' => 10,
474
  'body' => array(
475
- 'alias' => $domain,
 
476
  ),
477
  )
478
  );
@@ -1180,8 +1241,26 @@ if ( ! class_exists( 'ExactDN' ) ) {
1180
  $new_tag = $tag;
1181
 
1182
  // If present, replace the link href with an ExactDN URL for the full-size image.
1183
- if ( ! empty( $images['link_url'][ $index ] ) && $this->validate_image_url( $images['link_url'][ $index ] ) ) {
1184
- $new_tag = preg_replace( '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i', '\1' . $this->generate_url( $images['link_url'][ $index ] ) . '\2', $new_tag, 1 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1185
  }
1186
 
1187
  // Insert new image src into the srcset as well, if we have a width.
@@ -1334,9 +1413,13 @@ if ( ! class_exists( 'ExactDN' ) ) {
1334
  }
1335
  } // End foreach().
1336
  } // End if();
 
 
 
 
 
1337
  $element_types = apply_filters( 'eio_allowed_background_image_elements', array( 'div', 'li', 'span', 'section', 'a' ) );
1338
  foreach ( $element_types as $element_type ) {
1339
- // Process background images on HTML elements.
1340
  $content = $this->filter_bg_images( $content, $element_type );
1341
  }
1342
  if ( $this->filtering_the_page ) {
@@ -1365,6 +1448,83 @@ if ( ! class_exists( 'ExactDN' ) ) {
1365
  return $content;
1366
  }
1367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1368
  /**
1369
  * Parse page content looking for elements with CSS background-image properties.
1370
  *
@@ -1627,6 +1787,10 @@ if ( ! class_exists( 'ExactDN' ) ) {
1627
  $started = microtime( true );
1628
  $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
1629
 
 
 
 
 
1630
  // Don't foul up the admin side of things, unless a plugin wants to.
1631
  if ( is_admin() &&
1632
  /**
@@ -2658,9 +2822,21 @@ if ( ! class_exists( 'ExactDN' ) ) {
2658
  * @return boolean True to skip the page, unchanged otherwise.
2659
  */
2660
  function skip_page( $skip = false, $uri = '' ) {
 
 
 
 
 
 
 
 
 
2661
  if ( false !== strpos( $uri, 'ct_builder=' ) ) {
2662
  return true;
2663
  }
 
 
 
2664
  if ( false !== strpos( $uri, '?fl_builder' ) ) {
2665
  return true;
2666
  }
@@ -2871,6 +3047,9 @@ if ( ! class_exists( 'ExactDN' ) ) {
2871
  if ( $this->plan_id > 1 && false === strpos( $image_url, 'quality=' ) && 75 !== (int) $webp_quality && $webp_quality < $jpg_quality ) {
2872
  $more_args['quality'] = $webp_quality;
2873
  }
 
 
 
2874
  // Merge given args with the automatic (option-based) args, and also makes sure args is an array if it was previously a string.
2875
  $args = wp_parse_args( $args, $more_args );
2876
 
@@ -2978,7 +3157,7 @@ if ( ! class_exists( 'ExactDN' ) ) {
2978
  }
2979
 
2980
  // Clear out args for some files (like videos) that might go through image_downsize.
2981
- if ( ! empty( $extension ) && in_array( $extension, array( 'mp4', 'm4p', 'm4v', 'mov', 'wvm', 'qt', 'webp', 'ogv', 'mpg', 'mpeg', 'mpv' ), true ) ) {
2982
  $args = array();
2983
  }
2984
 
162
  if ( ! $this->setup() ) {
163
  return;
164
  }
165
+ // Enables scheduled health checks via wp-cron.
166
+ add_action( 'easyio_verification_checkin', array( $this, 'health_check' ) );
167
 
168
  // Images in post content and galleries.
169
  add_filter( 'the_content', array( $this, 'filter_the_content' ), 999999 );
276
  $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
277
  // If we don't have a domain yet, go grab one.
278
  $this->plan_id = $this->get_exactdn_option( 'plan_id' );
279
+ $new_site = false;
280
  if ( ! $this->get_exactdn_domain() ) {
281
  $this->debug_message( 'attempting to activate exactDN' );
282
  $exactdn_domain = $this->activate_site();
283
+ $new_site = true;
284
  } else {
285
  $this->debug_message( 'grabbing existing exactDN domain' );
286
  $exactdn_domain = $this->get_exactdn_domain();
288
  if ( ! $exactdn_domain ) {
289
  delete_option( $this->prefix . 'exactdn' );
290
  delete_site_option( $this->prefix . 'exactdn' );
291
+ $this->cron_setup( false );
292
  return false;
293
  }
294
+ $verified = true;
295
  // If we have a domain, verify it.
296
+ if ( $new_site ) {
297
+ $verified = $this->verify_domain( $exactdn_domain );
298
+ if ( $verified ) {
299
+ // When this is a new site that is verified, setup health check.
300
+ $this->cron_setup();
301
+ }
302
+ }
303
+ if ( $verified ) {
304
  $this->exactdn_domain = $exactdn_domain;
305
  $this->debug_message( 'exactdn_domain: ' . $exactdn_domain );
306
  $this->debug_message( 'exactdn_plan_id: ' . $this->plan_id );
310
  delete_option( $this->prefix . 'exactdn_verified' );
311
  delete_site_option( $this->prefix . 'exactdn_domain' );
312
  delete_site_option( $this->prefix . 'exactdn_verified' );
313
+ $this->cron_setup( false );
314
  return false;
315
  }
316
 
317
+ /**
318
+ * Setup wp_cron tasks for scheduled verification.
319
+ *
320
+ * @global object $wpdb
321
+ *
322
+ * @param bool $schedule True to add event, false to remove/unschedule it.
323
+ */
324
+ function cron_setup( $schedule = true ) {
325
+ $this->debug_message( '<b>' . __FUNCTION__ . '()</b>' );
326
+ $event = 'easyio_verification_checkin';
327
+ // Setup scheduled optimization if the user has enabled it, and it isn't already scheduled.
328
+ if ( $schedule && ! wp_next_scheduled( $event ) ) {
329
+ $this->debug_message( "scheduling $event" );
330
+ wp_schedule_event( time() + DAY_IN_SECONDS, apply_filters( 'easyio_verification_schedule', 'daily', $event ), $event );
331
+ } elseif ( $schedule ) {
332
+ $this->debug_message( "$event already scheduled: " . wp_next_scheduled( $event ) );
333
+ } elseif ( wp_next_scheduled( $event ) ) {
334
+ $this->debug_message( "un-scheduling $event" );
335
+ wp_clear_scheduled_hook( $event );
336
+ if ( ! function_exists( 'is_plugin_active_for_network' ) && is_multisite() ) {
337
+ // Need to include the plugin library for the is_plugin_active function.
338
+ require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
339
+ }
340
+ if ( is_multisite() && is_plugin_active_for_network( constant( strtoupper( $this->prefix ) . 'PLUGIN_FILE_REL' ) ) ) {
341
+ global $wpdb;
342
+ $blogs = $wpdb->get_results( $wpdb->prepare( "SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d", $wpdb->siteid ), ARRAY_A );
343
+ if ( ewww_image_optimizer_iterable( $blogs ) ) {
344
+ foreach ( $blogs as $blog ) {
345
+ switch_to_blog( $blog['blog_id'] );
346
+ wp_clear_scheduled_hook( $event );
347
+ restore_current_blog();
348
+ }
349
+ }
350
+ }
351
+ }
352
+ }
353
+
354
  /**
355
  * Use the Site URL to get the zone domain.
356
  */
434
  return false;
435
  }
436
 
437
+ /**
438
+ * Do a health check to verify the Easy IO domain is still good.
439
+ */
440
+ function health_check() {
441
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
442
+ $this->verify_domain( $this->exactdn_domain );
443
+ $this->set_exactdn_option( 'checkin', time() - 60 );
444
+ }
445
+
446
  /**
447
  * Verify the ExactDN domain.
448
  *
462
  }
463
 
464
  $this->check_verify_method();
465
+ $this->set_exactdn_option( 'checkin', time() + HOUR_IN_SECONDS );
466
 
467
  // Set a default error.
468
  global $exactdn_activate_error;
485
  array(
486
  'timeout' => 10,
487
  'body' => array(
488
+ 'alias' => $domain,
489
+ 'url' => $test_url,
490
+ 'origin' => $this->content_url(),
491
  ),
492
  )
493
  );
532
  array(
533
  'timeout' => 10,
534
  'body' => array(
535
+ 'alias' => $domain,
536
+ 'origin' => $this->content_url(),
537
  ),
538
  )
539
  );
1241
  $new_tag = $tag;
1242
 
1243
  // If present, replace the link href with an ExactDN URL for the full-size image.
1244
+ if ( defined( 'EIO_PRESERVE_LINKED_IMAGES' ) && EIO_PRESERVE_LINKED_IMAGES && ! empty( $images['link_url'][ $index ] ) && $this->validate_image_url( $images['link_url'][ $index ] ) ) {
1245
+ $new_tag = preg_replace(
1246
+ '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i',
1247
+ '\1' . $this->generate_url(
1248
+ $images['link_url'][ $index ],
1249
+ array(
1250
+ 'lossy' => 0,
1251
+ 'strip' => 'none',
1252
+ )
1253
+ ) . '\2',
1254
+ $new_tag,
1255
+ 1
1256
+ );
1257
+ } elseif ( ! empty( $images['link_url'][ $index ] ) && $this->validate_image_url( $images['link_url'][ $index ] ) ) {
1258
+ $new_tag = preg_replace(
1259
+ '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i',
1260
+ '\1' . $this->generate_url( $images['link_url'][ $index ] ) . '\2',
1261
+ $new_tag,
1262
+ 1
1263
+ );
1264
  }
1265
 
1266
  // Insert new image src into the srcset as well, if we have a width.
1413
  }
1414
  } // End foreach().
1415
  } // End if();
1416
+
1417
+ // Process <picture> elements in the page.
1418
+ $content = $this->filter_picture_images( $content );
1419
+
1420
+ // Process background images on HTML elements.
1421
  $element_types = apply_filters( 'eio_allowed_background_image_elements', array( 'div', 'li', 'span', 'section', 'a' ) );
1422
  foreach ( $element_types as $element_type ) {
 
1423
  $content = $this->filter_bg_images( $content, $element_type );
1424
  }
1425
  if ( $this->filtering_the_page ) {
1448
  return $content;
1449
  }
1450
 
1451
+ /**
1452
+ * Parse page content for picture elements to rewrite.
1453
+ *
1454
+ * @param string $content The HTML content to parse.
1455
+ * @return string The filtered HTML content.
1456
+ */
1457
+ function filter_picture_images( $content ) {
1458
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
1459
+ if ( false === strpos( $content, '<picture' ) ) {
1460
+ $this->debug_message( 'no picture elements, done' );
1461
+ return $content;
1462
+ }
1463
+ // Images listed as picture/source elements.
1464
+ $pictures = $this->get_picture_tags_from_html( $content );
1465
+ if ( $this->is_iterable( $pictures ) ) {
1466
+ foreach ( $pictures as $index => $picture ) {
1467
+ $sources = $this->get_elements_from_html( $picture, 'source' );
1468
+ if ( $this->is_iterable( $sources ) ) {
1469
+ foreach ( $sources as $source ) {
1470
+ $this->debug_message( "parsing a picture source: $source" );
1471
+ $srcset = $this->get_attribute( $source, 'srcset' );
1472
+ if ( $srcset ) {
1473
+ $new_srcset = $this->srcset_replace( $srcset );
1474
+ if ( $new_srcset ) {
1475
+ $new_source = str_replace( $srcset, $new_srcset, $source );
1476
+ $picture = str_replace( $source, $new_source, $picture );
1477
+ }
1478
+ }
1479
+ }
1480
+ if ( $picture !== $pictures[ $index ] ) {
1481
+ $this->debug_message( 'rewrote source for picture element' );
1482
+ $content = str_replace( $pictures[ $index ], $picture, $content );
1483
+ }
1484
+ }
1485
+ }
1486
+ }
1487
+ return $content;
1488
+ }
1489
+
1490
+ /**
1491
+ * Replaces images within a srcset attribute with their ExactDN derivatives.
1492
+ *
1493
+ * @param string $srcset A valid srcset attribute from an img element.
1494
+ * @return bool|string False if no changes were made, or the new srcset if any ExactDN images replaced the originals.
1495
+ */
1496
+ function srcset_replace( $srcset ) {
1497
+ $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
1498
+ $srcset_urls = explode( ' ', $srcset );
1499
+ $modified = false;
1500
+ if ( $this->is_iterable( $srcset_urls ) && count( $srcset_urls ) > 1 ) {
1501
+ $this->debug_message( 'parsing srcset urls' );
1502
+ foreach ( $srcset_urls as $srcurl ) {
1503
+ if ( is_numeric( substr( $srcurl, 0, 1 ) ) ) {
1504
+ continue;
1505
+ }
1506
+ $trailing = ' ';
1507
+ if ( ',' === substr( $srcurl, -1 ) ) {
1508
+ $trailing = ',';
1509
+ $srcurl = rtrim( $srcurl, ',' );
1510
+ }
1511
+ $this->debug_message( "looking for $srcurl from srcset" );
1512
+ if ( $this->validate_image_url( $srcurl ) ) {
1513
+ $srcset = str_replace( $srcurl . $trailing, $this->generate_url( $srcurl ) . $trailing, $srcset );
1514
+ $this->debug_message( "replaced $srcurl in srcset" );
1515
+ $modified = true;
1516
+ }
1517
+ }
1518
+ } elseif ( $this->validate_image_url( $srcset ) ) {
1519
+ return $this->generate_url( $srcset );
1520
+ }
1521
+ if ( $modified ) {
1522
+ return $srcset;
1523
+ } else {
1524
+ return false;
1525
+ }
1526
+ }
1527
+
1528
  /**
1529
  * Parse page content looking for elements with CSS background-image properties.
1530
  *
1787
  $started = microtime( true );
1788
  $this->debug_message( '<b>' . __METHOD__ . '()</b>' );
1789
 
1790
+ if ( is_array( $attachment_id ) || is_object( $attachment_id ) ) {
1791
+ return $image;
1792
+ }
1793
+
1794
  // Don't foul up the admin side of things, unless a plugin wants to.
1795
  if ( is_admin() &&
1796
  /**
2822
  * @return boolean True to skip the page, unchanged otherwise.
2823
  */
2824
  function skip_page( $skip = false, $uri = '' ) {
2825
+ if ( false !== strpos( $uri, 'cornerstone=' ) || false !== strpos( $uri, 'cornerstone-endpoint' ) ) {
2826
+ return true;
2827
+ }
2828
+ if ( false !== strpos( $uri, 'et_fb=' ) ) {
2829
+ return true;
2830
+ }
2831
+ if ( false !== strpos( $uri, 'tatsu=' ) ) {
2832
+ return true;
2833
+ }
2834
  if ( false !== strpos( $uri, 'ct_builder=' ) ) {
2835
  return true;
2836
  }
2837
+ if ( false !== strpos( $uri, 'ct_render_shortcode=' ) || false !== strpos( $uri, 'action=oxy_render' ) ) {
2838
+ return true;
2839
+ }
2840
  if ( false !== strpos( $uri, '?fl_builder' ) ) {
2841
  return true;
2842
  }
3047
  if ( $this->plan_id > 1 && false === strpos( $image_url, 'quality=' ) && 75 !== (int) $webp_quality && $webp_quality < $jpg_quality ) {
3048
  $more_args['quality'] = $webp_quality;
3049
  }
3050
+ if ( defined( 'EIO_WEBP_SHARP_YUV' ) && EIO_WEBP_SHARP_YUV ) {
3051
+ $more_args['sharp'] = 1;
3052
+ }
3053
  // Merge given args with the automatic (option-based) args, and also makes sure args is an array if it was previously a string.
3054
  $args = wp_parse_args( $args, $more_args );
3055
 
3157
  }
3158
 
3159
  // Clear out args for some files (like videos) that might go through image_downsize.
3160
+ if ( ! empty( $extension ) && in_array( $extension, array( 'mp4', 'm4p', 'm4v', 'mov', 'wvm', 'qt', 'ogv', 'mpg', 'mpeg', 'mpv' ), true ) ) {
3161
  $args = array();
3162
  }
3163
 
common.php CHANGED
@@ -14,7 +14,7 @@ if ( ! defined( 'ABSPATH' ) ) {
14
  exit;
15
  }
16
 
17
- define( 'EWWW_IMAGE_OPTIMIZER_VERSION', '619.0' );
18
 
19
  // Initialize a couple globals.
20
  $eio_debug = '';
@@ -50,7 +50,7 @@ use lsolesen\pel\PelTag;
50
  add_action( 'plugins_loaded', 'ewww_image_optimizer_preinit' );
51
  // Runs any checks that need to run everywhere and early.
52
  add_action( 'init', 'ewww_image_optimizer_init', 9 );
53
- // Load our front-end parsers for ExactDN and/or Alt WebP.
54
  add_action( 'init', 'ewww_image_optimizer_parser_init', 99 );
55
  // Initializes the plugin for admin interactions, like saving network settings and scheduling cron jobs.
56
  add_action( 'admin_init', 'ewww_image_optimizer_admin_init' );
@@ -212,7 +212,9 @@ if ( defined( 'WP_CLI' ) && WP_CLI ) {
212
  if ( 'done' !== get_option( 'ewww_image_optimizer_relative_migration_status' ) ) {
213
  require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewwwio-relative-migration.php' );
214
  }
215
- require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-eio-alt-webp-weglot-alt.php' );
 
 
216
 
217
  /**
218
  * Setup page parsing classes after theme functions.php is loaded and plugins have run init routines.
@@ -257,17 +259,24 @@ function ewww_image_optimizer_parser_init() {
257
  global $eio_lazy_load;
258
  $eio_lazy_load = new EIO_Lazy_Load();
259
  }
260
- // If Alt WebP Rewriting is enabled.
261
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
262
  $buffer_start = true;
263
  /**
264
  * Page Parsing class for working with HTML content.
265
  */
266
  require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-eio-page-parser.php' );
267
- /**
268
- * Alt WebP class for parsing image urls and rewriting them for WebP support.
269
- */
270
- require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-eio-alt-webp.php' );
 
 
 
 
 
 
 
271
  }
272
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_picture_webp' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
273
  $buffer_start = true;
@@ -408,6 +417,39 @@ function ewww_image_optimizer_ce_webp_enabled() {
408
  return false;
409
  }
410
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  /**
412
  * Checks to see if the WebP rules from WPFC are enabled.
413
  *
@@ -765,7 +807,7 @@ function ewww_image_optimizer_init() {
765
  }
766
  // Resizes and auto-rotates images.
767
  add_filter( 'wp_handle_upload', 'ewww_image_optimizer_handle_upload' );
768
- if ( class_exists( 'S3_Uploads' ) ) {
769
  ewwwio_debug_message( 's3-uploads detected, deferring resize_upload' );
770
  add_filter( 'ewww_image_optimizer_defer_resizing', '__return_true' );
771
  }
@@ -1299,7 +1341,9 @@ function ewww_image_optimizer_current_screen( $screen ) {
1299
  global $eio_debug;
1300
  if ( false === strpos( $screen->id, 'settings_page_ewww-image-optimizer' ) && false === strpos( $screen->id, 'settings_page_easy-image-optimizer' ) ) {
1301
  $ewwwio_temp_debug = false;
1302
- $eio_debug = '';
 
 
1303
  }
1304
  }
1305
 
@@ -1598,6 +1642,7 @@ function ewww_image_optimizer_install_table() {
1598
  }
1599
  // Make sure there are valid dates in updated column.
1600
  $wpdb->query( "UPDATE $wpdb->ewwwio_images SET updated = '1971-01-01 00:00:00' WHERE updated < '1001-01-01 00:00:01'" );
 
1601
  // Check the current collation and adjust it if necessary.
1602
  $column_collate = $wpdb->get_col_charset( $wpdb->ewwwio_images, 'path' );
1603
  if ( ! empty( $column_collate ) && ! is_wp_error( $column_collate ) && 'utf8mb4' !== $column_collate ) {
@@ -1676,7 +1721,7 @@ function ewww_image_optimizer_install_table() {
1676
  level int unsigned,
1677
  pending tinyint NOT NULL DEFAULT 0,
1678
  updates int unsigned,
1679
- updated timestamp DEFAULT '1971-01-01 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
1680
  trace blob,
1681
  $primary_key_definition
1682
  KEY path (path($path_index_size)),
@@ -1870,6 +1915,9 @@ function ewww_image_optimizer_cloud_based_media() {
1870
  if ( class_exists( 'S3_Uploads' ) && function_exists( 's3_uploads_enabled' ) && s3_uploads_enabled() ) {
1871
  return true;
1872
  }
 
 
 
1873
  if ( class_exists( 'wpCloud\StatelessMedia\EWWW' ) && function_exists( 'ud_get_stateless_media' ) ) {
1874
  $sm = ud_get_stateless_media();
1875
  if ( method_exists( $sm, 'get' ) ) {
@@ -2246,7 +2294,7 @@ function ewww_image_optimizer_notice_reoptimization() {
2246
  */
2247
  function ewww_image_optimizer_load_editor( $editors ) {
2248
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2249
- if ( class_exists( 'S3_Uploads_Image_Editor_Imagick' ) ) {
2250
  return $editors;
2251
  }
2252
  if ( ! class_exists( 'EWWWIO_GD_Editor' ) && ! class_exists( 'EWWWIO_Imagick_Editor' ) ) {
@@ -2684,7 +2732,6 @@ function ewww_image_optimizer_auto() {
2684
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2685
  if ( get_transient( 'ewww_image_optimizer_no_scheduled_optimization' ) ) {
2686
  ewwwio_debug_message( 'detected bulk operation in progress, bailing' );
2687
- ewww_image_optimizer_debug_log();
2688
  return;
2689
  }
2690
  if ( ! class_exists( 'WP_Background_Process' ) ) {
@@ -3001,7 +3048,7 @@ function ewww_image_optimizer_settings_link( $links ) {
3001
  /**
3002
  * Check for GD support of both PNG and JPG.
3003
  *
3004
- * @return bool True if full GD support is detected.
3005
  */
3006
  function ewww_image_optimizer_gd_support() {
3007
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
@@ -3014,13 +3061,86 @@ function ewww_image_optimizer_gd_support() {
3014
  }
3015
  ewwwio_memory( __FUNCTION__ );
3016
  if ( ( ! empty( $gd_support['JPEG Support'] ) || ! empty( $gd_support['JPG Support'] ) ) && ! empty( $gd_support['PNG Support'] ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3017
  return true;
3018
  }
3019
  }
3020
  }
 
3021
  return false;
3022
  }
3023
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3024
  /**
3025
  * Check for IMagick support of both PNG and JPG.
3026
  *
@@ -3040,6 +3160,110 @@ function ewww_image_optimizer_imagick_support() {
3040
  return false;
3041
  }
3042
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3043
  /**
3044
  * Check for GMagick support of both PNG and JPG.
3045
  *
@@ -3849,7 +4073,7 @@ function ewww_image_optimizer_cloud_restore_from_meta_data( $id, $gallery = 'med
3849
  }
3850
  }
3851
  }
3852
- if ( class_exists( 'S3_Uploads' ) ) {
3853
  ewww_image_optimizer_remote_push( $meta, $id );
3854
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
3855
  }
@@ -4024,7 +4248,7 @@ function ewww_image_optimizer_delete( $id ) {
4024
  }
4025
  $s3_path = false;
4026
  $s3_dir = false;
4027
- if ( class_exists( 'S3_Uploads' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ) {
4028
  $s3_path = get_attached_file( $id );
4029
  if ( 0 === strpos( $s3_path, 's3://' ) ) {
4030
  ewwwio_debug_message( 'removing: ' . $s3_path . '.webp' );
@@ -4464,7 +4688,7 @@ function ewww_image_optimizer_cloud_key_verify_ajax() {
4464
  wp_json_encode(
4465
  array(
4466
  'error' => sprintf(
4467
- /* translators: %s: an error message from the WebP self-test */
4468
  esc_html__( 'Could not validate API key, HTTP error: %s', 'ewww-image-optimizer' ),
4469
  $error_message
4470
  ),
@@ -4777,7 +5001,6 @@ function ewww_image_optimizer_cloud_optimizer( $file, $type, $convert = false, $
4777
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
4778
  wp_raise_memory_limit( 'image' );
4779
  }
4780
- ewww_image_optimizer_debug_log();
4781
  }
4782
  $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
4783
  if ( 'exceeded' === get_transient( 'ewww_image_optimizer_cloud_status' ) ) {
@@ -4837,6 +5060,7 @@ function ewww_image_optimizer_cloud_optimizer( $file, $type, $convert = false, $
4837
  $lossy = 0;
4838
  $lossy_fast = 0;
4839
  }
 
4840
  if ( 'image/webp' === $newtype ) {
4841
  $webp = 1;
4842
  $jpg_quality = apply_filters( 'webp_quality', 75, 'image/webp' );
@@ -4891,6 +5115,7 @@ function ewww_image_optimizer_cloud_optimizer( $file, $type, $convert = false, $
4891
  ewwwio_debug_message( "newfile: $newfile" );
4892
  ewwwio_debug_message( "newtype: $newtype" );
4893
  ewwwio_debug_message( "webp: $webp" );
 
4894
  ewwwio_debug_message( "jpg fill: $jpg_fill" );
4895
  ewwwio_debug_message( "jpg quality: $jpg_quality" );
4896
  $free_exec = EWWW_IMAGE_OPTIMIZER_NOEXEC && 'image/jpeg' === $type;
@@ -4930,6 +5155,7 @@ function ewww_image_optimizer_cloud_optimizer( $file, $type, $convert = false, $
4930
  'lossy' => $lossy,
4931
  'lossy_fast' => $lossy_fast,
4932
  'webp' => $webp,
 
4933
  'backup' => $hash,
4934
  'domain' => $domain,
4935
  );
@@ -5034,7 +5260,6 @@ function ewww_image_optimizer_cloud_autorotate( $file, $type ) {
5034
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
5035
  wp_raise_memory_limit( 'image' );
5036
  }
5037
- ewww_image_optimizer_debug_log();
5038
  }
5039
  $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
5040
  if ( empty( $api_key ) ) {
@@ -5161,7 +5386,6 @@ function ewww_image_optimizer_cloud_backup( $file ) {
5161
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
5162
  wp_raise_memory_limit( 'image' );
5163
  }
5164
- ewww_image_optimizer_debug_log();
5165
  }
5166
  if ( ! ewww_image_optimizer_cloud_verify( $api_key ) ) {
5167
  ewwwio_debug_message( 'cloud verify failed, image not backed up' );
@@ -5275,7 +5499,6 @@ function ewww_image_optimizer_cloud_resize( $file, $type, $dst_x, $dst_y, $src_x
5275
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
5276
  wp_raise_memory_limit( 'image' );
5277
  }
5278
- ewww_image_optimizer_debug_log();
5279
  }
5280
  $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
5281
  if ( empty( $api_key ) ) {
@@ -5779,7 +6002,7 @@ function ewww_image_optimizer_update_table( $attachment, $opt_size, $orig_size,
5779
  $updates['level'] = $ewww_image->level;
5780
  }
5781
  $updates['orig_size'] = $orig_size;
5782
- $updates['updated'] = gmdate( 'Y-m-d H:i:s' );
5783
  $ewwwdb->insert( $ewwwdb->ewwwio_images, $updates );
5784
  } else {
5785
  if ( is_array( $already_optimized ) && empty( $already_optimized['orig_size'] ) ) {
@@ -6111,7 +6334,7 @@ function ewww_image_optimizer_stream_wrapped( $filename ) {
6111
  */
6112
  function ewww_image_optimizer_remote_push( $meta, $id ) {
6113
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6114
- if ( class_exists( 'S3_Uploads' ) && ! empty( $meta['file'] ) ) {
6115
  $s3_upload_dir = wp_get_upload_dir();
6116
  $s3_upload_dir = trailingslashit( $s3_upload_dir['basedir'] );
6117
  $s3_path = get_attached_file( $id );
@@ -6202,7 +6425,8 @@ function ewww_image_optimizer_remote_fetch( $id, $meta ) {
6202
  if ( ! function_exists( 'download_url' ) ) {
6203
  require_once( ABSPATH . '/wp-admin/includes/file.php' );
6204
  }
6205
- if ( class_exists( 'S3_Uploads' ) && ! empty( $meta['file'] ) ) {
 
6206
  $s3_upload_dir = wp_get_upload_dir();
6207
  $s3_upload_dir = trailingslashit( $s3_upload_dir['basedir'] );
6208
  $s3_path = get_attached_file( $id );
@@ -6523,10 +6747,10 @@ function ewww_image_optimizer_remote_fetch( $id, $meta ) {
6523
  if ( ! empty( $filename ) && ewwwio_is_file( $filename ) ) {
6524
  ewwwio_debug_message( "$filename found, success!" );
6525
  return $filename;
6526
- } else {
6527
  ewwwio_debug_message( "$filename not found, boo..." );
6528
- return false;
6529
  }
 
6530
  }
6531
 
6532
  /**
@@ -6959,7 +7183,6 @@ function ewww_image_optimizer_resize_upload( $file ) {
6959
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
6960
  wp_raise_memory_limit( 'image' );
6961
  }
6962
- ewww_image_optimizer_debug_log();
6963
  }
6964
  if ( ! function_exists( 'wp_get_image_editor' ) ) {
6965
  ewwwio_debug_message( 'no image editor function' );
@@ -8003,7 +8226,7 @@ function ewww_image_optimizer_resize_from_meta_data( $meta, $id = null, $log = t
8003
  ewwwio_debug_message( 'uploading to Amazon S3' );
8004
  }
8005
  }
8006
- if ( class_exists( 'S3_Uploads' ) ) {
8007
  ewww_image_optimizer_remote_push( $meta, $id );
8008
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
8009
  }
@@ -8323,8 +8546,6 @@ function ewww_image_optimizer_update_attachment( $meta, $id ) {
8323
  'post_mime_type' => $mime,
8324
  )
8325
  );
8326
- ewww_image_optimizer_debug_log();
8327
- ewwwio_memory( __FUNCTION__ );
8328
  return $meta;
8329
  }
8330
 
@@ -8699,6 +8920,34 @@ function ewww_image_optimizer_is_animated_png( $filename ) {
8699
  return $apng;
8700
  }
8701
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8702
  /**
8703
  * Count how many sizes are in the metadata, accounting for those with duplicate dimensions.
8704
  *
@@ -8809,6 +9058,10 @@ function ewww_image_optimizer_custom_column( $column_name, $id, $meta = null ) {
8809
  echo '<div>' . esc_html__( 'Amazon S3 image', 'ewww-image-optimizer' ) . '</div>';
8810
  $ewww_cdn = true;
8811
  }
 
 
 
 
8812
  if ( is_array( $meta ) & class_exists( 'wpCloud\StatelessMedia\EWWW' ) && ! empty( $meta['gs_link'] ) ) {
8813
  echo '<div>' . esc_html__( 'WP Stateless image', 'ewww-image-optimizer' ) . '</div>';
8814
  $ewww_cdn = true;
@@ -8933,6 +9186,15 @@ function ewww_image_optimizer_custom_column( $column_name, $id, $meta = null ) {
8933
  ) . '</div>';
8934
  }
8935
  break;
 
 
 
 
 
 
 
 
 
8936
  default:
8937
  // Not a supported mimetype.
8938
  $msg = '<div>' . esc_html__( 'Unsupported file type', 'ewww-image-optimizer' ) . '</div>';
@@ -9181,8 +9443,6 @@ function ewww_image_optimizer_custom_column( $column_name, $id, $meta = null ) {
9181
  } // End if().
9182
  echo '</div>';
9183
  } // End if().
9184
- ewwwio_memory( __FUNCTION__ );
9185
- ewww_image_optimizer_debug_log();
9186
  }
9187
 
9188
  /**
@@ -9253,7 +9513,6 @@ function ewww_image_optimizer_count_unoptimized_sizes( $sizes ) {
9253
  $processed = array();
9254
  foreach ( $sizes as $size => $data ) {
9255
  ewwwio_debug_message( "checking for size: $size" );
9256
- ewww_image_optimizer_debug_log();
9257
  if ( strpos( $size, 'webp' ) === 0 ) {
9258
  continue;
9259
  }
@@ -10599,7 +10858,7 @@ function ewwwio_debug_info() {
10599
  ewwwio_debug_message( 'png2jpg fill:' );
10600
  ewww_image_optimizer_jpg_background();
10601
  ewwwio_debug_message( 'webp conversion: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ? 'on' : 'off' ) );
10602
- ewwwio_debug_message( 'alt webp rewriting: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) ? 'on' : 'off' ) );
10603
  ewwwio_debug_message( 'picture webp rewriting: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_picture_webp' ) ? 'on' : 'off' ) );
10604
  ewwwio_debug_message( 'WebP Rewrite exclusions:' );
10605
  $webp_exclude_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_rewrite_exclude' ) ? esc_html( implode( "\n", ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_rewrite_exclude' ) ) ) : '';
@@ -10622,6 +10881,7 @@ function ewwwio_debug_info() {
10622
  ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) &&
10623
  ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_picture_webp' ) &&
10624
  ! ewww_image_optimizer_ce_webp_enabled() &&
 
10625
  ! ewww_image_optimizer_easy_active()
10626
  ) {
10627
  if ( defined( 'PHP_SAPI' ) ) {
@@ -10664,7 +10924,7 @@ function ewww_image_optimizer_intro_wizard() {
10664
  $eio_base = new EIO_Base();
10665
  $easyio_site_url = $eio_base->content_url();
10666
  $no_tracking = false;
10667
- $webp_available = true;
10668
  $bulk_available = false;
10669
  $tools_available = true;
10670
  if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) ) {
@@ -10714,12 +10974,6 @@ function ewww_image_optimizer_intro_wizard() {
10714
  // Expand the missing utilities list for use in the error message.
10715
  $tools_missing_message = implode( ', ', $tools_missing );
10716
  }
10717
- if (
10718
- defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) && EWWW_IMAGE_OPTIMIZER_NOEXEC &&
10719
- ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' )
10720
- ) {
10721
- $webp_available = false;
10722
- }
10723
  if (
10724
  ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ||
10725
  ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ||
@@ -10761,7 +11015,6 @@ function ewww_image_optimizer_intro_wizard() {
10761
  }
10762
  if ( $tools_missing_notice && ! $tools_available ) {
10763
  ewww_image_optimizer_enable_free_exec();
10764
- $webp_available = false;
10765
  }
10766
  }
10767
  if ( 3 === $wizard_step ) {
@@ -10841,7 +11094,9 @@ function ewww_image_optimizer_intro_wizard() {
10841
  );
10842
  ?>
10843
  </span>
10844
- <?php if ( empty( ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) ) : ?>
 
 
10845
  <br><br>
10846
  <a href="<?php echo esc_url( add_query_arg( 'site_url', trim( $easyio_site_url ), 'https://ewww.io/manage-sites/' ) ); ?>" target="_blank">
10847
  <?php esc_html_e( 'First, add your Site URL to your account:', 'easy-image-optimizer' ); ?>
@@ -10852,7 +11107,7 @@ function ewww_image_optimizer_intro_wizard() {
10852
  <a id='ewwwio-easy-activate' href='#' class='button-secondary'><?php esc_html_e( 'Activate', 'ewww-image-optimizer' ); ?></a>
10853
  <span id='ewwwio-easy-activation-processing'><img src='<?php echo esc_url( $loading_image_url ); ?>' alt='loading'/></span>
10854
  <?php elseif ( class_exists( 'ExactDN' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) && $exactdn->get_exactdn_domain() && $exactdn->verify_domain( $exactdn->get_exactdn_domain() ) ) : ?>
10855
- <span style="color: #3eadc9; font-weight: bolder"><?php esc_html_e( 'Verified', 'ewww-image-optimizer' ); ?></span>
10856
  <span class="dashicons dashicons-yes"></span>
10857
  <?php endif; ?>
10858
  </p>
@@ -11616,7 +11871,7 @@ function ewww_image_optimizer_options( $network = 'singlesite' ) {
11616
  sprintf(
11617
  /* translators: %s: S3 Image Optimizer (link) */
11618
  esc_html__( 'Optimize unlimited Amazon S3 buckets with our %s.' ),
11619
- '<a href="https://ewww.io/downloads/s3-image-optimizer/">' . esc_html__( 'S3 Image Optimizer', 'ewww-image-optimizer' ) . '</a>'
11620
  ) : '' );
11621
  ?>
11622
  </p>
@@ -11740,6 +11995,7 @@ function ewww_image_optimizer_options( $network = 'singlesite' ) {
11740
  if ( ewww_image_optimizer_easy_active() || $cf_host ) {
11741
  ewww_image_optimizer_webp_rewrite_verify();
11742
  }
 
11743
  $test_webp_image = plugins_url( '/images/test.png.webp', __FILE__ );
11744
  $test_png_image = plugins_url( '/images/test.png', __FILE__ );
11745
  ?>
@@ -11829,9 +12085,6 @@ function ewww_image_optimizer_options( $network = 'singlesite' ) {
11829
  </tr>
11830
  </table>
11831
  <input type='hidden' id='ewww_image_optimizer_allow_multisite_override_active' name='ewww_image_optimizer_allow_multisite_override_active' value='0'>
11832
- <?php if ( get_site_option( 'ewww_image_optimizer_cloud_key' ) ) : ?>
11833
- <input type='hidden' id='ewww_image_optimizer_cloud_key' name='ewww_image_optimizer_cloud_key' value='<?php echo esc_attr( get_site_option( 'ewww_image_optimizer_cloud_key' ) ); ?>' />
11834
- <?php endif; ?>
11835
  </div><!-- end container general settings -->
11836
  <p class='submit'><input type='submit' class='button-primary' value='<?php esc_attr_e( 'Save Changes', 'ewww-image-optimizer' ); ?>' /></p>
11837
  </form>
@@ -11854,7 +12107,6 @@ function ewww_image_optimizer_options( $network = 'singlesite' ) {
11854
  <a class='button-secondary' href='<?php echo esc_url( admin_url( 'admin.php?action=ewww_image_optimizer_remove_cloud_key' ) ); ?>'>
11855
  <?php esc_html_e( 'Remove API key', 'ewww-image-optimizer' ); ?>
11856
  </a>
11857
- <input type='hidden' id='ewww_image_optimizer_cloud_key' name='ewww_image_optimizer_cloud_key' value='<?php echo esc_attr( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ); ?>' />
11858
  <p>
11859
  <?php if ( false !== strpos( $verify_cloud, 'great' ) ) : ?>
11860
  <span style="color: #3eadc9; font-weight: bolder"><?php esc_html_e( 'Verified,', 'ewww-image-optimizer' ); ?> </span><?php echo wp_kses_post( ewww_image_optimizer_cloud_quota() ); ?>
@@ -11921,6 +12173,8 @@ function ewww_image_optimizer_options( $network = 'singlesite' ) {
11921
  ?>
11922
  <?php if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) : ?>
11923
  <p style='color: red'><?php esc_html_e( 'Inactive, please disable the Image Accelerator option on the Jetpack Dashboard.', 'ewww-image-optimizer' ); ?></p>
 
 
11924
  <?php elseif ( ! $exactdn_enabled ) : ?>
11925
  <p>
11926
  <strong><a href="https://ewww.io/plans/" target="_blank">
@@ -12172,7 +12426,7 @@ function ewww_image_optimizer_options( $network = 'singlesite' ) {
12172
  </tr>
12173
  <?php endif; ?>
12174
  <?php endif; ?>
12175
- <?php if ( $free_exec ) : ?>
12176
  <tr id='ewww_image_optimizer_webp_container'>
12177
  <th scope='row'>
12178
  <label for='ewww_image_optimizer_webp'><?php esc_html_e( 'WebP Conversion', 'ewww-image-optimizer' ); ?></label>
@@ -12219,7 +12473,42 @@ function ewww_image_optimizer_options( $network = 'singlesite' ) {
12219
  </tr>
12220
  <tr>
12221
  <?php endif; ?>
12222
- <?php if ( ! $free_exec && ! ewww_image_optimizer_ce_webp_enabled() && ! ewww_image_optimizer_easy_active() ) : ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12223
  <tr class='ewww_image_optimizer_webp_setting_container' <?php echo ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ? '' : ' style="display:none"'; ?>>
12224
  <th scope='row'>
12225
  <?php esc_html_e( 'WebP Delivery Method', 'ewww-image-optimizer' ); ?>
@@ -12437,32 +12726,6 @@ AddType image/webp .webp</pre>
12437
  </td>
12438
  </tr>
12439
  <?php endif; ?>
12440
- <tr id='ewww_image_optimizer_webp_easyio_container' style='display:none;'>
12441
- <th scope='row'>
12442
- <label for='ewww_image_optimizer_webp'><?php esc_html_e( 'WebP Conversion', 'ewww-image-optimizer' ); ?></label>
12443
- </th>
12444
- <td>
12445
- <p class='description'><?php esc_html_e( 'WebP images are served automatically by Easy IO.', 'ewww-image-optimizer' ); ?></p>
12446
- </td>
12447
- </tr>
12448
- <?php elseif ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) : ?>
12449
- <tr id='ewww_image_optimizer_webp_easyio_container'>
12450
- <th scope='row'>
12451
- <label for='ewww_image_optimizer_webp'><?php esc_html_e( 'WebP Conversion', 'ewww-image-optimizer' ); ?></label>
12452
- </th>
12453
- <td>
12454
- <p class='description'><?php esc_html_e( 'WebP images are served automatically by Easy IO.', 'ewww-image-optimizer' ); ?></p>
12455
- </td>
12456
- </tr>
12457
- <?php elseif ( get_option( 'easyio_exactdn' ) ) : ?>
12458
- <tr>
12459
- <th scope='row'>
12460
- <label for='ewww_image_optimizer_webp'><?php esc_html_e( 'WebP Conversion', 'ewww-image-optimizer' ); ?></label>
12461
- </th>
12462
- <td>
12463
- <p class='description'><?php esc_html_e( 'WebP images are served automatically by Easy Image Optimizer.', 'ewww-image-optimizer' ); ?></p>
12464
- </td>
12465
- </tr>
12466
  <?php endif; ?>
12467
 
12468
  <?php if ( class_exists( 'Cloudinary' ) && Cloudinary::config_get( 'api_secret' ) ) : ?>
@@ -13062,6 +13325,7 @@ AddType image/webp .webp</pre>
13062
  <p class='submit'><input type='submit' class='button-primary' value='<?php esc_attr_e( 'Save Changes', 'ewww-image-optimizer' ); ?>' /></p>
13063
  </form>
13064
 
 
13065
  <hr style='clear:both;'>
13066
  <h2><?php esc_html_e( "Shane's Recommendations", 'ewww-image-optimizer' ); ?></h2>
13067
  <p><?php esc_html_e( 'These are products I have personally used. An * indicates an affiliate link so you can support future development of EWWW IO.', 'ewww-image-optimizer' ); ?></p>
@@ -13090,6 +13354,7 @@ AddType image/webp .webp</pre>
13090
  <a href="https://nodeping.com?rid=201407082225W862K">NodePing</a>* -
13091
  <?php esc_html_e( 'Monitor all the things, and make sure they stay online. They have a fantastic array of monitors and notifications that you can setup, they are cheap, simple, and have great support.', 'ewww-image-optimizer' ); ?>
13092
  </p>
 
13093
  </div><!-- end container wrap -->
13094
  <?php
13095
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help' ) ) {
@@ -13228,6 +13493,8 @@ function ewww_image_optimizer_remove_easyio() {
13228
  delete_site_option( 'ewww_image_optimizer_exactdn_verified' );
13229
  delete_site_option( 'ewww_image_optimizer_exactdn_validation' );
13230
  delete_site_option( 'ewww_image_optimizer_exactdn_suspended' );
 
 
13231
  $sendback = wp_get_referer();
13232
  wp_safe_redirect( $sendback );
13233
  exit;
@@ -13398,8 +13665,10 @@ function ewww_image_optimizer_debug_log() {
13398
  $eio_debug = str_replace( '<br>', "\n", $eio_debug );
13399
  file_put_contents( $debug_log, $timestamp . $eio_debug, FILE_APPEND );
13400
  }
 
 
 
13401
  }
13402
- $eio_debug = '';
13403
  ewwwio_memory( __FUNCTION__ );
13404
  }
13405
 
14
  exit;
15
  }
16
 
17
+ define( 'EWWW_IMAGE_OPTIMIZER_VERSION', '620' );
18
 
19
  // Initialize a couple globals.
20
  $eio_debug = '';
50
  add_action( 'plugins_loaded', 'ewww_image_optimizer_preinit' );
51
  // Runs any checks that need to run everywhere and early.
52
  add_action( 'init', 'ewww_image_optimizer_init', 9 );
53
+ // Load our front-end parsers for ExactDN, Lazy Load and WebP.
54
  add_action( 'init', 'ewww_image_optimizer_parser_init', 99 );
55
  // Initializes the plugin for admin interactions, like saving network settings and scheduling cron jobs.
56
  add_action( 'admin_init', 'ewww_image_optimizer_admin_init' );
212
  if ( 'done' !== get_option( 'ewww_image_optimizer_relative_migration_status' ) ) {
213
  require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-ewwwio-relative-migration.php' );
214
  }
215
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_ALT_WEBP' ) && EWWW_IMAGE_OPTIMIZER_ALT_WEBP ) {
216
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-eio-alt-webp-weglot-alt.php' );
217
+ }
218
 
219
  /**
220
  * Setup page parsing classes after theme functions.php is loaded and plugins have run init routines.
259
  global $eio_lazy_load;
260
  $eio_lazy_load = new EIO_Lazy_Load();
261
  }
262
+ // If JS WebP Rewriting is enabled.
263
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
264
  $buffer_start = true;
265
  /**
266
  * Page Parsing class for working with HTML content.
267
  */
268
  require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-eio-page-parser.php' );
269
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_ALT_WEBP' ) && EWWW_IMAGE_OPTIMIZER_ALT_WEBP ) {
270
+ /**
271
+ * Alt WebP class for parsing image urls and rewriting them for WebP support.
272
+ */
273
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-eio-alt-webp.php' );
274
+ } else {
275
+ /**
276
+ * JS WebP class for parsing image urls and rewriting them for WebP support.
277
+ */
278
+ require_once( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'classes/class-eio-js-webp.php' );
279
+ }
280
  }
281
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_picture_webp' ) && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
282
  $buffer_start = true;
417
  return false;
418
  }
419
 
420
+ /**
421
+ * Checks to see if the WebP option from the SWIS Performance plugin is enabled.
422
+ *
423
+ * @return bool True if the WebP option for SWIS is enabled.
424
+ */
425
+ function ewww_image_optimizer_swis_webp_enabled() {
426
+ if ( function_exists( 'swis' ) && class_exists( '\SWIS\Cache' ) ) {
427
+ $cache_settings = swis()->cache->get_settings();
428
+ if ( swis()->settings->get_option( 'cache' ) && ! empty( $cache_settings['webp'] ) ) {
429
+ ewwwio_debug_message( 'SWIS WebP option enabled' );
430
+ return true;
431
+ }
432
+ }
433
+ return false;
434
+ }
435
+
436
+ /**
437
+ * Checks to see if there is a method available for WebP conversion.
438
+ *
439
+ * @return bool True if a WebP Convertor is available.
440
+ */
441
+ function ewww_image_optimizer_webp_available() {
442
+ if (
443
+ defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) && EWWW_IMAGE_OPTIMIZER_NOEXEC &&
444
+ ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) &&
445
+ ! ewww_image_optimizer_imagick_supports_webp() &&
446
+ ! ewww_image_optimizer_gd_supports_webp()
447
+ ) {
448
+ return true;
449
+ }
450
+ return true;
451
+ }
452
+
453
  /**
454
  * Checks to see if the WebP rules from WPFC are enabled.
455
  *
807
  }
808
  // Resizes and auto-rotates images.
809
  add_filter( 'wp_handle_upload', 'ewww_image_optimizer_handle_upload' );
810
+ if ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) {
811
  ewwwio_debug_message( 's3-uploads detected, deferring resize_upload' );
812
  add_filter( 'ewww_image_optimizer_defer_resizing', '__return_true' );
813
  }
1341
  global $eio_debug;
1342
  if ( false === strpos( $screen->id, 'settings_page_ewww-image-optimizer' ) && false === strpos( $screen->id, 'settings_page_easy-image-optimizer' ) ) {
1343
  $ewwwio_temp_debug = false;
1344
+ if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_debug' ) ) {
1345
+ $eio_debug = '';
1346
+ }
1347
  }
1348
  }
1349
 
1642
  }
1643
  // Make sure there are valid dates in updated column.
1644
  $wpdb->query( "UPDATE $wpdb->ewwwio_images SET updated = '1971-01-01 00:00:00' WHERE updated < '1001-01-01 00:00:01'" );
1645
+ $wpdb->query( "ALTER TABLE $wpdb->ewwwio_images ALTER updated SET DEFAULT CURRENT_TIMESTAMP" );
1646
  // Check the current collation and adjust it if necessary.
1647
  $column_collate = $wpdb->get_col_charset( $wpdb->ewwwio_images, 'path' );
1648
  if ( ! empty( $column_collate ) && ! is_wp_error( $column_collate ) && 'utf8mb4' !== $column_collate ) {
1721
  level int unsigned,
1722
  pending tinyint NOT NULL DEFAULT 0,
1723
  updates int unsigned,
1724
+ updated timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
1725
  trace blob,
1726
  $primary_key_definition
1727
  KEY path (path($path_index_size)),
1915
  if ( class_exists( 'S3_Uploads' ) && function_exists( 's3_uploads_enabled' ) && s3_uploads_enabled() ) {
1916
  return true;
1917
  }
1918
+ if ( class_exists( 'S3_Uploads\Plugin' ) && function_exists( 'S3_Uploads\enabled' ) && \S3_Uploads\enabled() ) {
1919
+ return true;
1920
+ }
1921
  if ( class_exists( 'wpCloud\StatelessMedia\EWWW' ) && function_exists( 'ud_get_stateless_media' ) ) {
1922
  $sm = ud_get_stateless_media();
1923
  if ( method_exists( $sm, 'get' ) ) {
2294
  */
2295
  function ewww_image_optimizer_load_editor( $editors ) {
2296
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2297
+ if ( class_exists( 'S3_Uploads\Image_Editor_Imagick' ) ) {
2298
  return $editors;
2299
  }
2300
  if ( ! class_exists( 'EWWWIO_GD_Editor' ) && ! class_exists( 'EWWWIO_Imagick_Editor' ) ) {
2732
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
2733
  if ( get_transient( 'ewww_image_optimizer_no_scheduled_optimization' ) ) {
2734
  ewwwio_debug_message( 'detected bulk operation in progress, bailing' );
 
2735
  return;
2736
  }
2737
  if ( ! class_exists( 'WP_Background_Process' ) ) {
3048
  /**
3049
  * Check for GD support of both PNG and JPG.
3050
  *
3051
+ * @return string The version of GD if full support is detected.
3052
  */
3053
  function ewww_image_optimizer_gd_support() {
3054
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3061
  }
3062
  ewwwio_memory( __FUNCTION__ );
3063
  if ( ( ! empty( $gd_support['JPEG Support'] ) || ! empty( $gd_support['JPG Support'] ) ) && ! empty( $gd_support['PNG Support'] ) ) {
3064
+ return ! empty( $gd_support['GD Version'] ) ? $gd_support['GD Version'] : '1';
3065
+ }
3066
+ }
3067
+ }
3068
+ return false;
3069
+ }
3070
+
3071
+ /**
3072
+ * Check for GD support of WebP format.
3073
+ *
3074
+ * @return bool True if proper WebP support is detected.
3075
+ */
3076
+ function ewww_image_optimizer_gd_supports_webp() {
3077
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3078
+ $gd_version = ewww_image_optimizer_gd_support();
3079
+ if ( $gd_version ) {
3080
+ if (
3081
+ function_exists( 'imagewebp' ) &&
3082
+ function_exists( 'imagepalettetotruecolor' ) &&
3083
+ function_exists( 'imageistruecolor' ) &&
3084
+ function_exists( 'imagealphablending' ) &&
3085
+ function_exists( 'imagesavealpha' )
3086
+ ) {
3087
+ if ( version_compare( $gd_version, '2.2.5', '>=' ) ) {
3088
+ ewwwio_debug_message( 'yes it does' );
3089
  return true;
3090
  }
3091
  }
3092
  }
3093
+ ewwwio_debug_message( 'sorry nope' );
3094
  return false;
3095
  }
3096
 
3097
+ /**
3098
+ * Use GD to convert an image to WebP.
3099
+ *
3100
+ * @param string $file The original source image path.
3101
+ * @param string $type The mime-type of the original image.
3102
+ * @param string $webpfile The location to store the new WebP image.
3103
+ */
3104
+ function ewww_image_optimizer_gd_create_webp( $file, $type, $webpfile ) {
3105
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3106
+ $quality = (int) apply_filters( 'webp_quality', 75, 'image/webp' );
3107
+ if ( $quality < 50 || $quality > 100 ) {
3108
+ $quality = 75;
3109
+ }
3110
+ switch ( $type ) {
3111
+ case 'image/jpeg':
3112
+ $image = imagecreatefromjpeg( $file );
3113
+ if ( false === $image ) {
3114
+ return;
3115
+ }
3116
+ break;
3117
+ case 'image/png':
3118
+ $image = imagecreatefrompng( $file );
3119
+ if ( false === $image ) {
3120
+ return;
3121
+ }
3122
+ if ( ! imageistruecolor( $image ) ) {
3123
+ ewwwio_debug_message( 'converting to true color' );
3124
+ imagepalettetotruecolor( $image );
3125
+ }
3126
+ if ( ewww_image_optimizer_png_alpha( $file ) ) {
3127
+ ewwwio_debug_message( 'saving alpha and disabling alpha blending' );
3128
+ imagealphablending( $image, false );
3129
+ imagesavealpha( $image, true );
3130
+ }
3131
+ if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP' ) || ! EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP ) {
3132
+ $quality = 100;
3133
+ }
3134
+ break;
3135
+ default:
3136
+ return;
3137
+ }
3138
+ ewwwio_debug_message( "creating $webpfile with quality $quality" );
3139
+ $result = imagewebp( $image, $webpfile, $quality );
3140
+ // Make sure to cleanup--if $webpfile is borked, that will be handled elsewhere.
3141
+ imagedestroy( $image );
3142
+ }
3143
+
3144
  /**
3145
  * Check for IMagick support of both PNG and JPG.
3146
  *
3160
  return false;
3161
  }
3162
 
3163
+ /**
3164
+ * Check for IMagick support of WebP.
3165
+ *
3166
+ * @return bool True if WebP support is detected.
3167
+ */
3168
+ function ewww_image_optimizer_imagick_supports_webp() {
3169
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3170
+ if ( ewww_image_optimizer_imagick_support() ) {
3171
+ $imagick = new Imagick();
3172
+ $formats = $imagick->queryFormats();
3173
+ if ( in_array( 'WEBP', $formats, true ) ) {
3174
+ ewwwio_debug_message( 'yes it does' );
3175
+ return true;
3176
+ }
3177
+ }
3178
+ ewwwio_debug_message( 'sorry nope' );
3179
+ return false;
3180
+ }
3181
+
3182
+ /**
3183
+ * Use IMagick to convert an image to WebP.
3184
+ *
3185
+ * @param string $file The original source image path.
3186
+ * @param string $type The mime-type of the original image.
3187
+ * @param string $webpfile The location to store the new WebP image.
3188
+ */
3189
+ function ewww_image_optimizer_imagick_create_webp( $file, $type, $webpfile ) {
3190
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
3191
+ $sharp_yuv = defined( 'EIO_WEBP_SHARP_YUV' ) && EIO_WEBP_SHARP_YUV ? true : false;
3192
+ $quality = (int) apply_filters( 'webp_quality', 75, 'image/webp' );
3193
+ if ( $quality < 50 || $quality > 100 ) {
3194
+ $quality = 75;
3195
+ }
3196
+ $profiles = array();
3197
+ switch ( $type ) {
3198
+ case 'image/jpeg':
3199
+ $image = new Imagick( $file );
3200
+ if ( false === $image ) {
3201
+ return;
3202
+ }
3203
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) ) {
3204
+ // Getting possible color profiles.
3205
+ $profiles = $image->getImageProfiles( 'icc', true );
3206
+ }
3207
+ $color = $image->getImageColorspace();
3208
+ ewwwio_debug_message( "color space is $color" );
3209
+ if ( Imagick::COLORSPACE_CMYK === $color ) {
3210
+ ewwwio_debug_message( 'found CMYK image' );
3211
+ if ( ewwwio_is_file( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'vendor/icc/sRGB2014.icc' ) ) {
3212
+ ewwwio_debug_message( 'adding icc profile' );
3213
+ $icc_profile = file_get_contents( EWWW_IMAGE_OPTIMIZER_PLUGIN_PATH . 'vendor/icc/sRGB2014.icc' );
3214
+ $image->profileImage( 'icc', $icc_profile );
3215
+ }
3216
+ ewwwio_debug_message( 'attempting SRGB transform' );
3217
+ $image->transformImageColorspace( Imagick::COLORSPACE_SRGB );
3218
+ ewwwio_debug_message( 'removing icc profile' );
3219
+ $image->setImageProfile( 'icc', null );
3220
+ $profiles = array();
3221
+ }
3222
+ $image->setImageFormat( 'WEBP' );
3223
+ if ( $sharp_yuv ) {
3224
+ ewwwio_debug_message( 'enabling sharp_yuv' );
3225
+ $image->setOption( 'webp:use-sharp-yuv', 'true' );
3226
+ }
3227
+ ewwwio_debug_message( "setting quality to $quality" );
3228
+ $image->setImageCompressionQuality( $quality );
3229
+ break;
3230
+ case 'image/png':
3231
+ $image = new Imagick( $file );
3232
+ if ( false === $image ) {
3233
+ return;
3234
+ }
3235
+ $image->setImageFormat( 'WEBP' );
3236
+ if ( defined( 'EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP' ) && EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP ) {
3237
+ ewwwio_debug_message( 'doing lossy conversion' );
3238
+ if ( $sharp_yuv ) {
3239
+ ewwwio_debug_message( 'enabling sharp_yuv' );
3240
+ $image->setOption( 'webp:use-sharp-yuv', 'true' );
3241
+ }
3242
+ ewwwio_debug_message( "setting quality to $quality" );
3243
+ $image->setImageCompressionQuality( $quality );
3244
+ } else {
3245
+ ewwwio_debug_message( 'sticking to lossless' );
3246
+ $image->setOption( 'webp:lossless', true );
3247
+ $image->setOption( 'webp:alpha-quality', 100 );
3248
+ }
3249
+ break;
3250
+ default:
3251
+ return;
3252
+ }
3253
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) ) {
3254
+ ewwwio_debug_message( 'removing meta' );
3255
+ $image->stripImage();
3256
+ if ( ! empty( $profiles ) ) {
3257
+ ewwwio_debug_message( 'adding color profile to WebP' );
3258
+ $image->profileImage( 'icc', $profiles['icc'] );
3259
+ }
3260
+ }
3261
+ ewwwio_debug_message( 'getting blob' );
3262
+ $image_blob = $image->getImageBlob();
3263
+ ewwwio_debug_message( 'writing file' );
3264
+ file_put_contents( $webpfile, $image_blob );
3265
+ }
3266
+
3267
  /**
3268
  * Check for GMagick support of both PNG and JPG.
3269
  *
4073
  }
4074
  }
4075
  }
4076
+ if ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_uploads\Plugin' ) ) {
4077
  ewww_image_optimizer_remote_push( $meta, $id );
4078
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
4079
  }
4248
  }
4249
  $s3_path = false;
4250
  $s3_dir = false;
4251
+ if ( ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ) {
4252
  $s3_path = get_attached_file( $id );
4253
  if ( 0 === strpos( $s3_path, 's3://' ) ) {
4254
  ewwwio_debug_message( 'removing: ' . $s3_path . '.webp' );
4688
  wp_json_encode(
4689
  array(
4690
  'error' => sprintf(
4691
+ /* translators: %s: an HTTP error message */
4692
  esc_html__( 'Could not validate API key, HTTP error: %s', 'ewww-image-optimizer' ),
4693
  $error_message
4694
  ),
5001
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
5002
  wp_raise_memory_limit( 'image' );
5003
  }
 
5004
  }
5005
  $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
5006
  if ( 'exceeded' === get_transient( 'ewww_image_optimizer_cloud_status' ) ) {
5060
  $lossy = 0;
5061
  $lossy_fast = 0;
5062
  }
5063
+ $sharp_yuv = defined( 'EIO_WEBP_SHARP_YUV' ) && EIO_WEBP_SHARP_YUV ? 1 : 0;
5064
  if ( 'image/webp' === $newtype ) {
5065
  $webp = 1;
5066
  $jpg_quality = apply_filters( 'webp_quality', 75, 'image/webp' );
5115
  ewwwio_debug_message( "newfile: $newfile" );
5116
  ewwwio_debug_message( "newtype: $newtype" );
5117
  ewwwio_debug_message( "webp: $webp" );
5118
+ ewwwio_debug_message( "sharp_yuv: $sharp_yuv" );
5119
  ewwwio_debug_message( "jpg fill: $jpg_fill" );
5120
  ewwwio_debug_message( "jpg quality: $jpg_quality" );
5121
  $free_exec = EWWW_IMAGE_OPTIMIZER_NOEXEC && 'image/jpeg' === $type;
5155
  'lossy' => $lossy,
5156
  'lossy_fast' => $lossy_fast,
5157
  'webp' => $webp,
5158
+ 'sharp_yuv' => $sharp_yuv,
5159
  'backup' => $hash,
5160
  'domain' => $domain,
5161
  );
5260
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
5261
  wp_raise_memory_limit( 'image' );
5262
  }
 
5263
  }
5264
  $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
5265
  if ( empty( $api_key ) ) {
5386
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
5387
  wp_raise_memory_limit( 'image' );
5388
  }
 
5389
  }
5390
  if ( ! ewww_image_optimizer_cloud_verify( $api_key ) ) {
5391
  ewwwio_debug_message( 'cloud verify failed, image not backed up' );
5499
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
5500
  wp_raise_memory_limit( 'image' );
5501
  }
 
5502
  }
5503
  $api_key = ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' );
5504
  if ( empty( $api_key ) ) {
6002
  $updates['level'] = $ewww_image->level;
6003
  }
6004
  $updates['orig_size'] = $orig_size;
6005
+ /* $updates['updated'] = gmdate( 'Y-m-d H:i:s' ); */
6006
  $ewwwdb->insert( $ewwwdb->ewwwio_images, $updates );
6007
  } else {
6008
  if ( is_array( $already_optimized ) && empty( $already_optimized['orig_size'] ) ) {
6334
  */
6335
  function ewww_image_optimizer_remote_push( $meta, $id ) {
6336
  ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
6337
+ if ( ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) && ! empty( $meta['file'] ) ) {
6338
  $s3_upload_dir = wp_get_upload_dir();
6339
  $s3_upload_dir = trailingslashit( $s3_upload_dir['basedir'] );
6340
  $s3_path = get_attached_file( $id );
6425
  if ( ! function_exists( 'download_url' ) ) {
6426
  require_once( ABSPATH . '/wp-admin/includes/file.php' );
6427
  }
6428
+ $filename = false;
6429
+ if ( ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) && ! empty( $meta['file'] ) ) {
6430
  $s3_upload_dir = wp_get_upload_dir();
6431
  $s3_upload_dir = trailingslashit( $s3_upload_dir['basedir'] );
6432
  $s3_path = get_attached_file( $id );
6747
  if ( ! empty( $filename ) && ewwwio_is_file( $filename ) ) {
6748
  ewwwio_debug_message( "$filename found, success!" );
6749
  return $filename;
6750
+ } elseif ( ! empty( $filename ) ) {
6751
  ewwwio_debug_message( "$filename not found, boo..." );
 
6752
  }
6753
+ return false;
6754
  }
6755
 
6756
  /**
7183
  add_filter( 'image_memory_limit', 'ewww_image_optimizer_raise_memory_limit' );
7184
  wp_raise_memory_limit( 'image' );
7185
  }
 
7186
  }
7187
  if ( ! function_exists( 'wp_get_image_editor' ) ) {
7188
  ewwwio_debug_message( 'no image editor function' );
8226
  ewwwio_debug_message( 'uploading to Amazon S3' );
8227
  }
8228
  }
8229
+ if ( class_exists( 'S3_Uploads' ) || class_exists( 'S3_Uploads\Plugin' ) ) {
8230
  ewww_image_optimizer_remote_push( $meta, $id );
8231
  ewwwio_debug_message( 're-uploading to S3(_Uploads)' );
8232
  }
8546
  'post_mime_type' => $mime,
8547
  )
8548
  );
 
 
8549
  return $meta;
8550
  }
8551
 
8920
  return $apng;
8921
  }
8922
 
8923
+ /**
8924
+ * Check a JPG to see if it uses the CMYK color space.
8925
+ *
8926
+ * @param string $filename Name of the JPG to test.
8927
+ * @return bool True if CMYK, false otherwise.
8928
+ */
8929
+ function ewww_image_optimizer_is_cmyk( $filename ) {
8930
+ ewwwio_debug_message( '<b>' . __FUNCTION__ . '()</b>' );
8931
+ if ( ewww_image_optimizer_imagick_support() ) {
8932
+ $image = new Imagick( $filename );
8933
+ $color = $image->getImageColorspace();
8934
+ ewwwio_debug_message( "color space is $color" );
8935
+ $image->destroy();
8936
+ if ( Imagick::COLORSPACE_CMYK === $color ) {
8937
+ return true;
8938
+ }
8939
+ } elseif ( ewww_image_optimizer_gd_support() ) {
8940
+ $info = getimagesize( $filename );
8941
+ if ( ! empty( $info['channels'] ) ) {
8942
+ ewwwio_debug_message( "channel count is {$info['channels']}" );
8943
+ if ( 4 === (int) $info['channels'] ) {
8944
+ return true;
8945
+ }
8946
+ }
8947
+ }
8948
+ return false;
8949
+ }
8950
+
8951
  /**
8952
  * Count how many sizes are in the metadata, accounting for those with duplicate dimensions.
8953
  *
9058
  echo '<div>' . esc_html__( 'Amazon S3 image', 'ewww-image-optimizer' ) . '</div>';
9059
  $ewww_cdn = true;
9060
  }
9061
+ if ( is_array( $meta ) && class_exists( 'S3_Uploads\Plugin' ) && preg_match( '/^(http|s3|gs)\w*:/', get_attached_file( $id ) ) ) {
9062
+ echo '<div>' . esc_html__( 'Amazon S3 image', 'ewww-image-optimizer' ) . '</div>';
9063
+ $ewww_cdn = true;
9064
+ }
9065
  if ( is_array( $meta ) & class_exists( 'wpCloud\StatelessMedia\EWWW' ) && ! empty( $meta['gs_link'] ) ) {
9066
  echo '<div>' . esc_html__( 'WP Stateless image', 'ewww-image-optimizer' ) . '</div>';
9067
  $ewww_cdn = true;
9186
  ) . '</div>';
9187
  }
9188
  break;
9189
+ case 'image/webp':
9190
+ if ( ! $ewww_cdn && ewwwio_is_file( $file_path ) ) {
9191
+ $webp_size = ewww_image_optimizer_filesize( $file_path );
9192
+ // Get a human readable filesize.
9193
+ $webp_size = ewww_image_optimizer_size_format( $webp_size );
9194
+ echo '<div>WebP: ' . esc_html( $webp_size ) . '</div>';
9195
+ }
9196
+ return;
9197
+ break;
9198
  default:
9199
  // Not a supported mimetype.
9200
  $msg = '<div>' . esc_html__( 'Unsupported file type', 'ewww-image-optimizer' ) . '</div>';
9443
  } // End if().
9444
  echo '</div>';
9445
  } // End if().
 
 
9446
  }
9447
 
9448
  /**
9513
  $processed = array();
9514
  foreach ( $sizes as $size => $data ) {
9515
  ewwwio_debug_message( "checking for size: $size" );
 
9516
  if ( strpos( $size, 'webp' ) === 0 ) {
9517
  continue;
9518
  }
10858
  ewwwio_debug_message( 'png2jpg fill:' );
10859
  ewww_image_optimizer_jpg_background();
10860
  ewwwio_debug_message( 'webp conversion: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ? 'on' : 'off' ) );
10861
+ ewwwio_debug_message( 'js webp rewriting: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) ? 'on' : 'off' ) );
10862
  ewwwio_debug_message( 'picture webp rewriting: ' . ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_picture_webp' ) ? 'on' : 'off' ) );
10863
  ewwwio_debug_message( 'WebP Rewrite exclusions:' );
10864
  $webp_exclude_paths = ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_rewrite_exclude' ) ? esc_html( implode( "\n", ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_rewrite_exclude' ) ) ) : '';
10881
  ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) &&
10882
  ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_picture_webp' ) &&
10883
  ! ewww_image_optimizer_ce_webp_enabled() &&
10884
+ ! ewww_image_optimizer_swis_webp_enabled() &&
10885
  ! ewww_image_optimizer_easy_active()
10886
  ) {
10887
  if ( defined( 'PHP_SAPI' ) ) {
10924
  $eio_base = new EIO_Base();
10925
  $easyio_site_url = $eio_base->content_url();
10926
  $no_tracking = false;
10927
+ $webp_available = ewww_image_optimizer_webp_available();
10928
  $bulk_available = false;
10929
  $tools_available = true;
10930
  if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_NOEXEC' ) ) {
10974
  // Expand the missing utilities list for use in the error message.
10975
  $tools_missing_message = implode( ', ', $tools_missing );
10976
  }
 
 
 
 
 
 
10977
  if (
10978
  ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ||
10979
  ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ||
11015
  }
11016
  if ( $tools_missing_notice && ! $tools_available ) {
11017
  ewww_image_optimizer_enable_free_exec();
 
11018
  }
11019
  }
11020
  if ( 3 === $wizard_step ) {
11094
  );
11095
  ?>
11096
  </span>
11097
+ <?php if ( false !== strpos( $easyio_site_url, 'localhost' ) ) : ?>
11098
+ <br><span class="description" style="font-weight: bolder"><?php esc_html_e( 'Easy IO cannot be activated on localhost installs.', 'ewww-image-optimizer' ); ?></span>
11099
+ <?php elseif ( empty( ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) ) : ?>
11100
  <br><br>
11101
  <a href="<?php echo esc_url( add_query_arg( 'site_url', trim( $easyio_site_url ), 'https://ewww.io/manage-sites/' ) ); ?>" target="_blank">
11102
  <?php esc_html_e( 'First, add your Site URL to your account:', 'easy-image-optimizer' ); ?>
11107
  <a id='ewwwio-easy-activate' href='#' class='button-secondary'><?php esc_html_e( 'Activate', 'ewww-image-optimizer' ); ?></a>
11108
  <span id='ewwwio-easy-activation-processing'><img src='<?php echo esc_url( $loading_image_url ); ?>' alt='loading'/></span>
11109
  <?php elseif ( class_exists( 'ExactDN' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) && $exactdn->get_exactdn_domain() && $exactdn->verify_domain( $exactdn->get_exactdn_domain() ) ) : ?>
11110
+ <br><span style="color: #3eadc9; font-weight: bolder"><?php esc_html_e( 'Verified', 'ewww-image-optimizer' ); ?></span>
11111
  <span class="dashicons dashicons-yes"></span>
11112
  <?php endif; ?>
11113
  </p>
11871
  sprintf(
11872
  /* translators: %s: S3 Image Optimizer (link) */
11873
  esc_html__( 'Optimize unlimited Amazon S3 buckets with our %s.' ),
11874
+ '<a href="https://wordpress.org/plugins/s3-image-optimizer/">' . esc_html__( 'S3 Image Optimizer', 'ewww-image-optimizer' ) . '</a>'
11875
  ) : '' );
11876
  ?>
11877
  </p>
11995
  if ( ewww_image_optimizer_easy_active() || $cf_host ) {
11996
  ewww_image_optimizer_webp_rewrite_verify();
11997
  }
11998
+ $webp_available = ewww_image_optimizer_webp_available();
11999
  $test_webp_image = plugins_url( '/images/test.png.webp', __FILE__ );
12000
  $test_png_image = plugins_url( '/images/test.png', __FILE__ );
12001
  ?>
12085
  </tr>
12086
  </table>
12087
  <input type='hidden' id='ewww_image_optimizer_allow_multisite_override_active' name='ewww_image_optimizer_allow_multisite_override_active' value='0'>
 
 
 
12088
  </div><!-- end container general settings -->
12089
  <p class='submit'><input type='submit' class='button-primary' value='<?php esc_attr_e( 'Save Changes', 'ewww-image-optimizer' ); ?>' /></p>
12090
  </form>
12107
  <a class='button-secondary' href='<?php echo esc_url( admin_url( 'admin.php?action=ewww_image_optimizer_remove_cloud_key' ) ); ?>'>
12108
  <?php esc_html_e( 'Remove API key', 'ewww-image-optimizer' ); ?>
12109
  </a>
 
12110
  <p>
12111
  <?php if ( false !== strpos( $verify_cloud, 'great' ) ) : ?>
12112
  <span style="color: #3eadc9; font-weight: bolder"><?php esc_html_e( 'Verified,', 'ewww-image-optimizer' ); ?> </span><?php echo wp_kses_post( ewww_image_optimizer_cloud_quota() ); ?>
12173
  ?>
12174
  <?php if ( class_exists( 'Jetpack' ) && Jetpack::is_module_active( 'photon' ) && ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) : ?>
12175
  <p style='color: red'><?php esc_html_e( 'Inactive, please disable the Image Accelerator option on the Jetpack Dashboard.', 'ewww-image-optimizer' ); ?></p>
12176
+ <?php elseif ( false !== strpos( $easyio_site_url, 'localhost' ) ) : ?>
12177
+ <p class="description" style="font-weight: bolder"><?php esc_html_e( 'Easy IO cannot be activated on localhost installs.', 'ewww-image-optimizer' ); ?></p>
12178
  <?php elseif ( ! $exactdn_enabled ) : ?>
12179
  <p>
12180
  <strong><a href="https://ewww.io/plans/" target="_blank">
12426
  </tr>
12427
  <?php endif; ?>
12428
  <?php endif; ?>
12429
+ <?php if ( ! $webp_available ) : ?>
12430
  <tr id='ewww_image_optimizer_webp_container'>
12431
  <th scope='row'>
12432
  <label for='ewww_image_optimizer_webp'><?php esc_html_e( 'WebP Conversion', 'ewww-image-optimizer' ); ?></label>
12473
  </tr>
12474
  <tr>
12475
  <?php endif; ?>
12476
+ <?php if ( ewww_image_optimizer_easy_active() ) : ?>
12477
+ <tr id='ewww_image_optimizer_webp_easyio_container'>
12478
+ <th scope='row'>
12479
+ <label for='ewww_image_optimizer_webp'><?php esc_html_e( 'WebP Conversion', 'ewww-image-optimizer' ); ?></label>
12480
+ </th>
12481
+ <td>
12482
+ <p class='description'><?php esc_html_e( 'WebP images are served automatically by Easy IO.', 'ewww-image-optimizer' ); ?></p>
12483
+ </td>
12484
+ </tr>
12485
+ <?php elseif ( ewww_image_optimizer_ce_webp_enabled() ) : ?>
12486
+ <tr id='ewww_image_optimizer_webp_setting_container'>
12487
+ <th scope='row'>
12488
+ <?php esc_html_e( 'WebP Delivery Method', 'ewww-image-optimizer' ); ?>
12489
+ </th>
12490
+ <td>
12491
+ <p class='description'><?php esc_html_e( 'WebP images are delivered by Cache Enabler.', 'ewww-image-optimizer' ); ?></p>
12492
+ </td>
12493
+ </tr>
12494
+ <?php elseif ( ewww_image_optimizer_swis_webp_enabled() ) : ?>
12495
+ <tr id='ewww_image_optimizer_webp_setting_container'>
12496
+ <th scope='row'>
12497
+ <?php esc_html_e( 'WebP Delivery Method', 'ewww-image-optimizer' ); ?>
12498
+ </th>
12499
+ <td>
12500
+ <p class='description'><?php esc_html_e( 'WebP images are delivered by SWIS Performance.', 'ewww-image-optimizer' ); ?></p>
12501
+ </td>
12502
+ </tr>
12503
+ <?php elseif ( $webp_available ) : ?>
12504
+ <tr id='ewww_image_optimizer_webp_easyio_container' style='display:none;'>
12505
+ <th scope='row'>
12506
+ <label for='ewww_image_optimizer_webp'><?php esc_html_e( 'WebP Conversion', 'ewww-image-optimizer' ); ?></label>
12507
+ </th>
12508
+ <td>
12509
+ <p class='description'><?php esc_html_e( 'WebP images are served automatically by Easy IO.', 'ewww-image-optimizer' ); ?></p>
12510
+ </td>
12511
+ </tr>
12512
  <tr class='ewww_image_optimizer_webp_setting_container' <?php echo ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' ) ? '' : ' style="display:none"'; ?>>
12513
  <th scope='row'>
12514
  <?php esc_html_e( 'WebP Delivery Method', 'ewww-image-optimizer' ); ?>
12726
  </td>
12727
  </tr>
12728
  <?php endif; ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12729
  <?php endif; ?>
12730
 
12731
  <?php if ( class_exists( 'Cloudinary' ) && Cloudinary::config_get( 'api_secret' ) ) : ?>
13325
  <p class='submit'><input type='submit' class='button-primary' value='<?php esc_attr_e( 'Save Changes', 'ewww-image-optimizer' ); ?>' /></p>
13326
  </form>
13327
 
13328
+ <?php if ( ! ewww_image_optimizer_easy_active() && ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) : ?>
13329
  <hr style='clear:both;'>
13330
  <h2><?php esc_html_e( "Shane's Recommendations", 'ewww-image-optimizer' ); ?></h2>
13331
  <p><?php esc_html_e( 'These are products I have personally used. An * indicates an affiliate link so you can support future development of EWWW IO.', 'ewww-image-optimizer' ); ?></p>
13354
  <a href="https://nodeping.com?rid=201407082225W862K">NodePing</a>* -
13355
  <?php esc_html_e( 'Monitor all the things, and make sure they stay online. They have a fantastic array of monitors and notifications that you can setup, they are cheap, simple, and have great support.', 'ewww-image-optimizer' ); ?>
13356
  </p>
13357
+ <?php endif; ?>
13358
  </div><!-- end container wrap -->
13359
  <?php
13360
  if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_enable_help' ) ) {
13493
  delete_site_option( 'ewww_image_optimizer_exactdn_verified' );
13494
  delete_site_option( 'ewww_image_optimizer_exactdn_validation' );
13495
  delete_site_option( 'ewww_image_optimizer_exactdn_suspended' );
13496
+ global $exactdn;
13497
+ $exactdn->cron_setup( false );
13498
  $sendback = wp_get_referer();
13499
  wp_safe_redirect( $sendback );
13500
  exit;
13665
  $eio_debug = str_replace( '<br>', "\n", $eio_debug );
13666
  file_put_contents( $debug_log, $timestamp . $eio_debug, FILE_APPEND );
13667
  }
13668
+ $eio_debug = '';
13669
+ } elseif ( ! is_dir( dirname( $debug_log ) ) || ! is_writable( dirname( $debug_log ) ) ) {
13670
+ $eio_debug = '';
13671
  }
 
13672
  ewwwio_memory( __FUNCTION__ );
13673
  }
13674
 
ewww-image-optimizer.php CHANGED
@@ -13,7 +13,7 @@ Plugin Name: EWWW Image Optimizer
13
  Plugin URI: https://wordpress.org/plugins/ewww-image-optimizer/
14
  Description: Reduce file sizes for images within WordPress including NextGEN Gallery and GRAND FlAGallery. Uses jpegtran, optipng/pngout, and gifsicle.
15
  Author: Exactly WWW
16
- Version: 6.1.9
17
  Author URI: https://ewww.io/
18
  License: GPLv3
19
  */
13
  Plugin URI: https://wordpress.org/plugins/ewww-image-optimizer/
14
  Description: Reduce file sizes for images within WordPress including NextGEN Gallery and GRAND FlAGallery. Uses jpegtran, optipng/pngout, and gifsicle.
15
  Author: Exactly WWW
16
+ Version: 6.2.0
17
  Author URI: https://ewww.io/
18
  License: GPLv3
19
  */
includes/check-webp.js ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var ewww_webp_supported = false;
2
+ // webp detection adapted from https://developers.google.com/speed/webp/faq#how_can_i_detect_browser_support_using_javascript
3
+ function check_webp_feature(feature, callback) {
4
+ callback = (typeof callback !== 'undefined') ? callback : function(){};
5
+ if (ewww_webp_supported) {
6
+ callback(ewww_webp_supported);
7
+ return;
8
+ }
9
+ var kTestImages = {
10
+ alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
11
+ };
12
+ var img = new Image();
13
+ img.onload = function () {
14
+ ewww_webp_supported = (img.width > 0) && (img.height > 0);
15
+ if (callback) {
16
+ callback(ewww_webp_supported);
17
+ }
18
+ };
19
+ img.onerror = function () {
20
+ if (callback) {
21
+ callback(false);
22
+ }
23
+ };
24
+ img.src = "data:image/webp;base64," + kTestImages[feature];
25
+ }
26
+ check_webp_feature('alpha');
includes/check-webp.min.js ADDED
@@ -0,0 +1 @@
 
1
+ var ewww_webp_supported=!1;function check_webp_feature(A,e){if(e=void 0!==e?e:function(){},ewww_webp_supported)e(ewww_webp_supported);else{var w=new Image;w.onload=function(){ewww_webp_supported=0<w.width&&0<w.height,e&&e(ewww_webp_supported)},w.onerror=function(){e&&e(!1)},w.src="data:image/webp;base64,"+{alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA=="}[A]}}check_webp_feature("alpha");
includes/eio-tools.js CHANGED
@@ -271,6 +271,10 @@ jQuery(document).ready(function($) {
271
  if (!confirm(ewww_vars.tool_warning)) {
272
  return false;
273
  }
 
 
 
 
274
  var ewww_originals_data = {
275
  action: 'ewwwio_get_all_attachments',
276
  ewww_wpnonce: ewww_vars._wpnonce,
@@ -279,11 +283,23 @@ jQuery(document).ready(function($) {
279
  try {
280
  ewww_original_attachments = JSON.parse(response);
281
  } catch (err) {
 
 
 
282
  $('#ewww-clean-originals-progress').html('<span style="color: red"><b>' + ewww_vars.invalid_response + '</b></span>');
 
283
  console.log(err);
284
  console.log(response);
285
  return false;
286
  }
 
 
 
 
 
 
 
 
287
  ewww_total_originals = ewww_original_attachments.length;
288
  $('.ewww-tool-info').hide();
289
  $('.ewww-tool-form').hide();
@@ -319,6 +335,12 @@ jQuery(document).ready(function($) {
319
  return false;
320
  }
321
  if(!ewww_original_attachments.length) {
 
 
 
 
 
 
322
  $('#ewww-clean-originals-progress').html(ewww_vars.finished);
323
  return false;
324
  }
@@ -334,6 +356,10 @@ jQuery(document).ready(function($) {
334
  action: 'bulk_aux_images_count_converted',
335
  ewww_wpnonce: ewww_vars._wpnonce,
336
  };
 
 
 
 
337
  $.post(ajaxurl, ewww_converted_data, function(response) {
338
  try {
339
  var ewww_response = JSON.parse(response);
@@ -391,6 +417,10 @@ jQuery(document).ready(function($) {
391
  action: 'ewwwio_get_all_attachments',
392
  ewww_wpnonce: ewww_vars._wpnonce,
393
  };
 
 
 
 
394
  $.post(ajaxurl, ewww_webp_data, function(response) {
395
  try {
396
  ewww_webp_attachments = JSON.parse(response);
@@ -481,6 +511,10 @@ jQuery(document).ready(function($) {
481
  });
482
  }
483
  $('#ewww-clean-table').on( 'submit', function() {
 
 
 
 
484
  ewww_total_pages = Math.ceil(ewww_vars.image_count / 500);
485
  $('.ewww-tool-info').hide();
486
  $('.ewww-tool-form').hide();
@@ -525,6 +559,10 @@ jQuery(document).ready(function($) {
525
  });
526
  }
527
  $('#ewww-clean-meta').on( 'submit', function() {
 
 
 
 
528
  $('.ewww-tool-info').hide();
529
  $('.ewww-tool-form').hide();
530
  $('.ewww-tool-divider').hide();
271
  if (!confirm(ewww_vars.tool_warning)) {
272
  return false;
273
  }
274
+ var header_label = $(this).find('input[type="submit"]').val();
275
+ if (header_label) {
276
+ $('#ewwwio-tools-header').html(header_label);
277
+ }
278
  var ewww_originals_data = {
279
  action: 'ewwwio_get_all_attachments',
280
  ewww_wpnonce: ewww_vars._wpnonce,
283
  try {
284
  ewww_original_attachments = JSON.parse(response);
285
  } catch (err) {
286
+ $('.ewww-tool-info').hide();
287
+ $('.ewww-tool-form').hide();
288
+ $('.ewww-tool-divider').hide();
289
  $('#ewww-clean-originals-progress').html('<span style="color: red"><b>' + ewww_vars.invalid_response + '</b></span>');
290
+ $('#ewww-clean-originals-progress').show();
291
  console.log(err);
292
  console.log(response);
293
  return false;
294
  }
295
+ if ( ewww_original_attachments.error ) {
296
+ $('.ewww-tool-info').hide();
297
+ $('.ewww-tool-form').hide();
298
+ $('.ewww-tool-divider').hide();
299
+ $('#ewww-clean-originals-progress').html(ewww_original_attachments.error);
300
+ $('#ewww-clean-originals-progress').show();
301
+ return false;
302
+ }
303
  ewww_total_originals = ewww_original_attachments.length;
304
  $('.ewww-tool-info').hide();
305
  $('.ewww-tool-form').hide();
335
  return false;
336
  }
337
  if(!ewww_original_attachments.length) {
338
+ var ewww_originals_data = {
339
+ action: 'bulk_aux_images_delete_original',
340
+ ewww_wpnonce: ewww_vars._wpnonce,
341
+ delete_originals_done: 1,
342
+ };
343
+ $.post(ajaxurl, ewww_originals_data);
344
  $('#ewww-clean-originals-progress').html(ewww_vars.finished);
345
  return false;
346
  }
356
  action: 'bulk_aux_images_count_converted',
357
  ewww_wpnonce: ewww_vars._wpnonce,
358
  };
359
+ var header_label = $(this).find('input[type="submit"]').val();
360
+ if (header_label) {
361
+ $('#ewwwio-tools-header').html(header_label);
362
+ }
363
  $.post(ajaxurl, ewww_converted_data, function(response) {
364
  try {
365
  var ewww_response = JSON.parse(response);
417
  action: 'ewwwio_get_all_attachments',
418
  ewww_wpnonce: ewww_vars._wpnonce,
419
  };
420
+ var header_label = $(this).find('input[type="submit"]').val();
421
+ if (header_label) {
422
+ $('#ewwwio-tools-header').html(header_label);
423
+ }
424
  $.post(ajaxurl, ewww_webp_data, function(response) {
425
  try {
426
  ewww_webp_attachments = JSON.parse(response);
511
  });
512
  }
513
  $('#ewww-clean-table').on( 'submit', function() {
514
+ var header_label = $(this).find('input[type="submit"]').val();
515
+ if (header_label) {
516
+ $('#ewwwio-tools-header').html(header_label);
517
+ }
518
  ewww_total_pages = Math.ceil(ewww_vars.image_count / 500);
519
  $('.ewww-tool-info').hide();
520
  $('.ewww-tool-form').hide();
559
  });
560
  }
561
  $('#ewww-clean-meta').on( 'submit', function() {
562
+ var header_label = $(this).find('input[type="submit"]').val();
563
+ if (header_label) {
564
+ $('#ewwwio-tools-header').html(header_label);
565
+ }
566
  $('.ewww-tool-info').hide();
567
  $('.ewww-tool-form').hide();
568
  $('.ewww-tool-divider').hide();
includes/lazysizes-post.js CHANGED
@@ -1,4 +1,4 @@
1
- lazysizesWebP('alpha', lazySizes.init);
2
  function shouldAutoScale(target){
3
  if (eio_lazy_vars.skip_autoscale == 1) {
4
  console.log('autoscale disabled globally');
1
+ lazySizes.init();
2
  function shouldAutoScale(target){
3
  if (eio_lazy_vars.skip_autoscale == 1) {
4
  console.log('autoscale disabled globally');
includes/lazysizes-pre.js CHANGED
@@ -1,4 +1,4 @@
1
- var ewww_webp_supported = false;
2
  function lazysizesWebP(feature, callback) {
3
  var kTestImages = {
4
  alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
@@ -13,7 +13,7 @@ function lazysizesWebP(feature, callback) {
13
  callback();
14
  };
15
  img.src = "data:image/webp;base64," + kTestImages[feature];
16
- }
17
  window.lazySizesConfig = window.lazySizesConfig || {};
18
  window.lazySizesConfig.init = false;
19
  window.lazySizesConfig.expand = document.documentElement.clientHeight > 500 && document.documentElement.clientWidth > 500 ? 1000 : 740;
1
+ /*var ewww_webp_supported = false;
2
  function lazysizesWebP(feature, callback) {
3
  var kTestImages = {
4
  alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
13
  callback();
14
  };
15
  img.src = "data:image/webp;base64," + kTestImages[feature];
16
+ }*/
17
  window.lazySizesConfig = window.lazySizesConfig || {};
18
  window.lazySizesConfig.init = false;
19
  window.lazySizesConfig.expand = document.documentElement.clientHeight > 500 && document.documentElement.clientWidth > 500 ? 1000 : 740;
includes/lazysizes.min.js CHANGED
@@ -1 +1 @@
1
- var ewww_webp_supported=!1;function lazysizesWebP(e,t){var a=new Image;a.onload=function(){ewww_webp_supported=0<a.width&&0<a.height,t()},a.onerror=function(){t()},a.src="data:image/webp;base64,"+{alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",animation:"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"}[e]}function shouldAutoScale(e){if(1==eio_lazy_vars.skip_autoscale)return!1;if(e.hasAttributes())for(var t=e.attributes,a=/skip-autoscale/,i=t.length-1;0<=i;i--){if(a.test(t[i].name))return!1;if(a.test(t[i].value))return!1}return!0}function constrainSrc(e,t,a,i){if(null===e)return e;var r=/w=(\d+)/,n=/fit=(\d+),(\d+)/,o=/resize=(\d+),(\d+)/,s=decodeURIComponent(e);if("undefined"==typeof eio_lazy_vars&&(eio_lazy_vars={exactdn_domain:".exactdn.com"}),0<e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)){var l=o.exec(s);if(l&&t<l[1])return s.replace(o,"resize="+t+","+a);var d=r.exec(e);if(d&&t<=d[1]){if("bg-cover"!==i&&"img-crop"!==i)return e.replace(r,"w="+t);var c=d[1]-t;return 20<c||a<1080?e.replace(r,"resize="+t+","+a):e}var u=n.exec(s);if(u&&t<u[1]){if("bg-cover"!==i&&"img-crop"!==i)return s.replace(n,"fit="+t+","+a);var f=u[1]-t,A=u[2]-a;return 20<f||20<A?e.replace(r,"resize="+t+","+a):e}if(!d&&!u&&!l)return"img"===i?e+"&fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"&resize="+t+","+a:t<a?e+"&h="+a:e+"&w="+t}return-1==e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)?"img"===i?e+"?fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"?resize="+t+","+a:t<a?e+"?h="+a:e+"?w="+t:e}window.lazySizesConfig=window.lazySizesConfig||{},window.lazySizesConfig.init=!1,window.lazySizesConfig.expand=500<document.documentElement.clientHeight&&500<document.documentElement.clientWidth?1e3:740,50<eio_lazy_vars.threshold&&(window.lazySizesConfig.expand=eio_lazy_vars.threshold),function(e,t){var a=function(i,A,n){"use strict";var g,h;if(function(){var e,t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in h=i.lazySizesConfig||i.lazysizesConfig||{},t)e in h||(h[e]=t[e])}(),!A||!A.getElementsByClassName)return{init:function(){},cfg:h,noSupport:!0};var z=A.documentElement,r=i.HTMLPictureElement,o="addEventListener",m="getAttribute",e=i[o].bind(i),v=i.setTimeout,a=i.requestAnimationFrame||v,s=i.requestIdleCallback,p=/^picture$/i,l=["load","error","lazyincluded","_lazyloaded"],d={},y=Array.prototype.forEach,c=function(e,t){return d[t]||(d[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")),d[t].test(e[m]("class")||"")&&d[t]},b=function(e,t){c(e,t)||e.setAttribute("class",(e[m]("class")||"").trim()+" "+t)},w=function(e,t){var a;(a=c(e,t))&&e.setAttribute("class",(e[m]("class")||"").replace(a," "))},_=function(t,a,e){var i=e?o:"removeEventListener";e&&_(t,a),l.forEach(function(e){t[i](e,a)})},C=function(e,t,a,i,r){var n=A.createEvent("Event");return a||(a={}),a.instance=g,n.initEvent(t,!i,!r),n.detail=a,e.dispatchEvent(n),n},S=function(e,t){var a;!r&&(a=i.picturefill||h.pf)?(t&&t.src&&!e[m]("srcset")&&e.setAttribute("srcset",t.src),a({reevaluate:!0,elements:[e]})):t&&t.src&&(e.src=t.src)},u=function(e,t){return(getComputedStyle(e,null)||{})[t]},f=function(e,t,a){for(a=a||e.offsetWidth;a<h.minSize&&t&&!e._lazysizesWidth;)a=t.offsetWidth,t=t.parentNode;return a},E=(we=[],_e=[],Ce=we,Se=function(){var e=Ce;for(Ce=we.length?_e:we,be=!(ye=!0);e.length;)e.shift()();ye=!1},Ee=function(e,t){ye&&!t?e.apply(this,arguments):(Ce.push(e),be||(be=!0,(A.hidden?v:a)(Se)))},Ee._lsFlush=Se,Ee),t=function(a,e){return e?function(){E(a)}:function(){var e=this,t=arguments;E(function(){a.apply(e,t)})}},W=function(e){var t,a,i=function(){t=null,e()},r=function(){var e=n.now()-a;e<99?v(r,99-e):(s||i)(i)};return function(){a=n.now(),t||(t=v(r,99))}},x=(ee=/^img$/i,te=/^iframe$/i,ae="onscroll"in i&&!/(gle|ing)bot/.test(navigator.userAgent),ie=0,re=0,ne=-1,oe=function(e){re--,(!e||re<0||!e.target)&&(re=0)},se=function(e){return null==$&&($="hidden"==u(A.body,"visibility")),$||!("hidden"==u(e.parentNode,"visibility")&&"hidden"==u(e,"visibility"))},le=function(e,t){var a,i=e,r=se(e);for(I-=t,G+=t,J-=t,O+=t;r&&(i=i.offsetParent)&&i!=A.body&&i!=z;)(r=0<(u(i,"opacity")||1))&&"visible"!=u(i,"overflow")&&(a=i.getBoundingClientRect(),r=O>a.left&&J<a.right&&G>a.top-1&&I<a.bottom+1);return r},de=function(){var e,t,a,i,r,n,o,s,l,d,c,u,f=g.elements;if((D=h.loadMode)&&re<8&&(e=f.length)){for(t=0,ne++;t<e;t++)if(f[t]&&!f[t]._lazyRace)if(!ae||g.prematureUnveil&&g.prematureUnveil(f[t]))ze(f[t]);else if((s=f[t][m]("data-expand"))&&(n=1*s)||(n=ie),d||(d=!h.expand||h.expand<1?500<z.clientHeight&&500<z.clientWidth?500:370:h.expand,g._defEx=d,c=d*h.expFactor,u=h.hFac,$=null,ie<c&&re<1&&2<ne&&2<D&&!A.hidden?(ie=c,ne=0):ie=1<D&&1<ne&&re<6?d:0),l!==n&&(U=innerWidth+n*u,F=innerHeight+n,o=-1*n,l=n),a=f[t].getBoundingClientRect(),(G=a.bottom)>=o&&(I=a.top)<=F&&(O=a.right)>=o*u&&(J=a.left)<=U&&(G||O||J||I)&&(h.loadHidden||se(f[t]))&&(P&&re<3&&!s&&(D<3||ne<4)||le(f[t],n))){if(ze(f[t]),r=!0,9<re)break}else!r&&P&&!i&&re<4&&ne<4&&2<D&&(k[0]||h.preloadAfterLoad)&&(k[0]||!s&&(G||O||J||I||"auto"!=f[t][m](h.sizesAttr)))&&(i=k[0]||f[t]);i&&!r&&ze(i)}},q=de,V=0,X=h.throttleDelay,Y=h.ricTimeout,K=function(){j=!1,V=n.now(),q()},Z=s&&49<Y?function(){s(K,{timeout:Y}),Y!==h.ricTimeout&&(Y=h.ricTimeout)}:t(function(){v(K)},!0),ce=function(e){var t;(e=!0===e)&&(Y=33),j||(j=!0,(t=X-(n.now()-V))<0&&(t=0),e||t<9?Z():v(Z,t))},ue=function(e){var t=e.target;t._lazyCache?delete t._lazyCache:(oe(e),b(t,h.loadedClass),w(t,h.loadingClass),_(t,Ae),C(t,"lazyloaded"))},fe=t(ue),Ae=function(e){fe({target:e.target})},ge=function(e){var t,a=e[m](h.srcsetAttr);(t=h.customMedia[e[m]("data-media")||e[m]("media")])&&e.setAttribute("media",t),a&&e.setAttribute("srcset",a)},he=t(function(t,e,a,i,r){var n,o,s,l,d,c,u,f,A;(d=C(t,"lazybeforeunveil",e)).defaultPrevented||(i&&(a?b(t,h.autosizesClass):t.setAttribute("sizes",i)),o=t[m](h.srcsetAttr),n=t[m](h.srcAttr),r&&(s=t.parentNode,l=s&&p.test(s.nodeName||"")),c=e.firesLoad||"src"in t&&(o||n||l),d={target:t},b(t,h.loadingClass),c&&(clearTimeout(H),H=v(oe,2500),_(t,Ae,!0)),l&&y.call(s.getElementsByTagName("source"),ge),o?t.setAttribute("srcset",o):n&&!l&&(te.test(t.nodeName)?(f=n,0==(A=(u=t).getAttribute("data-load-mode")||h.iframeLoadMode)?u.contentWindow.location.replace(f):1==A&&(u.src=f)):t.src=n),r&&(o||l)&&S(t,{src:n})),t._lazyRace&&delete t._lazyRace,w(t,h.lazyClass),E(function(){var e=t.complete&&1<t.naturalWidth;c&&!e||(e&&b(t,h.fastLoadedClass),ue(d),t._lazyCache=!0,v(function(){"_lazyCache"in t&&delete t._lazyCache},9)),"lazy"==t.loading&&re--},!0)}),ze=function(e){if(!e._lazyRace){var t,a=ee.test(e.nodeName),i=a&&(e[m](h.sizesAttr)||e[m]("sizes")),r="auto"==i;(!r&&P||!a||!e[m]("src")&&!e.srcset||e.complete||c(e,h.errorClass)||!c(e,h.lazyClass))&&(t=C(e,"lazyunveilread").detail,r&&M.updateElem(e,!0,e.offsetWidth),e._lazyRace=!0,re++,he(e,t,r,i,a))}},me=W(function(){h.loadMode=3,ce()}),ve=function(){3==h.loadMode&&(h.loadMode=2),me()},pe=function(){P||(n.now()-T<999?v(pe,999):(P=!0,h.loadMode=3,ce(),e("scroll",ve,!0)))},{_:function(){T=n.now(),g.elements=A.getElementsByClassName(h.lazyClass),k=A.getElementsByClassName(h.lazyClass+" "+h.preloadClass),e("scroll",ce,!0),e("resize",ce,!0),e("pageshow",function(e){if(e.persisted){var t=A.querySelectorAll("."+h.loadingClass);t.length&&t.forEach&&a(function(){t.forEach(function(e){e.complete&&ze(e)})})}}),i.MutationObserver?new MutationObserver(ce).observe(z,{childList:!0,subtree:!0,attributes:!0}):(z[o]("DOMNodeInserted",ce,!0),z[o]("DOMAttrModified",ce,!0),setInterval(ce,999)),e("hashchange",ce,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){A[o](e,ce,!0)}),/d$|^c/.test(A.readyState)?pe():(e("load",pe),A[o]("DOMContentLoaded",ce),v(pe,2e4)),g.elements.length?(de(),E._lsFlush()):ce()},checkElems:ce,unveil:ze,_aLSL:ve}),M=(N=t(function(e,t,a,i){var r,n,o;if(e._lazysizesWidth=i,i+="px",e.setAttribute("sizes",i),p.test(t.nodeName||""))for(r=t.getElementsByTagName("source"),n=0,o=r.length;n<o;n++)r[n].setAttribute("sizes",i);a.detail.dataAttr||S(e,a.detail)}),R=function(e,t,a){var i,r=e.parentNode;r&&(a=f(e,r,a),(i=C(e,"lazybeforesizes",{width:a,dataAttr:!!t})).defaultPrevented||(a=i.detail.width)&&a!==e._lazysizesWidth&&N(e,r,i,a))},Q=W(function(){var e,t=L.length;if(t)for(e=0;e<t;e++)R(L[e])}),{_:function(){L=A.getElementsByClassName(h.autosizesClass),e("resize",Q)},checkElems:Q,updateElem:R}),B=function(){!B.i&&A.getElementsByClassName&&(B.i=!0,M._(),x._())};var L,N,R,Q;var k,P,H,D,T,U,F,I,J,O,G,$,q,j,V,X,Y,K,Z,ee,te,ae,ie,re,ne,oe,se,le,de,ce,ue,fe,Ae,ge,he,ze,me,ve,pe;var ye,be,we,_e,Ce,Se,Ee;return v(function(){h.init&&B()}),g={cfg:h,autoSizer:M,loader:x,init:B,uP:S,aC:b,rC:w,hC:c,fire:C,gW:f,rAF:E}}(e,e.document,Date);e.lazySizes=a,"object"==typeof module&&module.exports&&(module.exports=a)}("undefined"!=typeof window?window:{}),lazysizesWebP("alpha",lazySizes.init),document.addEventListener("lazybeforesizes",function(e){e.target.getAttribute("data-src");void 0!==e.target._lazysizesWidth&&e.detail.width<e.target._lazysizesWidth&&(e.detail.width=e.target._lazysizesWidth)}),document.addEventListener("lazybeforeunveil",function(e){var t=e.target,a=t.getAttribute("data-srcset");if(t.naturalWidth&&!a&&1<t.naturalWidth&&1<t.naturalHeight){var i=window.devicePixelRatio||1,r=t.naturalWidth,n=t.naturalHeight,o=t.getAttribute("data-eio-rwidth"),s=t.getAttribute("data-eio-rheight");o&&r<o&&(r=o,n=s);var l=t.clientWidth&&1.25*t.clientWidth<r,d=t.clientHeight&&1.25*t.clientHeight<n;if(l||d){var c=Math.round(t.offsetWidth*i),u=Math.round(t.offsetHeight*i),f=t.getAttribute("data-src"),A=t.getAttribute("data-src-webp");if(ewww_webp_supported&&A&&-1==f.search("webp=1")&&(f=A),shouldAutoScale(t)&&shouldAutoScale(t.parentNode))if(window.lazySizes.hC(t,"et_pb_jt_filterable_grid_item_image")||window.lazySizes.hC(t,"ss-foreground-image")||window.lazySizes.hC(t,"img-crop"))g=constrainSrc(f,c,u,"img-crop");else g=constrainSrc(f,c,u,"img");else var g=!1;g&&f!=g&&t.setAttribute("data-src",g)}}if(ewww_webp_supported){if(a){var h=t.getAttribute("data-srcset-webp");h&&t.setAttribute("data-srcset",h)}if(!(A=t.getAttribute("data-src-webp")))return;t.setAttribute("data-src",A)}}),function(e,t){var a=function(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)};t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(o,e,s){"use strict";var l;e.addEventListener&&(l=/\(|\)|\s|'/,addEventListener("lazybeforeunveil",function(e){var t,a;if(e.detail.instance==s&&(!e.defaultPrevented&&("none"==e.target.preload&&(e.target.preload="auto"),t=e.target.getAttribute("data-bg")))){ewww_webp_supported&&(a=e.target.getAttribute("data-bg-webp"))&&(t=a);var i=o.devicePixelRatio||1,r=Math.round(e.target.offsetWidth*i),n=Math.round(e.target.offsetHeight*i);shouldAutoScale(e.target)&&shouldAutoScale(e.target.parentNode)&&(t=o.lazySizes.hC(e.target,"wp-block-cover")?(o.lazySizes.hC(e.target,"has-parallax")&&(r=Math.round(o.screen.width*i),n=Math.round(o.screen.height*i)),constrainSrc(t,r,n,"bg-cover")):o.lazySizes.hC(e.target,"elementor-bg")?constrainSrc(t,r,n,"bg-cover"):constrainSrc(t,r,n,"bg")),e.target.style.backgroundImage="url("+(l.test(t)?JSON.stringify(t):t)+")"}},!1))});
1
+ function shouldAutoScale(e){if(1==eio_lazy_vars.skip_autoscale)return!1;if(e.hasAttributes())for(var t=e.attributes,a=/skip-autoscale/,i=t.length-1;0<=i;i--){if(a.test(t[i].name))return!1;if(a.test(t[i].value))return!1}return!0}function constrainSrc(e,t,a,i){if(null===e)return e;var r=/w=(\d+)/,n=/fit=(\d+),(\d+)/,o=/resize=(\d+),(\d+)/,s=decodeURIComponent(e);if("undefined"==typeof eio_lazy_vars&&(eio_lazy_vars={exactdn_domain:".exactdn.com"}),0<e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)){var l=o.exec(s);if(l&&t<l[1])return s.replace(o,"resize="+t+","+a);var d=r.exec(e);if(d&&t<=d[1]){if("bg-cover"!==i&&"img-crop"!==i)return e.replace(r,"w="+t);var c=d[1]-t;return 20<c||a<1080?e.replace(r,"resize="+t+","+a):e}var u=n.exec(s);if(u&&t<u[1]){if("bg-cover"!==i&&"img-crop"!==i)return s.replace(n,"fit="+t+","+a);var f=u[1]-t,g=u[2]-a;return 20<f||20<g?e.replace(r,"resize="+t+","+a):e}if(!d&&!u&&!l)return"img"===i?e+"&fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"&resize="+t+","+a:t<a?e+"&h="+a:e+"&w="+t}return-1==e.search("\\?")&&0<e.search(eio_lazy_vars.exactdn_domain)?"img"===i?e+"?fit="+t+","+a:"bg-cover"===i||"img-crop"===i?e+"?resize="+t+","+a:t<a?e+"?h="+a:e+"?w="+t:e}window.lazySizesConfig=window.lazySizesConfig||{},window.lazySizesConfig.init=!1,window.lazySizesConfig.expand=500<document.documentElement.clientHeight&&500<document.documentElement.clientWidth?1e3:740,50<eio_lazy_vars.threshold&&(window.lazySizesConfig.expand=eio_lazy_vars.threshold),function(e,t){var a=function(i,g,n){"use strict";var h,z;if(function(){var e,t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:!0,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:!0,ricTimeout:0,throttleDelay:125};for(e in z=i.lazySizesConfig||i.lazysizesConfig||{},t)e in z||(z[e]=t[e])}(),!g||!g.getElementsByClassName)return{init:function(){},cfg:z,noSupport:!0};var v=g.documentElement,r=i.HTMLPictureElement,o="addEventListener",m="getAttribute",e=i[o].bind(i),y=i.setTimeout,a=i.requestAnimationFrame||y,s=i.requestIdleCallback,p=/^picture$/i,l=["load","error","lazyincluded","_lazyloaded"],d={},b=Array.prototype.forEach,c=function(e,t){return d[t]||(d[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")),d[t].test(e[m]("class")||"")&&d[t]},w=function(e,t){c(e,t)||e.setAttribute("class",(e[m]("class")||"").trim()+" "+t)},_=function(e,t){var a;(a=c(e,t))&&e.setAttribute("class",(e[m]("class")||"").replace(a," "))},C=function(t,a,e){var i=e?o:"removeEventListener";e&&C(t,a),l.forEach(function(e){t[i](e,a)})},A=function(e,t,a,i,r){var n=g.createEvent("Event");return a||(a={}),a.instance=h,n.initEvent(t,!i,!r),n.detail=a,e.dispatchEvent(n),n},S=function(e,t){var a;!r&&(a=i.picturefill||z.pf)?(t&&t.src&&!e[m]("srcset")&&e.setAttribute("srcset",t.src),a({reevaluate:!0,elements:[e]})):t&&t.src&&(e.src=t.src)},u=function(e,t){return(getComputedStyle(e,null)||{})[t]},f=function(e,t,a){for(a=a||e.offsetWidth;a<z.minSize&&t&&!e._lazysizesWidth;)a=t.offsetWidth,t=t.parentNode;return a},E=(_e=[],Ce=[],Ae=_e,Se=function(){var e=Ae;for(Ae=_e.length?Ce:_e,we=!(be=!0);e.length;)e.shift()();be=!1},Ee=function(e,t){be&&!t?e.apply(this,arguments):(Ae.push(e),we||(we=!0,(g.hidden?y:a)(Se)))},Ee._lsFlush=Se,Ee),t=function(a,e){return e?function(){E(a)}:function(){var e=this,t=arguments;E(function(){a.apply(e,t)})}},x=function(e){var t,a,i=function(){t=null,e()},r=function(){var e=n.now()-a;e<99?y(r,99-e):(s||i)(i)};return function(){a=n.now(),t||(t=y(r,99))}},M=(ee=/^img$/i,te=/^iframe$/i,ae="onscroll"in i&&!/(gle|ing)bot/.test(navigator.userAgent),ie=0,re=0,ne=-1,oe=function(e){re--,(!e||re<0||!e.target)&&(re=0)},se=function(e){return null==J&&(J="hidden"==u(g.body,"visibility")),J||!("hidden"==u(e.parentNode,"visibility")&&"hidden"==u(e,"visibility"))},le=function(e,t){var a,i=e,r=se(e);for($-=t,U+=t,q-=t,j+=t;r&&(i=i.offsetParent)&&i!=g.body&&i!=v;)(r=0<(u(i,"opacity")||1))&&"visible"!=u(i,"overflow")&&(a=i.getBoundingClientRect(),r=j>a.left&&q<a.right&&U>a.top-1&&$<a.bottom+1);return r},de=function(){var e,t,a,i,r,n,o,s,l,d,c,u,f=h.elements;if((P=z.loadMode)&&re<8&&(e=f.length)){for(t=0,ne++;t<e;t++)if(f[t]&&!f[t]._lazyRace)if(!ae||h.prematureUnveil&&h.prematureUnveil(f[t]))ve(f[t]);else if((s=f[t][m]("data-expand"))&&(n=1*s)||(n=ie),d||(d=!z.expand||z.expand<1?500<v.clientHeight&&500<v.clientWidth?500:370:z.expand,h._defEx=d,c=d*z.expFactor,u=z.hFac,J=null,ie<c&&re<1&&2<ne&&2<P&&!g.hidden?(ie=c,ne=0):ie=1<P&&1<ne&&re<6?d:0),l!==n&&(O=innerWidth+n*u,I=innerHeight+n,o=-1*n,l=n),a=f[t].getBoundingClientRect(),(U=a.bottom)>=o&&($=a.top)<=I&&(j=a.right)>=o*u&&(q=a.left)<=O&&(U||j||q||$)&&(z.loadHidden||se(f[t]))&&(T&&re<3&&!s&&(P<3||ne<4)||le(f[t],n))){if(ve(f[t]),r=!0,9<re)break}else!r&&T&&!i&&re<4&&ne<4&&2<P&&(B[0]||z.preloadAfterLoad)&&(B[0]||!s&&(U||j||q||$||"auto"!=f[t][m](z.sizesAttr)))&&(i=B[0]||f[t]);i&&!r&&ve(i)}},G=de,Q=0,V=z.throttleDelay,X=z.ricTimeout,Y=function(){K=!1,Q=n.now(),G()},Z=s&&49<X?function(){s(Y,{timeout:X}),X!==z.ricTimeout&&(X=z.ricTimeout)}:t(function(){y(Y)},!0),ce=function(e){var t;(e=!0===e)&&(X=33),K||(K=!0,(t=V-(n.now()-Q))<0&&(t=0),e||t<9?Z():y(Z,t))},ue=function(e){var t=e.target;t._lazyCache?delete t._lazyCache:(oe(e),w(t,z.loadedClass),_(t,z.loadingClass),C(t,ge),A(t,"lazyloaded"))},fe=t(ue),ge=function(e){fe({target:e.target})},he=function(e){var t,a=e[m](z.srcsetAttr);(t=z.customMedia[e[m]("data-media")||e[m]("media")])&&e.setAttribute("media",t),a&&e.setAttribute("srcset",a)},ze=t(function(t,e,a,i,r){var n,o,s,l,d,c,u,f,g;(d=A(t,"lazybeforeunveil",e)).defaultPrevented||(i&&(a?w(t,z.autosizesClass):t.setAttribute("sizes",i)),o=t[m](z.srcsetAttr),n=t[m](z.srcAttr),r&&(s=t.parentNode,l=s&&p.test(s.nodeName||"")),c=e.firesLoad||"src"in t&&(o||n||l),d={target:t},w(t,z.loadingClass),c&&(clearTimeout(F),F=y(oe,2500),C(t,ge,!0)),l&&b.call(s.getElementsByTagName("source"),he),o?t.setAttribute("srcset",o):n&&!l&&(te.test(t.nodeName)?(f=n,0==(g=(u=t).getAttribute("data-load-mode")||z.iframeLoadMode)?u.contentWindow.location.replace(f):1==g&&(u.src=f)):t.src=n),r&&(o||l)&&S(t,{src:n})),t._lazyRace&&delete t._lazyRace,_(t,z.lazyClass),E(function(){var e=t.complete&&1<t.naturalWidth;c&&!e||(e&&w(t,z.fastLoadedClass),ue(d),t._lazyCache=!0,y(function(){"_lazyCache"in t&&delete t._lazyCache},9)),"lazy"==t.loading&&re--},!0)}),ve=function(e){if(!e._lazyRace){var t,a=ee.test(e.nodeName),i=a&&(e[m](z.sizesAttr)||e[m]("sizes")),r="auto"==i;(!r&&T||!a||!e[m]("src")&&!e.srcset||e.complete||c(e,z.errorClass)||!c(e,z.lazyClass))&&(t=A(e,"lazyunveilread").detail,r&&W.updateElem(e,!0,e.offsetWidth),e._lazyRace=!0,re++,ze(e,t,r,i,a))}},me=x(function(){z.loadMode=3,ce()}),ye=function(){3==z.loadMode&&(z.loadMode=2),me()},pe=function(){T||(n.now()-D<999?y(pe,999):(T=!0,z.loadMode=3,ce(),e("scroll",ye,!0)))},{_:function(){D=n.now(),h.elements=g.getElementsByClassName(z.lazyClass),B=g.getElementsByClassName(z.lazyClass+" "+z.preloadClass),e("scroll",ce,!0),e("resize",ce,!0),e("pageshow",function(e){if(e.persisted){var t=g.querySelectorAll("."+z.loadingClass);t.length&&t.forEach&&a(function(){t.forEach(function(e){e.complete&&ve(e)})})}}),i.MutationObserver?new MutationObserver(ce).observe(v,{childList:!0,subtree:!0,attributes:!0}):(v[o]("DOMNodeInserted",ce,!0),v[o]("DOMAttrModified",ce,!0),setInterval(ce,999)),e("hashchange",ce,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){g[o](e,ce,!0)}),/d$|^c/.test(g.readyState)?pe():(e("load",pe),g[o]("DOMContentLoaded",ce),y(pe,2e4)),h.elements.length?(de(),E._lsFlush()):ce()},checkElems:ce,unveil:ve,_aLSL:ye}),W=(H=t(function(e,t,a,i){var r,n,o;if(e._lazysizesWidth=i,i+="px",e.setAttribute("sizes",i),p.test(t.nodeName||""))for(r=t.getElementsByTagName("source"),n=0,o=r.length;n<o;n++)r[n].setAttribute("sizes",i);a.detail.dataAttr||S(e,a.detail)}),R=function(e,t,a){var i,r=e.parentNode;r&&(a=f(e,r,a),(i=A(e,"lazybeforesizes",{width:a,dataAttr:!!t})).defaultPrevented||(a=i.detail.width)&&a!==e._lazysizesWidth&&H(e,r,i,a))},k=x(function(){var e,t=N.length;if(t)for(e=0;e<t;e++)R(N[e])}),{_:function(){N=g.getElementsByClassName(z.autosizesClass),e("resize",k)},checkElems:k,updateElem:R}),L=function(){!L.i&&g.getElementsByClassName&&(L.i=!0,W._(),M._())};var N,H,R,k;var B,T,F,P,D,O,I,$,q,j,U,J,G,K,Q,V,X,Y,Z,ee,te,ae,ie,re,ne,oe,se,le,de,ce,ue,fe,ge,he,ze,ve,me,ye,pe;var be,we,_e,Ce,Ae,Se,Ee;return y(function(){z.init&&L()}),h={cfg:z,autoSizer:W,loader:M,init:L,uP:S,aC:w,rC:_,hC:c,fire:A,gW:f,rAF:E}}(e,e.document,Date);e.lazySizes=a,"object"==typeof module&&module.exports&&(module.exports=a)}("undefined"!=typeof window?window:{}),lazySizes.init(),document.addEventListener("lazybeforesizes",function(e){e.target.getAttribute("data-src");void 0!==e.target._lazysizesWidth&&e.detail.width<e.target._lazysizesWidth&&(e.detail.width=e.target._lazysizesWidth)}),document.addEventListener("lazybeforeunveil",function(e){var t=e.target,a=t.getAttribute("data-srcset");if(t.naturalWidth&&!a&&1<t.naturalWidth&&1<t.naturalHeight){var i=window.devicePixelRatio||1,r=t.naturalWidth,n=t.naturalHeight,o=t.getAttribute("data-eio-rwidth"),s=t.getAttribute("data-eio-rheight");o&&r<o&&(r=o,n=s);var l=t.clientWidth&&1.25*t.clientWidth<r,d=t.clientHeight&&1.25*t.clientHeight<n;if(l||d){var c=Math.round(t.offsetWidth*i),u=Math.round(t.offsetHeight*i),f=t.getAttribute("data-src"),g=t.getAttribute("data-src-webp");if(ewww_webp_supported&&g&&-1==f.search("webp=1")&&(f=g),shouldAutoScale(t)&&shouldAutoScale(t.parentNode))if(window.lazySizes.hC(t,"et_pb_jt_filterable_grid_item_image")||window.lazySizes.hC(t,"ss-foreground-image")||window.lazySizes.hC(t,"img-crop"))h=constrainSrc(f,c,u,"img-crop");else h=constrainSrc(f,c,u,"img");else var h=!1;h&&f!=h&&t.setAttribute("data-src",h)}}if(ewww_webp_supported){if(a){var z=t.getAttribute("data-srcset-webp");z&&t.setAttribute("data-srcset",z)}if(!(g=t.getAttribute("data-src-webp")))return;t.setAttribute("data-src",g)}}),function(e,t){var a=function(){t(e.lazySizes),e.removeEventListener("lazyunveilread",a,!0)};t=t.bind(null,e,e.document),"object"==typeof module&&module.exports?t(require("lazysizes")):e.lazySizes?a():e.addEventListener("lazyunveilread",a,!0)}(window,function(o,e,s){"use strict";var l;e.addEventListener&&(l=/\(|\)|\s|'/,addEventListener("lazybeforeunveil",function(e){var t,a;if(e.detail.instance==s&&(!e.defaultPrevented&&("none"==e.target.preload&&(e.target.preload="auto"),t=e.target.getAttribute("data-bg")))){ewww_webp_supported&&(a=e.target.getAttribute("data-bg-webp"))&&(t=a);var i=o.devicePixelRatio||1,r=Math.round(e.target.offsetWidth*i),n=Math.round(e.target.offsetHeight*i);shouldAutoScale(e.target)&&shouldAutoScale(e.target.parentNode)&&(t=o.lazySizes.hC(e.target,"wp-block-cover")?(o.lazySizes.hC(e.target,"has-parallax")&&(r=Math.round(o.screen.width*i),n=Math.round(o.screen.height*i)),constrainSrc(t,r,n,"bg-cover")):o.lazySizes.hC(e.target,"elementor-bg")?constrainSrc(t,r,n,"bg-cover"):constrainSrc(t,r,n,"bg")),e.target.style.backgroundImage="url("+(l.test(t)?JSON.stringify(t):t)+")"}},!1))});
includes/load-webp.js ADDED
@@ -0,0 +1,704 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*globals jQuery,Window,HTMLElement,HTMLDocument,HTMLCollection,NodeList,MutationObserver */
2
+ /*exported Arrive*/
3
+ /*jshint latedef:false */
4
+
5
+ /*
6
+ * arrive.js
7
+ * v2.4.1
8
+ * https://github.com/uzairfarooq/arrive
9
+ * MIT licensed
10
+ *
11
+ * Copyright (c) 2014-2017 Uzair Farooq
12
+ */
13
+ var Arrive = (function(window, $, undefined) {
14
+
15
+ "use strict";
16
+
17
+ if(!window.MutationObserver || typeof HTMLElement === 'undefined'){
18
+ return; //for unsupported browsers
19
+ }
20
+
21
+ var arriveUniqueId = 0;
22
+
23
+ var utils = (function() {
24
+ var matches = HTMLElement.prototype.matches || HTMLElement.prototype.webkitMatchesSelector || HTMLElement.prototype.mozMatchesSelector
25
+ || HTMLElement.prototype.msMatchesSelector;
26
+
27
+ return {
28
+ matchesSelector: function(elem, selector) {
29
+ return elem instanceof HTMLElement && matches.call(elem, selector);
30
+ },
31
+ // to enable function overloading - By John Resig (MIT Licensed)
32
+ addMethod: function (object, name, fn) {
33
+ var old = object[ name ];
34
+ object[ name ] = function(){
35
+ if ( fn.length == arguments.length ) {
36
+ return fn.apply( this, arguments );
37
+ }
38
+ else if ( typeof old == 'function' ) {
39
+ return old.apply( this, arguments );
40
+ }
41
+ };
42
+ },
43
+ callCallbacks: function(callbacksToBeCalled, registrationData) {
44
+ if (registrationData && registrationData.options.onceOnly && registrationData.firedElems.length == 1) {
45
+ // as onlyOnce param is true, make sure we fire the event for only one item
46
+ callbacksToBeCalled = [callbacksToBeCalled[0]];
47
+ }
48
+
49
+ for (var i = 0, cb; (cb = callbacksToBeCalled[i]); i++) {
50
+ if (cb && cb.callback) {
51
+ cb.callback.call(cb.elem, cb.elem);
52
+ }
53
+ }
54
+
55
+ if (registrationData && registrationData.options.onceOnly && registrationData.firedElems.length == 1) {
56
+ // unbind event after first callback as onceOnly is true.
57
+ registrationData.me.unbindEventWithSelectorAndCallback.call(
58
+ registrationData.target, registrationData.selector, registrationData.callback
59
+ );
60
+ }
61
+ },
62
+ // traverse through all descendants of a node to check if event should be fired for any descendant
63
+ checkChildNodesRecursively: function(nodes, registrationData, matchFunc, callbacksToBeCalled) {
64
+ // check each new node if it matches the selector
65
+ for (var i=0, node; (node = nodes[i]); i++) {
66
+ if (matchFunc(node, registrationData, callbacksToBeCalled)) {
67
+ callbacksToBeCalled.push({ callback: registrationData.callback, elem: node });
68
+ }
69
+
70
+ if (node.childNodes.length > 0) {
71
+ utils.checkChildNodesRecursively(node.childNodes, registrationData, matchFunc, callbacksToBeCalled);
72
+ }
73
+ }
74
+ },
75
+ mergeArrays: function(firstArr, secondArr){
76
+ // Overwrites default options with user-defined options.
77
+ var options = {},
78
+ attrName;
79
+ for (attrName in firstArr) {
80
+ if (firstArr.hasOwnProperty(attrName)) {
81
+ options[attrName] = firstArr[attrName];
82
+ }
83
+ }
84
+ for (attrName in secondArr) {
85
+ if (secondArr.hasOwnProperty(attrName)) {
86
+ options[attrName] = secondArr[attrName];
87
+ }
88
+ }
89
+ return options;
90
+ },
91
+ toElementsArray: function (elements) {
92
+ // check if object is an array (or array like object)
93
+ // Note: window object has .length property but it's not array of elements so don't consider it an array
94
+ if (typeof elements !== "undefined" && (typeof elements.length !== "number" || elements === window)) {
95
+ elements = [elements];
96
+ }
97
+ return elements;
98
+ }
99
+ };
100
+ })();
101
+
102
+
103
+ // Class to maintain state of all registered events of a single type
104
+ var EventsBucket = (function() {
105
+ var EventsBucket = function() {
106
+ // holds all the events
107
+
108
+ this._eventsBucket = [];
109
+ // function to be called while adding an event, the function should do the event initialization/registration
110
+ this._beforeAdding = null;
111
+ // function to be called while removing an event, the function should do the event destruction
112
+ this._beforeRemoving = null;
113
+ };
114
+
115
+ EventsBucket.prototype.addEvent = function(target, selector, options, callback) {
116
+ var newEvent = {
117
+ target: target,
118
+ selector: selector,
119
+ options: options,
120
+ callback: callback,
121
+ firedElems: []
122
+ };
123
+
124
+ if (this._beforeAdding) {
125
+ this._beforeAdding(newEvent);
126
+ }
127
+
128
+ this._eventsBucket.push(newEvent);
129
+ return newEvent;
130
+ };
131
+
132
+ EventsBucket.prototype.removeEvent = function(compareFunction) {
133
+ for (var i=this._eventsBucket.length - 1, registeredEvent; (registeredEvent = this._eventsBucket[i]); i--) {
134
+ if (compareFunction(registeredEvent)) {
135
+ if (this._beforeRemoving) {
136
+ this._beforeRemoving(registeredEvent);
137
+ }
138
+
139
+ // mark callback as null so that even if an event mutation was already triggered it does not call callback
140
+ var removedEvents = this._eventsBucket.splice(i, 1);
141
+ if (removedEvents && removedEvents.length) {
142
+ removedEvents[0].callback = null;
143
+ }
144
+ }
145
+ }
146
+ };
147
+
148
+ EventsBucket.prototype.beforeAdding = function(beforeAdding) {
149
+ this._beforeAdding = beforeAdding;
150
+ };
151
+
152
+ EventsBucket.prototype.beforeRemoving = function(beforeRemoving) {
153
+ this._beforeRemoving = beforeRemoving;
154
+ };
155
+
156
+ return EventsBucket;
157
+ })();
158
+
159
+
160
+ /**
161
+ * @constructor
162
+ * General class for binding/unbinding arrive and leave events
163
+ */
164
+ var MutationEvents = function(getObserverConfig, onMutation) {
165
+ var eventsBucket = new EventsBucket(),
166
+ me = this;
167
+
168
+ var defaultOptions = {
169
+ fireOnAttributesModification: false
170
+ };
171
+
172
+ // actual event registration before adding it to bucket
173
+ eventsBucket.beforeAdding(function(registrationData) {
174
+ var
175
+ target = registrationData.target,
176
+ observer;
177
+
178
+ // mutation observer does not work on window or document
179
+ if (target === window.document || target === window) {
180
+ target = document.getElementsByTagName("html")[0];
181
+ }
182
+
183
+ // Create an observer instance
184
+ observer = new MutationObserver(function(e) {
185
+ onMutation.call(this, e, registrationData);
186
+ });
187
+
188
+ var config = getObserverConfig(registrationData.options);
189
+
190
+ observer.observe(target, config);
191
+
192
+ registrationData.observer = observer;
193
+ registrationData.me = me;
194
+ });
195
+
196
+ // cleanup/unregister before removing an event
197
+ eventsBucket.beforeRemoving(function (eventData) {
198
+ eventData.observer.disconnect();
199
+ });
200
+
201
+ this.bindEvent = function(selector, options, callback) {
202
+ options = utils.mergeArrays(defaultOptions, options);
203
+
204
+ var elements = utils.toElementsArray(this);
205
+
206
+ for (var i = 0; i < elements.length; i++) {
207
+ eventsBucket.addEvent(elements[i], selector, options, callback);
208
+ }
209
+ };
210
+
211
+ this.unbindEvent = function() {
212
+ var elements = utils.toElementsArray(this);
213
+ eventsBucket.removeEvent(function(eventObj) {
214
+ for (var i = 0; i < elements.length; i++) {
215
+ if (this === undefined || eventObj.target === elements[i]) {
216
+ return true;
217
+ }
218
+ }
219
+ return false;
220
+ });
221
+ };
222
+
223
+ this.unbindEventWithSelectorOrCallback = function(selector) {
224
+ var elements = utils.toElementsArray(this),
225
+ callback = selector,
226
+ compareFunction;
227
+
228
+ if (typeof selector === "function") {
229
+ compareFunction = function(eventObj) {
230
+ for (var i = 0; i < elements.length; i++) {
231
+ if ((this === undefined || eventObj.target === elements[i]) && eventObj.callback === callback) {
232
+ return true;
233
+ }
234
+ }
235
+ return false;
236
+ };
237
+ }
238
+ else {
239
+ compareFunction = function(eventObj) {
240
+ for (var i = 0; i < elements.length; i++) {
241
+ if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector) {
242
+ return true;
243
+ }
244
+ }
245
+ return false;
246
+ };
247
+ }
248
+ eventsBucket.removeEvent(compareFunction);
249
+ };
250
+
251
+ this.unbindEventWithSelectorAndCallback = function(selector, callback) {
252
+ var elements = utils.toElementsArray(this);
253
+ eventsBucket.removeEvent(function(eventObj) {
254
+ for (var i = 0; i < elements.length; i++) {
255
+ if ((this === undefined || eventObj.target === elements[i]) && eventObj.selector === selector && eventObj.callback === callback) {
256
+ return true;
257
+ }
258
+ }
259
+ return false;
260
+ });
261
+ };
262
+
263
+ return this;
264
+ };
265
+
266
+
267
+ /**
268
+ * @constructor
269
+ * Processes 'arrive' events
270
+ */
271
+ var ArriveEvents = function() {
272
+ // Default options for 'arrive' event
273
+ var arriveDefaultOptions = {
274
+ fireOnAttributesModification: false,
275
+ onceOnly: false,
276
+ existing: false
277
+ };
278
+
279
+ function getArriveObserverConfig(options) {
280
+ var config = {
281
+ attributes: false,
282
+ childList: true,
283
+ subtree: true
284
+ };
285
+
286
+ if (options.fireOnAttributesModification) {
287
+ config.attributes = true;
288
+ }
289
+
290
+ return config;
291
+ }
292
+
293
+ function onArriveMutation(mutations, registrationData) {
294
+ mutations.forEach(function( mutation ) {
295
+ var newNodes = mutation.addedNodes,
296
+ targetNode = mutation.target,
297
+ callbacksToBeCalled = [],
298
+ node;
299
+
300
+ // If new nodes are added
301
+ if( newNodes !== null && newNodes.length > 0 ) {
302
+ utils.checkChildNodesRecursively(newNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
303
+ }
304
+ else if (mutation.type === "attributes") {
305
+ if (nodeMatchFunc(targetNode, registrationData, callbacksToBeCalled)) {
306
+ callbacksToBeCalled.push({ callback: registrationData.callback, elem: targetNode });
307
+ }
308
+ }
309
+
310
+ utils.callCallbacks(callbacksToBeCalled, registrationData);
311
+ });
312
+ }
313
+
314
+ function nodeMatchFunc(node, registrationData, callbacksToBeCalled) {
315
+ // check a single node to see if it matches the selector
316
+ if (utils.matchesSelector(node, registrationData.selector)) {
317
+ if(node._id === undefined) {
318
+ node._id = arriveUniqueId++;
319
+ }
320
+ // make sure the arrive event is not already fired for the element
321
+ if (registrationData.firedElems.indexOf(node._id) == -1) {
322
+ registrationData.firedElems.push(node._id);
323
+
324
+ return true;
325
+ }
326
+ }
327
+
328
+ return false;
329
+ }
330
+
331
+ arriveEvents = new MutationEvents(getArriveObserverConfig, onArriveMutation);
332
+
333
+ var mutationBindEvent = arriveEvents.bindEvent;
334
+
335
+ // override bindEvent function
336
+ arriveEvents.bindEvent = function(selector, options, callback) {
337
+
338
+ if (typeof callback === "undefined") {
339
+ callback = options;
340
+ options = arriveDefaultOptions;
341
+ } else {
342
+ options = utils.mergeArrays(arriveDefaultOptions, options);
343
+ }
344
+
345
+ var elements = utils.toElementsArray(this);
346
+
347
+ if (options.existing) {
348
+ var existing = [];
349
+
350
+ for (var i = 0; i < elements.length; i++) {
351
+ var nodes = elements[i].querySelectorAll(selector);
352
+ for (var j = 0; j < nodes.length; j++) {
353
+ existing.push({ callback: callback, elem: nodes[j] });
354
+ }
355
+ }
356
+
357
+ // no need to bind event if the callback has to be fired only once and we have already found the element
358
+ if (options.onceOnly && existing.length) {
359
+ return callback.call(existing[0].elem, existing[0].elem);
360
+ }
361
+
362
+ setTimeout(utils.callCallbacks, 1, existing);
363
+ }
364
+
365
+ mutationBindEvent.call(this, selector, options, callback);
366
+ };
367
+
368
+ return arriveEvents;
369
+ };
370
+
371
+
372
+ /**
373
+ * @constructor
374
+ * Processes 'leave' events
375
+ */
376
+ var LeaveEvents = function() {
377
+ // Default options for 'leave' event
378
+ var leaveDefaultOptions = {};
379
+
380
+ function getLeaveObserverConfig() {
381
+ var config = {
382
+ childList: true,
383
+ subtree: true
384
+ };
385
+
386
+ return config;
387
+ }
388
+
389
+ function onLeaveMutation(mutations, registrationData) {
390
+ mutations.forEach(function( mutation ) {
391
+ var removedNodes = mutation.removedNodes,
392
+ callbacksToBeCalled = [];
393
+
394
+ if( removedNodes !== null && removedNodes.length > 0 ) {
395
+ utils.checkChildNodesRecursively(removedNodes, registrationData, nodeMatchFunc, callbacksToBeCalled);
396
+ }
397
+
398
+ utils.callCallbacks(callbacksToBeCalled, registrationData);
399
+ });
400
+ }
401
+
402
+ function nodeMatchFunc(node, registrationData) {
403
+ return utils.matchesSelector(node, registrationData.selector);
404
+ }
405
+
406
+ leaveEvents = new MutationEvents(getLeaveObserverConfig, onLeaveMutation);
407
+
408
+ var mutationBindEvent = leaveEvents.bindEvent;
409
+
410
+ // override bindEvent function
411
+ leaveEvents.bindEvent = function(selector, options, callback) {
412
+
413
+ if (typeof callback === "undefined") {
414
+ callback = options;
415
+ options = leaveDefaultOptions;
416
+ } else {
417
+ options = utils.mergeArrays(leaveDefaultOptions, options);
418
+ }
419
+
420
+ mutationBindEvent.call(this, selector, options, callback);
421
+ };
422
+
423
+ return leaveEvents;
424
+ };
425
+
426
+
427
+ var arriveEvents = new ArriveEvents(),
428
+ leaveEvents = new LeaveEvents();
429
+
430
+ function exposeUnbindApi(eventObj, exposeTo, funcName) {
431
+ // expose unbind function with function overriding
432
+ utils.addMethod(exposeTo, funcName, eventObj.unbindEvent);
433
+ utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorOrCallback);
434
+ utils.addMethod(exposeTo, funcName, eventObj.unbindEventWithSelectorAndCallback);
435
+ }
436
+
437
+ /*** expose APIs ***/
438
+ function exposeApi(exposeTo) {
439
+ exposeTo.arrive = arriveEvents.bindEvent;
440
+ exposeUnbindApi(arriveEvents, exposeTo, "unbindArrive");
441
+
442
+ exposeTo.leave = leaveEvents.bindEvent;
443
+ exposeUnbindApi(leaveEvents, exposeTo, "unbindLeave");
444
+ }
445
+
446
+ if ($) {
447
+ exposeApi($.fn);
448
+ }
449
+ exposeApi(HTMLElement.prototype);
450
+ exposeApi(NodeList.prototype);
451
+ exposeApi(HTMLCollection.prototype);
452
+ exposeApi(HTMLDocument.prototype);
453
+ exposeApi(Window.prototype);
454
+
455
+ var Arrive = {};
456
+ // expose functions to unbind all arrive/leave events
457
+ exposeUnbindApi(arriveEvents, Arrive, "unbindAllArrive");
458
+ exposeUnbindApi(leaveEvents, Arrive, "unbindAllLeave");
459
+
460
+ return Arrive;
461
+
462
+ })(window, typeof jQuery === 'undefined' ? null : jQuery, undefined);
463
+
464
+ var ewww_webp_supported = false;
465
+ // webp detection adapted from https://developers.google.com/speed/webp/faq#how_can_i_detect_browser_support_using_javascript
466
+ function check_webp_feature(feature, callback) {
467
+ if (ewww_webp_supported) {
468
+ callback(ewww_webp_supported);
469
+ return;
470
+ }
471
+ var kTestImages = {
472
+ alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
473
+ animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"
474
+ };
475
+ var img = new Image();
476
+ img.onload = function () {
477
+ ewww_webp_supported = (img.width > 0) && (img.height > 0);
478
+ callback(ewww_webp_supported);
479
+ };
480
+ img.onerror = function () {
481
+ callback(false);
482
+ };
483
+ img.src = "data:image/webp;base64," + kTestImages[feature];
484
+ }
485
+ function ewwwLoadImages(ewww_webp_supported) {
486
+ if (ewww_webp_supported) {
487
+ var nggImages = document.querySelectorAll('.batch-image img, .image-wrapper a, .ngg-pro-masonry-item a, .ngg-galleria-offscreen-seo-wrapper a');
488
+ for (var i = 0, len = nggImages.length; i < len; i++){
489
+ ewwwAttr(nggImages[i], 'data-src', nggImages[i].getAttribute('data-webp'));
490
+ ewwwAttr(nggImages[i], 'data-thumbnail', nggImages[i].getAttribute('data-webp-thumbnail'));
491
+ }
492
+ var revImages = document.querySelectorAll('.rev_slider ul li');
493
+ for (var i = 0, len = revImages.length; i < len; i++){
494
+ ewwwAttr(revImages[i], 'data-thumb', revImages[i].getAttribute('data-webp-thumb'));
495
+ var param_num = 1;
496
+ while ( param_num < 11 ) {
497
+ ewwwAttr(revImages[i], 'data-param' + param_num, revImages[i].getAttribute('data-webp-param' + param_num));
498
+ param_num++;
499
+ }
500
+ }
501
+ var revImages = document.querySelectorAll('.rev_slider img');
502
+ for (var i = 0, len = revImages.length; i < len; i++){
503
+ ewwwAttr(revImages[i], 'data-lazyload', revImages[i].getAttribute('data-webp-lazyload'));
504
+ }
505
+ var wooImages = document.querySelectorAll('div.woocommerce-product-gallery__image');
506
+ for (var i = 0, len = wooImages.length; i < len; i++){
507
+ ewwwAttr(wooImages[i], 'data-thumb', wooImages[i].getAttribute('data-webp-thumb'));
508
+ }
509
+ }
510
+ var videos = document.querySelectorAll('video');
511
+ for (var i = 0, len = videos.length; i < len; i++){
512
+ if (ewww_webp_supported) {
513
+ ewwwAttr(videos[i], 'poster', videos[i].getAttribute('data-poster-webp'));
514
+ } else {
515
+ ewwwAttr(videos[i], 'poster', videos[i].getAttribute('data-poster-image'));
516
+ }
517
+ }
518
+ var lazies = document.querySelectorAll('img.ewww_webp_lazy_load');
519
+ for (var i = 0, len = lazies.length; i < len; i++){
520
+ console.log('parsing an image: ' + lazies[i].getAttribute('data-src'));
521
+ if (ewww_webp_supported) {
522
+ console.log('webp good');
523
+ ewwwAttr(lazies[i], 'data-lazy-srcset', lazies[i].getAttribute('data-lazy-srcset-webp'));
524
+ ewwwAttr(lazies[i], 'data-srcset', lazies[i].getAttribute('data-srcset-webp'));
525
+ ewwwAttr(lazies[i], 'data-lazy-src', lazies[i].getAttribute('data-lazy-src-webp'));
526
+ ewwwAttr(lazies[i], 'data-src', lazies[i].getAttribute('data-src-webp'));
527
+ ewwwAttr(lazies[i], 'data-orig-file', lazies[i].getAttribute('data-webp-orig-file'));
528
+ ewwwAttr(lazies[i], 'data-medium-file', lazies[i].getAttribute('data-webp-medium-file'));
529
+ ewwwAttr(lazies[i], 'data-large-file', lazies[i].getAttribute('data-webp-large-file'));
530
+ var jpsrcset = lazies[i].getAttribute('srcset');
531
+ if (jpsrcset != null && jpsrcset !== false && jpsrcset.includes('R0lGOD')) {
532
+ ewwwAttr(lazies[i], 'src', lazies[i].getAttribute('data-lazy-src-webp'));
533
+ }
534
+ }
535
+ lazies[i].className = lazies[i].className.replace(/\bewww_webp_lazy_load\b/, '');
536
+ }
537
+ var elems = document.querySelectorAll('.ewww_webp');
538
+ for (var i = 0, len = elems.length; i < len; i++){
539
+ console.log('parsing an image: ' + elems[i].getAttribute('data-src'));
540
+ if (ewww_webp_supported) {
541
+ ewwwAttr(elems[i], 'srcset', elems[i].getAttribute('data-srcset-webp'));
542
+ ewwwAttr(elems[i], 'src', elems[i].getAttribute('data-src-webp'));
543
+ ewwwAttr(elems[i], 'data-orig-file', elems[i].getAttribute('data-webp-orig-file'));
544
+ ewwwAttr(elems[i], 'data-medium-file', elems[i].getAttribute('data-webp-medium-file'));
545
+ ewwwAttr(elems[i], 'data-large-file', elems[i].getAttribute('data-webp-large-file'));
546
+ ewwwAttr(elems[i], 'data-large_image', elems[i].getAttribute('data-webp-large_image'));
547
+ ewwwAttr(elems[i], 'data-src', elems[i].getAttribute('data-webp-src'));
548
+ } else {
549
+ ewwwAttr(elems[i], 'srcset', elems[i].getAttribute('data-srcset-img'));
550
+ ewwwAttr(elems[i], 'src', elems[i].getAttribute('data-src-img'));
551
+ }
552
+ elems[i].className = elems[i].className.replace(/\bewww_webp\b/, 'ewww_webp_loaded');
553
+ }
554
+ if (window.jQuery && jQuery.fn.isotope && jQuery.fn.imagesLoaded) {
555
+ jQuery('.fusion-posts-container-infinite').imagesLoaded( function() {
556
+ if ( jQuery( '.fusion-posts-container-infinite' ).hasClass( 'isotope' ) ) {
557
+ jQuery( '.fusion-posts-container-infinite' ).isotope();
558
+ }
559
+ });
560
+ jQuery('.fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper').imagesLoaded( function() {
561
+ jQuery( '.fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper' ).isotope();
562
+ });
563
+ }
564
+ }
565
+ check_webp_feature('alpha', ewwwWebPInit);
566
+ function ewwwWebPInit(ewww_webp_supported) {
567
+ ewwwLoadImages(ewww_webp_supported);
568
+ ewwwNggLoadGalleries(ewww_webp_supported);
569
+ document.arrive('.ewww_webp', function() {
570
+ ewwwLoadImages(ewww_webp_supported);
571
+ });
572
+ document.arrive('.ewww_webp_lazy_load', function() {
573
+ ewwwLoadImages(ewww_webp_supported);
574
+ });
575
+ document.arrive('videos', function() {
576
+ ewwwLoadImages(ewww_webp_supported);
577
+ });
578
+ if (document.readyState == 'loading') {
579
+ console.log('deferring ewwwJSONParserInit until DOMContentLoaded')
580
+ document.addEventListener('DOMContentLoaded', ewwwJSONParserInit);
581
+ } else {
582
+ console.log(document.readyState);
583
+ console.log('running JSON parsers post haste')
584
+ if ( typeof galleries !== 'undefined' ) {
585
+ console.log('galleries found, parsing')
586
+ ewwwNggParseGalleries(ewww_webp_supported);
587
+ }
588
+ ewwwWooParseVariations(ewww_webp_supported);
589
+ }
590
+ }
591
+ function ewwwAttr(elem, attr, value) {
592
+ if (value != null && value !== false) {
593
+ elem.setAttribute(attr, value);
594
+ }
595
+ }
596
+ function ewwwJSONParserInit() {
597
+ if ( typeof galleries !== 'undefined' ) {
598
+ check_webp_feature('alpha', ewwwNggParseGalleries);
599
+ }
600
+ check_webp_feature('alpha', ewwwWooParseVariations);
601
+ }
602
+ function ewwwWooParseVariations(ewww_webp_supported) {
603
+ if (!ewww_webp_supported) {
604
+ return;
605
+ }
606
+ var elems = document.querySelectorAll('form.variations_form');
607
+ for (var i = 0, len = elems.length; i < len; i++){
608
+ var variations = elems[i].getAttribute('data-product_variations');
609
+ var variations_changed = false;
610
+ try {
611
+ variations = JSON.parse(variations);
612
+ //console.log(variations);
613
+ console.log('parsing WC variations');
614
+ for ( var num in variations ) {
615
+ if (variations[ num ] !== undefined && variations[ num ].image !== undefined) {
616
+ console.log(variations[num].image);
617
+ if (variations[num].image.src_webp !== undefined) {
618
+ variations[num].image.src = variations[num].image.src_webp;
619
+ variations_changed = true;
620
+ }
621
+ if (variations[num].image.srcset_webp !== undefined) {
622
+ variations[num].image.srcset = variations[num].image.srcset_webp;
623
+ variations_changed = true;
624
+ }
625
+ if (variations[num].image.full_src_webp !== undefined) {
626
+ variations[num].image.full_src = variations[num].image.full_src_webp;
627
+ variations_changed = true;
628
+ }
629
+ if (variations[num].image.gallery_thumbnail_src_webp !== undefined) {
630
+ variations[num].image.gallery_thumbnail_src = variations[num].image.gallery_thumbnail_src_webp;
631
+ variations_changed = true;
632
+ }
633
+ if (variations[num].image.thumb_src_webp !== undefined) {
634
+ variations[num].image.thumb_src = variations[num].image.thumb_src_webp;
635
+ variations_changed = true;
636
+ }
637
+ }
638
+ }
639
+ if (variations_changed) {
640
+ ewwwAttr(elems[i], 'data-product_variations', JSON.stringify(variations));
641
+ }
642
+ } catch (err) {
643
+ console.log(err);
644
+ console.log(response);
645
+ }
646
+ }
647
+ }
648
+ function ewwwNggParseGalleries(ewww_webp_supported) {
649
+ if (ewww_webp_supported) {
650
+ for(var galleryIndex in galleries) {
651
+ var gallery = galleries[galleryIndex];
652
+ galleries[galleryIndex].images_list = ewwwNggParseImageList(gallery.images_list);
653
+ }
654
+ }
655
+ }
656
+ function ewwwNggLoadGalleries(ewww_webp_supported) {
657
+ if (ewww_webp_supported) {
658
+ document.addEventListener('ngg.galleria.themeadded', function(event, themename){
659
+ window.ngg_galleria._create_backup = window.ngg_galleria.create;
660
+ window.ngg_galleria.create = function(gallery_parent, themename) {
661
+ var gallery_id = $(gallery_parent).data('id');
662
+ galleries['gallery_' + gallery_id].images_list = ewwwNggParseImageList(galleries['gallery_' + gallery_id].images_list);
663
+ return window.ngg_galleria._create_backup(gallery_parent, themename);
664
+ };
665
+ });
666
+ }
667
+ }
668
+ function ewwwNggParseImageList(images_list) {
669
+ console.log('parsing gallery images');
670
+ for(var nggIndex in images_list) {
671
+ var nggImage = images_list[nggIndex];
672
+ if (typeof nggImage['image-webp'] !== typeof undefined) {
673
+ images_list[nggIndex]['image'] = nggImage['image-webp'];
674
+ delete images_list[nggIndex]['image-webp'];
675
+ }
676
+ if (typeof nggImage['thumb-webp'] !== typeof undefined) {
677
+ images_list[nggIndex]['thumb'] = nggImage['thumb-webp'];
678
+ delete images_list[nggIndex]['thumb-webp'];
679
+ }
680
+ if (typeof nggImage['full_image_webp'] !== typeof undefined) {
681
+ images_list[nggIndex]['full_image'] = nggImage['full_image_webp'];
682
+ delete images_list[nggIndex]['full_image_webp'];
683
+ }
684
+ if (typeof nggImage['srcsets'] !== typeof undefined) {
685
+ for(var nggSrcsetIndex in nggImage['srcsets']) {
686
+ nggSrcset = nggImage['srcsets'][nggSrcsetIndex];
687
+ if (typeof nggImage['srcsets'][nggSrcsetIndex + '-webp'] !== typeof undefined) {
688
+ images_list[nggIndex]['srcsets'][nggSrcsetIndex] = nggImage['srcsets'][nggSrcsetIndex + '-webp'];
689
+ delete images_list[nggIndex]['srcsets'][nggSrcsetIndex + '-webp'];
690
+ }
691
+ }
692
+ }
693
+ if (typeof nggImage['full_srcsets'] !== typeof undefined) {
694
+ for(var nggFSrcsetIndex in nggImage['full_srcsets']) {
695
+ nggFSrcset = nggImage['full_srcsets'][nggFSrcsetIndex];
696
+ if (typeof nggImage['full_srcsets'][nggFSrcsetIndex + '-webp'] !== typeof undefined) {
697
+ images_list[nggIndex]['full_srcsets'][nggFSrcsetIndex] = nggImage['full_srcsets'][nggFSrcsetIndex + '-webp'];
698
+ delete images_list[nggIndex]['full_srcsets'][nggFSrcsetIndex + '-webp'];
699
+ }
700
+ }
701
+ }
702
+ }
703
+ return images_list;
704
+ }
includes/load-webp.min.js ADDED
@@ -0,0 +1 @@
 
1
+ var Arrive=function(c,e,w){"use strict";if(c.MutationObserver&&"undefined"!=typeof HTMLElement){var r,t,a=0,u=(r=HTMLElement.prototype.matches||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector,{matchesSelector:function(e,t){return e instanceof HTMLElement&&r.call(e,t)},addMethod:function(e,t,r){var a=e[t];e[t]=function(){return r.length==arguments.length?r.apply(this,arguments):"function"==typeof a?a.apply(this,arguments):void 0}},callCallbacks:function(e,t){t&&t.options.onceOnly&&1==t.firedElems.length&&(e=[e[0]]);for(var r,a=0;r=e[a];a++)r&&r.callback&&r.callback.call(r.elem,r.elem);t&&t.options.onceOnly&&1==t.firedElems.length&&t.me.unbindEventWithSelectorAndCallback.call(t.target,t.selector,t.callback)},checkChildNodesRecursively:function(e,t,r,a){for(var i,n=0;i=e[n];n++)r(i,t,a)&&a.push({callback:t.callback,elem:i}),0<i.childNodes.length&&u.checkChildNodesRecursively(i.childNodes,t,r,a)},mergeArrays:function(e,t){var r,a={};for(r in e)e.hasOwnProperty(r)&&(a[r]=e[r]);for(r in t)t.hasOwnProperty(r)&&(a[r]=t[r]);return a},toElementsArray:function(e){return void 0===e||"number"==typeof e.length&&e!==c||(e=[e]),e}}),d=((t=function(){this._eventsBucket=[],this._beforeAdding=null,this._beforeRemoving=null}).prototype.addEvent=function(e,t,r,a){var i={target:e,selector:t,options:r,callback:a,firedElems:[]};return this._beforeAdding&&this._beforeAdding(i),this._eventsBucket.push(i),i},t.prototype.removeEvent=function(e){for(var t,r=this._eventsBucket.length-1;t=this._eventsBucket[r];r--)if(e(t)){this._beforeRemoving&&this._beforeRemoving(t);var a=this._eventsBucket.splice(r,1);a&&a.length&&(a[0].callback=null)}},t.prototype.beforeAdding=function(e){this._beforeAdding=e},t.prototype.beforeRemoving=function(e){this._beforeRemoving=e},t),o=function(i,n){var o=new d,l=this,s={fireOnAttributesModification:!1};return o.beforeAdding(function(t){var e,r=t.target;r!==c.document&&r!==c||(r=document.getElementsByTagName("html")[0]),e=new MutationObserver(function(e){n.call(this,e,t)});var a=i(t.options);e.observe(r,a),t.observer=e,t.me=l}),o.beforeRemoving(function(e){e.observer.disconnect()}),this.bindEvent=function(e,t,r){t=u.mergeArrays(s,t);for(var a=u.toElementsArray(this),i=0;i<a.length;i++)o.addEvent(a[i],e,t,r)},this.unbindEvent=function(){var r=u.toElementsArray(this);o.removeEvent(function(e){for(var t=0;t<r.length;t++)if(this===w||e.target===r[t])return!0;return!1})},this.unbindEventWithSelectorOrCallback=function(r){var e,a=u.toElementsArray(this),i=r;e="function"==typeof r?function(e){for(var t=0;t<a.length;t++)if((this===w||e.target===a[t])&&e.callback===i)return!0;return!1}:function(e){for(var t=0;t<a.length;t++)if((this===w||e.target===a[t])&&e.selector===r)return!0;return!1},o.removeEvent(e)},this.unbindEventWithSelectorAndCallback=function(r,a){var i=u.toElementsArray(this);o.removeEvent(function(e){for(var t=0;t<i.length;t++)if((this===w||e.target===i[t])&&e.selector===r&&e.callback===a)return!0;return!1})},this},i=new function(){var s={fireOnAttributesModification:!1,onceOnly:!1,existing:!1};function n(e,t,r){return!(!u.matchesSelector(e,t.selector)||(e._id===w&&(e._id=a++),-1!=t.firedElems.indexOf(e._id))||(t.firedElems.push(e._id),0))}var c=(i=new o(function(e){var t={attributes:!1,childList:!0,subtree:!0};return e.fireOnAttributesModification&&(t.attributes=!0),t},function(e,i){e.forEach(function(e){var t=e.addedNodes,r=e.target,a=[];null!==t&&0<t.length?u.checkChildNodesRecursively(t,i,n,a):"attributes"===e.type&&n(r,i)&&a.push({callback:i.callback,elem:r}),u.callCallbacks(a,i)})})).bindEvent;return i.bindEvent=function(e,t,r){t=void 0===r?(r=t,s):u.mergeArrays(s,t);var a=u.toElementsArray(this);if(t.existing){for(var i=[],n=0;n<a.length;n++)for(var o=a[n].querySelectorAll(e),l=0;l<o.length;l++)i.push({callback:r,elem:o[l]});if(t.onceOnly&&i.length)return r.call(i[0].elem,i[0].elem);setTimeout(u.callCallbacks,1,i)}c.call(this,e,t,r)},i},l=new function(){var a={};function i(e,t){return u.matchesSelector(e,t.selector)}var n=(l=new o(function(){return{childList:!0,subtree:!0}},function(e,a){e.forEach(function(e){var t=e.removedNodes,r=[];null!==t&&0<t.length&&u.checkChildNodesRecursively(t,a,i,r),u.callCallbacks(r,a)})})).bindEvent;return l.bindEvent=function(e,t,r){t=void 0===r?(r=t,a):u.mergeArrays(a,t),n.call(this,e,t,r)},l};e&&g(e.fn),g(HTMLElement.prototype),g(NodeList.prototype),g(HTMLCollection.prototype),g(HTMLDocument.prototype),g(Window.prototype);var n={};return s(i,n,"unbindAllArrive"),s(l,n,"unbindAllLeave"),n}function s(e,t,r){u.addMethod(t,r,e.unbindEvent),u.addMethod(t,r,e.unbindEventWithSelectorOrCallback),u.addMethod(t,r,e.unbindEventWithSelectorAndCallback)}function g(e){e.arrive=i.bindEvent,s(i,e,"unbindArrive"),e.leave=l.bindEvent,s(l,e,"unbindLeave")}}(window,"undefined"==typeof jQuery?null:jQuery,void 0),ewww_webp_supported=!1;function check_webp_feature(e,t){if(ewww_webp_supported)t(ewww_webp_supported);else{var r=new Image;r.onload=function(){ewww_webp_supported=0<r.width&&0<r.height,t(ewww_webp_supported)},r.onerror=function(){t(!1)},r.src="data:image/webp;base64,"+{alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",animation:"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"}[e]}}function ewwwLoadImages(e){if(e){for(var t=document.querySelectorAll(".batch-image img, .image-wrapper a, .ngg-pro-masonry-item a, .ngg-galleria-offscreen-seo-wrapper a"),r=0,a=t.length;r<a;r++)ewwwAttr(t[r],"data-src",t[r].getAttribute("data-webp")),ewwwAttr(t[r],"data-thumbnail",t[r].getAttribute("data-webp-thumbnail"));for(r=0,a=(n=document.querySelectorAll(".rev_slider ul li")).length;r<a;r++){ewwwAttr(n[r],"data-thumb",n[r].getAttribute("data-webp-thumb"));for(var i=1;i<11;)ewwwAttr(n[r],"data-param"+i,n[r].getAttribute("data-webp-param"+i)),i++}var n;for(r=0,a=(n=document.querySelectorAll(".rev_slider img")).length;r<a;r++)ewwwAttr(n[r],"data-lazyload",n[r].getAttribute("data-webp-lazyload"));var o=document.querySelectorAll("div.woocommerce-product-gallery__image");for(r=0,a=o.length;r<a;r++)ewwwAttr(o[r],"data-thumb",o[r].getAttribute("data-webp-thumb"))}var l=document.querySelectorAll("video");for(r=0,a=l.length;r<a;r++)ewwwAttr(l[r],"poster",e?l[r].getAttribute("data-poster-webp"):l[r].getAttribute("data-poster-image"));var s=document.querySelectorAll("img.ewww_webp_lazy_load");for(r=0,a=s.length;r<a;r++){if(e){ewwwAttr(s[r],"data-lazy-srcset",s[r].getAttribute("data-lazy-srcset-webp")),ewwwAttr(s[r],"data-srcset",s[r].getAttribute("data-srcset-webp")),ewwwAttr(s[r],"data-lazy-src",s[r].getAttribute("data-lazy-src-webp")),ewwwAttr(s[r],"data-src",s[r].getAttribute("data-src-webp")),ewwwAttr(s[r],"data-orig-file",s[r].getAttribute("data-webp-orig-file")),ewwwAttr(s[r],"data-medium-file",s[r].getAttribute("data-webp-medium-file")),ewwwAttr(s[r],"data-large-file",s[r].getAttribute("data-webp-large-file"));var c=s[r].getAttribute("srcset");null!=c&&!1!==c&&c.includes("R0lGOD")&&ewwwAttr(s[r],"src",s[r].getAttribute("data-lazy-src-webp"))}s[r].className=s[r].className.replace(/\bewww_webp_lazy_load\b/,"")}var w=document.querySelectorAll(".ewww_webp");for(r=0,a=w.length;r<a;r++)e?(ewwwAttr(w[r],"srcset",w[r].getAttribute("data-srcset-webp")),ewwwAttr(w[r],"src",w[r].getAttribute("data-src-webp")),ewwwAttr(w[r],"data-orig-file",w[r].getAttribute("data-webp-orig-file")),ewwwAttr(w[r],"data-medium-file",w[r].getAttribute("data-webp-medium-file")),ewwwAttr(w[r],"data-large-file",w[r].getAttribute("data-webp-large-file")),ewwwAttr(w[r],"data-large_image",w[r].getAttribute("data-webp-large_image")),ewwwAttr(w[r],"data-src",w[r].getAttribute("data-webp-src"))):(ewwwAttr(w[r],"srcset",w[r].getAttribute("data-srcset-img")),ewwwAttr(w[r],"src",w[r].getAttribute("data-src-img"))),w[r].className=w[r].className.replace(/\bewww_webp\b/,"ewww_webp_loaded");window.jQuery&&jQuery.fn.isotope&&jQuery.fn.imagesLoaded&&(jQuery(".fusion-posts-container-infinite").imagesLoaded(function(){jQuery(".fusion-posts-container-infinite").hasClass("isotope")&&jQuery(".fusion-posts-container-infinite").isotope()}),jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").imagesLoaded(function(){jQuery(".fusion-portfolio:not(.fusion-recent-works) .fusion-portfolio-wrapper").isotope()}))}function ewwwWebPInit(e){ewwwLoadImages(e),ewwwNggLoadGalleries(e),document.arrive(".ewww_webp",function(){ewwwLoadImages(e)}),document.arrive(".ewww_webp_lazy_load",function(){ewwwLoadImages(e)}),document.arrive("videos",function(){ewwwLoadImages(e)}),"loading"==document.readyState?document.addEventListener("DOMContentLoaded",ewwwJSONParserInit):("undefined"!=typeof galleries&&ewwwNggParseGalleries(e),ewwwWooParseVariations(e))}function ewwwAttr(e,t,r){null!=r&&!1!==r&&e.setAttribute(t,r)}function ewwwJSONParserInit(){"undefined"!=typeof galleries&&check_webp_feature("alpha",ewwwNggParseGalleries),check_webp_feature("alpha",ewwwWooParseVariations)}function ewwwWooParseVariations(e){if(e)for(var t=document.querySelectorAll("form.variations_form"),r=0,a=t.length;r<a;r++){var i=t[r].getAttribute("data-product_variations"),n=!1;try{for(var o in i=JSON.parse(i))void 0!==i[o]&&void 0!==i[o].image&&(void 0!==i[o].image.src_webp&&(i[o].image.src=i[o].image.src_webp,n=!0),void 0!==i[o].image.srcset_webp&&(i[o].image.srcset=i[o].image.srcset_webp,n=!0),void 0!==i[o].image.full_src_webp&&(i[o].image.full_src=i[o].image.full_src_webp,n=!0),void 0!==i[o].image.gallery_thumbnail_src_webp&&(i[o].image.gallery_thumbnail_src=i[o].image.gallery_thumbnail_src_webp,n=!0),void 0!==i[o].image.thumb_src_webp&&(i[o].image.thumb_src=i[o].image.thumb_src_webp,n=!0));n&&ewwwAttr(t[r],"data-product_variations",JSON.stringify(i))}catch(e){}}}function ewwwNggParseGalleries(e){if(e)for(var t in galleries){var r=galleries[t];galleries[t].images_list=ewwwNggParseImageList(r.images_list)}}function ewwwNggLoadGalleries(e){e&&document.addEventListener("ngg.galleria.themeadded",function(e,t){window.ngg_galleria._create_backup=window.ngg_galleria.create,window.ngg_galleria.create=function(e,t){var r=$(e).data("id");return galleries["gallery_"+r].images_list=ewwwNggParseImageList(galleries["gallery_"+r].images_list),window.ngg_galleria._create_backup(e,t)}})}function ewwwNggParseImageList(e){for(var t in e){var r=e[t];if(void 0!==r["image-webp"]&&(e[t].image=r["image-webp"],delete e[t]["image-webp"]),void 0!==r["thumb-webp"]&&(e[t].thumb=r["thumb-webp"],delete e[t]["thumb-webp"]),void 0!==r.full_image_webp&&(e[t].full_image=r.full_image_webp,delete e[t].full_image_webp),void 0!==r.srcsets)for(var a in r.srcsets)nggSrcset=r.srcsets[a],void 0!==r.srcsets[a+"-webp"]&&(e[t].srcsets[a]=r.srcsets[a+"-webp"],delete e[t].srcsets[a+"-webp"]);if(void 0!==r.full_srcsets)for(var i in r.full_srcsets)nggFSrcset=r.full_srcsets[i],void 0!==r.full_srcsets[i+"-webp"]&&(e[t].full_srcsets[i]=r.full_srcsets[i+"-webp"],delete e[t].full_srcsets[i+"-webp"])}return e}check_webp_feature("alpha",ewwwWebPInit);
mwebp.php CHANGED
@@ -214,7 +214,6 @@ function ewww_image_optimizer_webp_loop() {
214
  ewwwio_debug_message( "took $elapsed seconds this time around" );
215
  // Store the updated list of images back in the database.
216
  update_option( 'ewww_image_optimizer_webp_images', $images );
217
- ewww_image_optimizer_debug_log();
218
  die();
219
  }
220
 
214
  ewwwio_debug_message( "took $elapsed seconds this time around" );
215
  // Store the updated list of images back in the database.
216
  update_option( 'ewww_image_optimizer_webp_images', $images );
 
217
  die();
218
  }
219
 
readme.txt CHANGED
@@ -3,9 +3,9 @@ Contributors: nosilver4u
3
  Donate link: https://ewww.io/donate/
4
  Tags: optimize, image, convert, webp, resize, compress, lazy load, optimization, lossless, lossy, seo, scale
5
  Requires at least: 5.4
6
- Tested up to: 5.7
7
  Requires PHP: 7.1
8
- Stable tag: 6.1.9
9
  License: GPLv3
10
 
11
  Smaller Images, Faster Sites, Happier Visitors. Comprehensive image optimization that doesn't require a degree in rocket science.
@@ -132,6 +132,27 @@ That's not a question, but since I made it up, I'll answer it. See this resource
132
  * Feature requests can be viewed and submitted on our [feedback portal](https://feedback.ewww.io/b/features)
133
  * If you would like to help translate this plugin in your language, [join the team](https://translate.wordpress.org/projects/wp-plugins/ewww-image-optimizer/)
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  = 6.1.9 =
136
  * fixed: Easy IO's Include All Resources compat with Oxygen Builder and Beaver Builder
137
  * fixed: regex to detect SVG images in use elements caused excessive backtracking
3
  Donate link: https://ewww.io/donate/
4
  Tags: optimize, image, convert, webp, resize, compress, lazy load, optimization, lossless, lossy, seo, scale
5
  Requires at least: 5.4
6
+ Tested up to: 5.8
7
  Requires PHP: 7.1
8
+ Stable tag: 6.2.0
9
  License: GPLv3
10
 
11
  Smaller Images, Faster Sites, Happier Visitors. Comprehensive image optimization that doesn't require a degree in rocket science.
132
  * Feature requests can be viewed and submitted on our [feedback portal](https://feedback.ewww.io/b/features)
133
  * If you would like to help translate this plugin in your language, [join the team](https://translate.wordpress.org/projects/wp-plugins/ewww-image-optimizer/)
134
 
135
+ = 6.2.0 =
136
+ * added: PHP-based WebP Conversion via GD/Imagick in free mode when exec() is disabled
137
+ * added: enable -sharp_yuv option for WebP conversion with the EIO_WEBP_SHARP_YUV override
138
+ * added: WebP Conversion for CMYK images
139
+ * added: webp-supported conditional class added to body tag when JS WebP is active
140
+ * added: WP-CLI command can be run with --webp-only option
141
+ * added: Lazy Load for iframes, add 'iframe' in exclusions to disable
142
+ * added: compatibility with S3 Uploads 3.x
143
+ * added: preserve metadata and apply lossless compression to linked versions of images via Easy IO with EIO_PRESERVE_LINKED_IMAGES constant
144
+ * added: Easy IO rewrites URLs in existing picture elements
145
+ * changed: JS WebP scripts moved to beginning of page footer
146
+ * changed: native lazy loading is now enabled for right-sized PNG placeholders, override with EIO_DISABLE_NATIVE_LAZY constant
147
+ * changed: add resume ability to Delete Originals tool
148
+ * changed: move Easy IO check-in to wp_cron
149
+ * fixed: empty .webp images sometimes produced when cwebp encounters an error
150
+ * fixed: Bulk Optimizer for NextGEN loading incorrect script
151
+ * fixed: Bulk Optimizer for NextGEN fails to verify nonce for selective optimization
152
+ * fixed: Last Optimized times for Optimized Images table were incorrect
153
+ * fixed: Add Missing Dimensions overwrites smaller width/height attribute if only one is set
154
+ * fixed: replacing an existing attribute (like width) with a numeric value is broken
155
+
156
  = 6.1.9 =
157
  * fixed: Easy IO's Include All Resources compat with Oxygen Builder and Beaver Builder
158
  * fixed: regex to detect SVG images in use elements caused excessive backtracking
tests/test-agr.php CHANGED
@@ -43,7 +43,7 @@ class EWWWIO_AGR_Tests extends WP_UnitTestCase {
43
  * Test that GD is active and Imagick is not -- otherwise our tests are bogus.
44
  */
45
  function test_gd_active() {
46
- $this->assertTrue( ewww_image_optimizer_gd_support() );
47
  $this->assertFalse( ewww_image_optimizer_imagick_support() );
48
  }
49
 
43
  * Test that GD is active and Imagick is not -- otherwise our tests are bogus.
44
  */
45
  function test_gd_active() {
46
+ $this->assertNotEmpty( ewww_image_optimizer_gd_support() );
47
  $this->assertFalse( ewww_image_optimizer_imagick_support() );
48
  }
49
 
unique.php CHANGED
@@ -222,7 +222,7 @@ function ewww_image_optimizer_notice_hosting_requires_api() {
222
  }
223
  echo "<div id='ewww-image-optimizer-warning-exec' class='notice notice-warning is-dismissible'><p>" .
224
  /* translators: %s: Name of a web host, like WordPress.com or Pantheon. */
225
- sprintf( esc_html__( '%s sites require cloud-based optimization, because server-based optimization is disallowed. Those who upgrade to our premium service receive much higher compression, PNG/GIF/PDF compression, WebP conversion, and image backups.', 'ewww-image-optimizer' ), esc_html( $webhost ) ) .
226
  '<br><strong>' .
227
  /* translators: %s: link to 'start your free trial' */
228
  sprintf( esc_html__( 'Dismiss this notice to continue with free cloud-based JPG compression or %s.', 'ewww-image-optimizer' ), "<a href='https://ewww.io/plans/'>" . esc_html__( 'start your premium trial', 'ewww-image-optimizer' ) . '</a>' );
@@ -616,7 +616,7 @@ function ewww_image_optimizer_notice_utils( $quiet = null ) {
616
  ob_start();
617
  // Display a warning if exec() is disabled, can't run local tools without it.
618
  echo "<div id='ewww-image-optimizer-warning-exec' class='notice notice-warning is-dismissible'><p>" .
619
- esc_html__( 'Sites where the exec() function is disabled require cloud-based optimization, because free server-based optimization will not work. Those who upgrade to our premium service receive much higher compression, PNG/GIF/PDF compression, WebP conversion, and image backups.', 'ewww-image-optimizer' ) . '<br>' .
620
  '<strong>' .
621
  ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) || get_option( 'easyio_exactdn' ) ?
622
  esc_html__( 'Sites that use Easy IO already have built-in image optimization and may dismiss this notice to disable local compression.', 'ewww-image-optimizer' )
@@ -720,8 +720,10 @@ function ewww_image_optimizer_notice_utils( $quiet = null ) {
720
  break;
721
  case 'CWEBP':
722
  if ( ! $skip['webp'] && empty( $req ) ) {
723
- $missing[] = 'webp';
724
- $req = false;
 
 
725
  }
726
  if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
727
  ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
@@ -2107,6 +2109,7 @@ function ewww_image_optimizer( $file, $gallery_type = 4, $converted = false, $ne
2107
  // For exec-deprived servers.
2108
  if ( 10 === (int) $compression_level && EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
2109
  list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type );
 
2110
  break;
2111
  }
2112
  if ( $convert ) {
@@ -2401,6 +2404,11 @@ function ewww_image_optimizer( $file, $gallery_type = 4, $converted = false, $ne
2401
  }
2402
  break;
2403
  }
 
 
 
 
 
2404
  if ( $convert ) {
2405
  $tools = ewww_image_optimizer_path_check(
2406
  ! $skip['jpegtran'],
@@ -3006,7 +3014,13 @@ function ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tool, $rec
3006
  return esc_html__( 'Image dimensions too large for WebP conversion.', 'ewww-image-optimizer' );
3007
  }
3008
  if ( empty( $tool ) || 'image/gif' === $type ) {
3009
- ewww_image_optimizer_cloud_optimizer( $file, $type, false, $webpfile, 'image/webp' );
 
 
 
 
 
 
3010
  } else {
3011
  $nice = '';
3012
  if ( ! EWWW_IMAGE_OPTIMIZER_CLOUD ) {
@@ -3017,26 +3031,26 @@ function ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tool, $rec
3017
  }
3018
  }
3019
  // Check to see if we are supposed to strip metadata.
3020
- if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) ) {
3021
- // Don't copy metadata.
3022
- $copy_opt = 'none';
3023
- } else {
3024
- // Copy all the metadata.
3025
- $copy_opt = 'all';
3026
- }
3027
- $quality = (int) apply_filters( 'webp_quality', 75, 'image/webp' );
3028
- if ( $quality < 50 ) {
3029
  $quality = 75;
3030
  }
 
 
3031
  if ( defined( 'EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP' ) && EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP ) {
3032
- $lossless = "-q $quality";
3033
- } else {
3034
- $lossless = '-lossless';
3035
  }
3036
  switch ( $type ) {
3037
  case 'image/jpeg':
3038
- ewwwio_debug_message( "$nice " . $tool . " -q $quality -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1' );
3039
- exec( "$nice " . $tool . " -q $quality -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1', $cli_output );
 
 
 
 
 
 
3040
  break;
3041
  case 'image/png':
3042
  ewwwio_debug_message( "$nice " . $tool . " $lossless -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1' );
@@ -3050,7 +3064,7 @@ function ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tool, $rec
3050
  ewwwio_debug_message( 'webp file was too big, deleting' );
3051
  ewwwio_delete_file( $webpfile );
3052
  return esc_html__( 'WebP image was larger than original.', 'ewww-image-optimizer' );
3053
- } elseif ( ewwwio_is_file( $webpfile ) ) {
3054
  // Set correct file permissions.
3055
  $stat = stat( dirname( $webpfile ) );
3056
  $perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
@@ -3059,6 +3073,10 @@ function ewww_image_optimizer_webp_create( $file, $orig_size, $type, $tool, $rec
3059
  return esc_html__( 'WebP image larger than original, saved anyway with Force WebP option.', 'ewww-image-optimizer' );
3060
  }
3061
  return 'WebP: ' . ewww_image_optimizer_image_results( $orig_size, $webp_size );
 
 
 
 
3062
  }
3063
  return esc_html__( 'Image could not be converted to WebP.', 'ewww-image-optimizer' );
3064
  }
222
  }
223
  echo "<div id='ewww-image-optimizer-warning-exec' class='notice notice-warning is-dismissible'><p>" .
224
  /* translators: %s: Name of a web host, like WordPress.com or Pantheon. */
225
+ sprintf( esc_html__( '%s sites require cloud-based optimization, because server-based optimization is disallowed. Those who upgrade to our premium service receive much higher compression, PNG/GIF/PDF compression, and image backups.', 'ewww-image-optimizer' ), esc_html( $webhost ) ) .
226
  '<br><strong>' .
227
  /* translators: %s: link to 'start your free trial' */
228
  sprintf( esc_html__( 'Dismiss this notice to continue with free cloud-based JPG compression or %s.', 'ewww-image-optimizer' ), "<a href='https://ewww.io/plans/'>" . esc_html__( 'start your premium trial', 'ewww-image-optimizer' ) . '</a>' );
616
  ob_start();
617
  // Display a warning if exec() is disabled, can't run local tools without it.
618
  echo "<div id='ewww-image-optimizer-warning-exec' class='notice notice-warning is-dismissible'><p>" .
619
+ esc_html__( 'Sites where the exec() function is disabled require cloud-based optimization, because free server-based optimization will not work. Those who upgrade to our premium service receive much higher compression, PNG/GIF/PDF compression, and image backups.', 'ewww-image-optimizer' ) . '<br>' .
620
  '<strong>' .
621
  ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) || get_option( 'easyio_exactdn' ) ?
622
  esc_html__( 'Sites that use Easy IO already have built-in image optimization and may dismiss this notice to disable local compression.', 'ewww-image-optimizer' )
720
  break;
721
  case 'CWEBP':
722
  if ( ! $skip['webp'] && empty( $req ) ) {
723
+ if ( ! ewww_image_optimizer_imagick_supports_webp() && ! ewww_image_optimizer_gd_supports_webp() ) {
724
+ $missing[] = 'webp';
725
+ }
726
+ $req = false;
727
  }
728
  if ( ! defined( 'EWWW_IMAGE_OPTIMIZER_' . $key ) ) {
729
  ewwwio_debug_message( "defining EWWW_IMAGE_OPTIMIZER_$key" );
2109
  // For exec-deprived servers.
2110
  if ( 10 === (int) $compression_level && EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
2111
  list( $file, $converted, $result, $new_size, $backup_hash ) = ewww_image_optimizer_cloud_optimizer( $file, $type );
2112
+ $webp_result = ewww_image_optimizer_webp_create( $file, $new_size, $type, null, $orig_size !== $new_size );
2113
  break;
2114
  }
2115
  if ( $convert ) {
2404
  }
2405
  break;
2406
  }
2407
+ // For exec-deprived servers.
2408
+ if ( 10 >= (int) $compression_level && EWWW_IMAGE_OPTIMIZER_NOEXEC ) {
2409
+ $webp_result = ewww_image_optimizer_webp_create( $file, $orig_size, $type, null, $orig_size !== $new_size );
2410
+ break;
2411
+ }
2412
  if ( $convert ) {
2413
  $tools = ewww_image_optimizer_path_check(
2414
  ! $skip['jpegtran'],
3014
  return esc_html__( 'Image dimensions too large for WebP conversion.', 'ewww-image-optimizer' );
3015
  }
3016
  if ( empty( $tool ) || 'image/gif' === $type ) {
3017
+ if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_cloud_key' ) ) {
3018
+ ewww_image_optimizer_cloud_optimizer( $file, $type, false, $webpfile, 'image/webp' );
3019
+ } elseif ( ewww_image_optimizer_imagick_supports_webp() ) {
3020
+ ewww_image_optimizer_imagick_create_webp( $file, $type, $webpfile );
3021
+ } elseif ( ewww_image_optimizer_gd_supports_webp() ) {
3022
+ ewww_image_optimizer_gd_create_webp( $file, $type, $webpfile );
3023
+ }
3024
  } else {
3025
  $nice = '';
3026
  if ( ! EWWW_IMAGE_OPTIMIZER_CLOUD ) {
3031
  }
3032
  }
3033
  // Check to see if we are supposed to strip metadata.
3034
+ $copy_opt = ewww_image_optimizer_get_option( 'ewww_image_optimizer_metadata_remove' ) ? 'icc' : 'all';
3035
+ $quality = (int) apply_filters( 'webp_quality', 75, 'image/webp' );
3036
+ if ( $quality < 50 || $quality > 100 ) {
 
 
 
 
 
 
3037
  $quality = 75;
3038
  }
3039
+ $sharp_yuv = defined( 'EIO_WEBP_SHARP_YUV' ) && EIO_WEBP_SHARP_YUV ? '-sharp_yuv' : '';
3040
+ $lossless = '-lossless';
3041
  if ( defined( 'EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP' ) && EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP ) {
3042
+ $lossless = "-q $quality $sharp_yuv";
 
 
3043
  }
3044
  switch ( $type ) {
3045
  case 'image/jpeg':
3046
+ ewwwio_debug_message( "$nice " . $tool . " -q $quality $sharp_yuv -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1' );
3047
+ exec( "$nice " . $tool . " -q $quality $sharp_yuv -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1', $cli_output );
3048
+ if ( ! ewwwio_is_file( $webpfile ) && ewww_image_optimizer_imagick_supports_webp() && ewww_image_optimizer_is_cmyk( $file ) ) {
3049
+ ewwwio_debug_message( 'cmyk image skipped, trying imagick' );
3050
+ ewww_image_optimizer_imagick_create_webp( $file, $type, $webpfile );
3051
+ } elseif ( ewwwio_is_file( $webpfile ) && 'image/webp' !== ewww_image_optimizer_mimetype( $webpfile, 'i' ) ) {
3052
+ ewwwio_debug_message( 'non-webp file produced' );
3053
+ }
3054
  break;
3055
  case 'image/png':
3056
  ewwwio_debug_message( "$nice " . $tool . " $lossless -metadata $copy_opt -quiet " . ewww_image_optimizer_escapeshellarg( $file ) . ' -o ' . ewww_image_optimizer_escapeshellarg( $webpfile ) . ' 2>&1' );
3064
  ewwwio_debug_message( 'webp file was too big, deleting' );
3065
  ewwwio_delete_file( $webpfile );
3066
  return esc_html__( 'WebP image was larger than original.', 'ewww-image-optimizer' );
3067
+ } elseif ( ewwwio_is_file( $webpfile ) && 'image/webp' === ewww_image_optimizer_mimetype( $webpfile, 'i' ) ) {
3068
  // Set correct file permissions.
3069
  $stat = stat( dirname( $webpfile ) );
3070
  $perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
3073
  return esc_html__( 'WebP image larger than original, saved anyway with Force WebP option.', 'ewww-image-optimizer' );
3074
  }
3075
  return 'WebP: ' . ewww_image_optimizer_image_results( $orig_size, $webp_size );
3076
+ } elseif ( ewwwio_is_file( $webpfile ) ) {
3077
+ ewwwio_debug_message( 'webp file mimetype did not validate, deleting' );
3078
+ ewwwio_delete_file( $webpfile );
3079
+ return esc_html__( 'WebP conversion error.', 'ewww-image-optimizer' );
3080
  }
3081
  return esc_html__( 'Image could not be converted to WebP.', 'ewww-image-optimizer' );
3082
  }
vendor/icc/sRGB2014.icc ADDED
Binary file