Autoptimize - Version 2.5.1

Version Description

  • Images: Also optimize & lazyload <picture><source>
  • Images: Misc. improvements to lazyload
  • Images: Updated to LazySizes 5.0.0
  • CSS: improvements to the deferring logic for non-aggregated CSS resources.
  • Settings-page: Show "JS, CSS & HTML" advanced options by default (many people did not see the button)
Download this release

Release Info

Developer futtta
Plugin Icon 128x128 Autoptimize
Version 2.5.1
Comparing to
See all releases

Code changes from version 2.5.0 to 2.5.1

autoptimize.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Autoptimize
4
  Plugin URI: https://autoptimize.com/
5
  Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
- Version: 2.5.0
7
  Author: Frank Goossens (futtta)
8
  Author URI: https://autoptimize.com/
9
  Text Domain: autoptimize
@@ -20,7 +20,7 @@ if ( ! defined( 'ABSPATH' ) ) {
20
  exit;
21
  }
22
 
23
- define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.5.0' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
3
  Plugin Name: Autoptimize
4
  Plugin URI: https://autoptimize.com/
5
  Description: Makes your site faster by optimizing CSS, JS, Images, Google fonts and more.
6
+ Version: 2.5.1
7
  Author: Frank Goossens (futtta)
8
  Author URI: https://autoptimize.com/
9
  Text Domain: autoptimize
20
  exit;
21
  }
22
 
23
+ define( 'AUTOPTIMIZE_PLUGIN_VERSION', '2.5.1' );
24
 
25
  // plugin_dir_path() returns the trailing slash!
26
  define( 'AUTOPTIMIZE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
classes/autoptimizeBase.php CHANGED
@@ -23,6 +23,9 @@ abstract class autoptimizeBase
23
  */
24
  public $debug_log = false;
25
 
 
 
 
26
  public function __construct( $content )
27
  {
28
  $this->content = $content;
23
  */
24
  public $debug_log = false;
25
 
26
+ /** @var string */
27
+ public $cdn_url = '';
28
+
29
  public function __construct( $content )
30
  {
31
  $this->content = $content;
classes/autoptimizeConfig.php CHANGED
@@ -188,7 +188,7 @@ input[type=url]:invalid {color: red; border-color:red;} .form-table th{font-weig
188
  <div id="ao_title_and_button">
189
  <h1 id="ao_title"><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?>
190
  <span id="ao_adv_button">
191
- <?php if ( get_option( 'autoptimize_show_adv', '0' ) == '1' ) { ?>
192
  <a href="javascript:void(0);" id="ao_show_adv" class="button" style="display:none;"><span><?php _e("Show advanced settings","autoptimize") ?></span></a>
193
  <a href="javascript:void(0);" id="ao_hide_adv" class="button"><span><?php _e("Hide advanced settings","autoptimize") ?></span></a>
194
  <style>tr.ao_adv{display:table-row;} li.ao_adv{display:list-item;}</style>
@@ -403,7 +403,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
403
 
404
  </ul>
405
 
406
- <input type="hidden" id="autoptimize_show_adv" name="autoptimize_show_adv" value="<?php echo get_option('autoptimize_show_adv','0'); ?>">
407
 
408
  <p class="submit">
409
  <input type="submit" class="button-secondary" value="<?php _e('Save Changes','autoptimize') ?>" />
@@ -722,7 +722,7 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
722
  'autoptimize_css_datauris' => 0,
723
  'autoptimize_cdn_url' => '',
724
  'autoptimize_cache_nogzip' => 1,
725
- 'autoptimize_show_adv' => 0,
726
  'autoptimize_optimize_logged' => 1,
727
  'autoptimize_optimize_checkout' => 1,
728
  'autoptimize_minify_excluded' => 1,
@@ -884,14 +884,13 @@ if ( function_exists( 'is_plugin_active' ) && ! is_plugin_active( 'autoptimize-c
884
  /**
885
  * Returns true if doing ajax.
886
  *
887
- * @return type
888
  */
889
  protected static function doing_ajax()
890
  {
891
  if ( function_exists( 'wp_doing_ajax' ) ) {
892
  return wp_doing_ajax();
893
- } else {
894
- return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
895
  }
 
896
  }
897
  }
188
  <div id="ao_title_and_button">
189
  <h1 id="ao_title"><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?>
190
  <span id="ao_adv_button">
191
+ <?php if ( get_option( 'autoptimize_show_adv', '1' ) == '1' ) { ?>
192
  <a href="javascript:void(0);" id="ao_show_adv" class="button" style="display:none;"><span><?php _e("Show advanced settings","autoptimize") ?></span></a>
193
  <a href="javascript:void(0);" id="ao_hide_adv" class="button"><span><?php _e("Hide advanced settings","autoptimize") ?></span></a>
194
  <style>tr.ao_adv{display:table-row;} li.ao_adv{display:list-item;}</style>
403
 
404
  </ul>
405
 
406
+ <input type="hidden" id="autoptimize_show_adv" name="autoptimize_show_adv" value="<?php echo get_option('autoptimize_show_adv','1'); ?>">
407
 
408
  <p class="submit">
409
  <input type="submit" class="button-secondary" value="<?php _e('Save Changes','autoptimize') ?>" />
722
  'autoptimize_css_datauris' => 0,
723
  'autoptimize_cdn_url' => '',
724
  'autoptimize_cache_nogzip' => 1,
725
+ 'autoptimize_show_adv' => 1,
726
  'autoptimize_optimize_logged' => 1,
727
  'autoptimize_optimize_checkout' => 1,
728
  'autoptimize_minify_excluded' => 1,
884
  /**
885
  * Returns true if doing ajax.
886
  *
887
+ * @return bool
888
  */
889
  protected static function doing_ajax()
890
  {
891
  if ( function_exists( 'wp_doing_ajax' ) ) {
892
  return wp_doing_ajax();
 
 
893
  }
894
+ return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
895
  }
896
  }
classes/autoptimizeExtra.php CHANGED
@@ -155,7 +155,9 @@ class autoptimizeExtra
155
 
156
  public function filter_remove_dns_prefetch( $urls, $relation_type, $url_to_remove )
157
  {
158
- if ( 'dns-prefetch' === $relation_type ) {
 
 
159
  $cnt = 0;
160
  foreach ( $urls as $url ) {
161
  if ( false !== strpos( $url, $url_to_remove ) ) {
@@ -227,7 +229,7 @@ class autoptimizeExtra
227
  $fonts_string = $fonts_string . '&#038;subset=' . $subset_string;
228
  }
229
 
230
- $fonts_string = str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) );
231
 
232
  if ( ! empty( $fonts_string ) ) {
233
  if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
155
 
156
  public function filter_remove_dns_prefetch( $urls, $relation_type, $url_to_remove )
157
  {
158
+ $url_to_remove = (string) $url_to_remove;
159
+
160
+ if ( ! empty( $url_to_remove ) && 'dns-prefetch' === $relation_type ) {
161
  $cnt = 0;
162
  foreach ( $urls as $url ) {
163
  if ( false !== strpos( $url, $url_to_remove ) ) {
229
  $fonts_string = $fonts_string . '&#038;subset=' . $subset_string;
230
  }
231
 
232
+ $fonts_string = apply_filters( 'autoptimize_filter_extra_gfont_fontstring', str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) ) );
233
 
234
  if ( ! empty( $fonts_string ) ) {
235
  if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
classes/autoptimizeHTML.php CHANGED
@@ -16,6 +16,9 @@ class autoptimizeHTML extends autoptimizeBase
16
  */
17
  private $keepcomments = false;
18
 
 
 
 
19
  /**
20
  * Things to exclude from being minifed.
21
  *
16
  */
17
  private $keepcomments = false;
18
 
19
+ /** @var bool */
20
+ private $forcexhtml = false;
21
+
22
  /**
23
  * Things to exclude from being minifed.
24
  *
classes/autoptimizeImages.php CHANGED
@@ -200,6 +200,7 @@ class autoptimizeImages
200
  if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
201
  $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
202
  }
 
203
  }
204
 
205
  return $imgopt_host;
@@ -326,16 +327,12 @@ class autoptimizeImages
326
  // Do the work on cache miss only.
327
  if ( ! isset( $cache[ $in ] ) ) {
328
  // Default to what was given to us.
329
- $result = $in;
330
  if ( autoptimizeUtils::is_protocol_relative( $in ) ) {
331
  $result = $parsed_site_url['scheme'] . ':' . $in;
332
  } elseif ( 0 === strpos( $in, '/' ) ) {
333
  // Root-relative...
334
- $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'];
335
- // Add the path for subfolder installs.
336
- if ( isset( $parsed_site_url['path'] ) ) {
337
- $result .= $parsed_site_url['path'];
338
- }
339
  $result .= $in;
340
  } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
341
  $result = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
@@ -464,8 +461,9 @@ class autoptimizeImages
464
 
465
  public function replace_img_callback( $matches, $width = 0, $height = 0 )
466
  {
 
467
  if ( $this->can_optimize_image( $matches[1] ) ) {
468
- return str_replace( $matches[1], $this->build_imgopt_url( $matches[1], $width, $height ), $matches[0] );
469
  } else {
470
  return $matches[0];
471
  }
@@ -476,7 +474,6 @@ class autoptimizeImages
476
  /*
477
  * potential future functional improvements:
478
  *
479
- * picture element.
480
  * filter for critical CSS.
481
  */
482
  $to_replace = array();
@@ -484,9 +481,9 @@ class autoptimizeImages
484
  // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
485
  if ( $this->should_lazyload() ) {
486
  $in = autoptimizeBase::replace_contents_with_marker_if_exists(
487
- 'NOSCRIPT',
488
- '<noscript',
489
- '#<noscript.*?<\/noscript>#is',
490
  $in
491
  );
492
  }
@@ -536,14 +533,8 @@ class autoptimizeImages
536
  }
537
 
538
  // do lazyload stuff.
539
- if ( $this->should_lazyload() && str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
540
- $noscript_tag = '<noscript>' . $tag . '</noscript>';
541
- $tag = str_replace( 'srcset=', 'data-srcset=', $tag );
542
-
543
- // add lazyload class.
544
- $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
545
-
546
- // set placeholder.
547
  if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
548
  // if all img src have been replaced during srcset, we have to extract the
549
  // origin url from the imgopt one to be able to set a lqip placeholder.
@@ -551,25 +542,23 @@ class autoptimizeImages
551
  } else {
552
  $_url = $url;
553
  }
554
- if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
555
- $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait,w_' . $imgopt_w . ',h_' . $imgopt_h . '/' . $_url;
556
- } else {
557
- $placeholder = $this->get_default_lazyload_placeholder( $imgopt_w, $imgopt_h );
558
- }
559
- $placeholder = ' src=\'' . apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $placeholder );
560
 
561
- // add min-heigth off by default as it can deform images, can be enabled with filter.
562
- $min_height = '';
563
- if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_addminheight', false ) ) {
564
- $min_height = ' style="min-height:' . $imgopt_h . 'px;"';
 
 
 
 
 
 
565
  }
566
-
567
- // add noscript & placeholder.
568
- $tag = $noscript_tag . str_replace( ' src=', $min_height . $placeholder . '\' data-src=', $tag );
569
- $tag = apply_filters( 'autoptimize_filter_imgopt_lazyloaded_img', $tag );
570
  }
571
 
572
- // add tag to array for later replacement.
573
  if ( $tag !== $orig_tag ) {
574
  $to_replace[ $orig_tag ] = $tag;
575
  }
@@ -597,12 +586,16 @@ class autoptimizeImages
597
  );
598
  }
599
 
600
- // and restore noscript tags if these were hidden for lazyload purposes.
601
  if ( $this->should_lazyload() ) {
602
  $out = autoptimizeBase::restore_marked_content(
603
- 'NOSCRIPT',
604
  $out
605
  );
 
 
 
 
606
  }
607
 
608
  return $out;
@@ -648,77 +641,92 @@ class autoptimizeImages
648
  /**
649
  * Lazyload functions
650
  */
651
- public function should_lazyload() {
 
 
 
 
 
 
652
  if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) ) {
653
  $lazyload_return = true;
654
  } else {
655
  $lazyload_return = false;
656
  }
 
657
 
658
  return $lazyload_return;
659
  }
660
 
661
- public static function should_lazyload_wrapper() {
662
- // needed in autoptimizeMain.php.
663
- $self = new self();
664
- return $self->should_lazyload();
665
- }
666
-
667
  public function filter_lazyload_images( $in )
668
  {
669
  // only used is image optimization is NOT active but lazyload is.
670
  $to_replace = array();
671
 
672
- // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
673
  $out = autoptimizeBase::replace_contents_with_marker_if_exists(
674
- 'NOSCRIPT',
675
- '<noscript',
676
- '#<noscript.*?<\/noscript>#is',
677
  $in
678
  );
679
 
680
  // extract img tags and add lazyload attribs.
681
  if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
682
  foreach ( $matches[0] as $tag ) {
683
- $to_replace[ $tag ] = $this->add_lazyload( $tag );
 
 
684
  }
685
  $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
686
  }
687
 
 
 
 
688
  // restore noscript tags.
689
  $out = autoptimizeBase::restore_marked_content(
690
- 'NOSCRIPT',
691
  $out
692
  );
693
 
694
  return $out;
695
  }
696
 
697
- public function add_lazyload( $tag ) {
698
  // adds actual lazyload-attributes to an image node.
699
  if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
 
 
700
  // store original tag for use in noscript version.
701
  $noscript_tag = '<noscript>' . $tag . '</noscript>';
702
 
703
  // insert lazyload class.
704
  $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
705
 
706
- // get image width & heigth for placeholder fun (and to prevent content reflow).
707
- $_get_size = $this->get_size_from_tag( $tag );
708
- $width = $_get_size['width'];
709
- $height = $_get_size['height'];
710
- if ( false === $width ) {
711
- $widht = 210; // default width for SVG placeholder.
712
- }
713
- if ( false === $height ) {
714
- $heigth = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
 
 
 
 
 
 
715
  }
 
 
716
 
717
- // insert the actual lazyload stuff.
718
- // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
719
- $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
720
- $tag = str_replace( ' src=', ' src=\'' . $placeholder . '\' data-src=', $tag );
721
- $tag = str_replace( ' srcset=', ' data-srcset=', $tag );
722
 
723
  // add the noscript-tag from earlier.
724
  $tag = $noscript_tag . $tag;
@@ -756,7 +764,7 @@ class autoptimizeImages
756
  $options = $this->options;
757
 
758
  // set default exclusions.
759
- $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset' );
760
 
761
  // add from setting.
762
  if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
@@ -802,6 +810,51 @@ class autoptimizeImages
802
  return $webp_return;
803
  }
804
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
805
  /**
806
  * Admin page logic and related functions below.
807
  */
200
  if ( ! empty( $avail_imgopt ) && array_key_exists( 'hosts', $avail_imgopt ) && is_array( $avail_imgopt['hosts'] ) ) {
201
  $imgopt_host = array_rand( array_flip( $avail_imgopt['hosts'] ) );
202
  }
203
+ $imgopt_host = apply_filters( 'autoptimize_filter_imgopt_host', $imgopt_host );
204
  }
205
 
206
  return $imgopt_host;
327
  // Do the work on cache miss only.
328
  if ( ! isset( $cache[ $in ] ) ) {
329
  // Default to what was given to us.
330
+ $result = trim( $in );
331
  if ( autoptimizeUtils::is_protocol_relative( $in ) ) {
332
  $result = $parsed_site_url['scheme'] . ':' . $in;
333
  } elseif ( 0 === strpos( $in, '/' ) ) {
334
  // Root-relative...
335
+ $result = $parsed_site_url['scheme'] . '://' . $parsed_site_url['host'];
 
 
 
 
336
  $result .= $in;
337
  } elseif ( ! empty( $cdn_domain ) && strpos( $in, $cdn_domain ) !== 0 ) {
338
  $result = str_replace( $cdn_domain, $parsed_site_url['host'], $in );
461
 
462
  public function replace_img_callback( $matches, $width = 0, $height = 0 )
463
  {
464
+ $_normalized_img_url = $this->normalize_img_url( $matches[1] );
465
  if ( $this->can_optimize_image( $matches[1] ) ) {
466
+ return str_replace( $matches[1], $this->build_imgopt_url( $_normalized_img_url, $width, $height ), $matches[0] );
467
  } else {
468
  return $matches[0];
469
  }
474
  /*
475
  * potential future functional improvements:
476
  *
 
477
  * filter for critical CSS.
478
  */
479
  $to_replace = array();
481
  // hide noscript tags to avoid nesting noscript tags (as lazyloaded images add noscript).
482
  if ( $this->should_lazyload() ) {
483
  $in = autoptimizeBase::replace_contents_with_marker_if_exists(
484
+ 'SCRIPT',
485
+ '<script',
486
+ '#<(?:no)?script.*?<\/(?:no)?script>#is',
487
  $in
488
  );
489
  }
533
  }
534
 
535
  // do lazyload stuff.
536
+ if ( $this->should_lazyload( $in ) ) {
537
+ // first do lpiq placeholder logic.
 
 
 
 
 
 
538
  if ( strpos( $url, $this->get_imgopt_host() ) === 0 ) {
539
  // if all img src have been replaced during srcset, we have to extract the
540
  // origin url from the imgopt one to be able to set a lqip placeholder.
542
  } else {
543
  $_url = $url;
544
  }
 
 
 
 
 
 
545
 
546
+ if ( $this->can_optimize_image( $_url ) && apply_filters( 'autoptimize_filter_imgopt_lazyload_dolqip', true ) ) {
547
+ $lqip_w = '';
548
+ $lqip_h = '';
549
+ if ( isset( $imgopt_w ) && ! empty( $imgopt_w ) ) {
550
+ $lqip_w = ',w_' . $imgopt_w;
551
+ }
552
+ if ( isset( $imgopt_h ) && ! empty( $imgopt_h ) ) {
553
+ $lqip_h = ',h_' . $imgopt_h;
554
+ }
555
+ $placeholder = $this->get_imgopt_host() . 'client/q_lqip,ret_wait' . $lqip_w . $lqip_h . '/' . $_url;
556
  }
557
+ // then call add_lazyload-function with lpiq placeholder if set.
558
+ $tag = $this->add_lazyload( $tag, $placeholder );
 
 
559
  }
560
 
561
+ // and add tag to array for later replacement.
562
  if ( $tag !== $orig_tag ) {
563
  $to_replace[ $orig_tag ] = $tag;
564
  }
586
  );
587
  }
588
 
589
+ // lazyload: restore noscript tags + lazyload picture source tags.
590
  if ( $this->should_lazyload() ) {
591
  $out = autoptimizeBase::restore_marked_content(
592
+ 'SCRIPT',
593
  $out
594
  );
595
+
596
+ $out = $this->process_picture_tag( $out, true, true );
597
+ } else {
598
+ $out = $this->process_picture_tag( $out, true, false );
599
  }
600
 
601
  return $out;
641
  /**
642
  * Lazyload functions
643
  */
644
+ public static function should_lazyload_wrapper() {
645
+ // needed in autoptimizeMain.php.
646
+ $self = new self();
647
+ return $self->should_lazyload();
648
+ }
649
+
650
+ public function should_lazyload( $context = '' ) {
651
  if ( ! empty( $this->options['autoptimize_imgopt_checkbox_field_3'] ) ) {
652
  $lazyload_return = true;
653
  } else {
654
  $lazyload_return = false;
655
  }
656
+ $lazyload_return = apply_filters( 'autoptimize_filter_imgopt_should_lazyload', $lazyload_return, $context );
657
 
658
  return $lazyload_return;
659
  }
660
 
 
 
 
 
 
 
661
  public function filter_lazyload_images( $in )
662
  {
663
  // only used is image optimization is NOT active but lazyload is.
664
  $to_replace = array();
665
 
666
+ // hide (no)script tags to avoid nesting noscript tags (as lazyloaded images add noscript).
667
  $out = autoptimizeBase::replace_contents_with_marker_if_exists(
668
+ 'SCRIPT',
669
+ '<script',
670
+ '#<(?:no)?script.*?<\/(?:no)?script>#is',
671
  $in
672
  );
673
 
674
  // extract img tags and add lazyload attribs.
675
  if ( preg_match_all( '#<img[^>]*src[^>]*>#Usmi', $out, $matches ) ) {
676
  foreach ( $matches[0] as $tag ) {
677
+ if ( $this->should_lazyload( $out ) ) {
678
+ $to_replace[ $tag ] = $this->add_lazyload( $tag );
679
+ }
680
  }
681
  $out = str_replace( array_keys( $to_replace ), array_values( $to_replace ), $out );
682
  }
683
 
684
+ // and also lazyload picture tag.
685
+ $out = $this->process_picture_tag( $out, false, true );
686
+
687
  // restore noscript tags.
688
  $out = autoptimizeBase::restore_marked_content(
689
+ 'SCRIPT',
690
  $out
691
  );
692
 
693
  return $out;
694
  }
695
 
696
+ public function add_lazyload( $tag, $placeholder = '' ) {
697
  // adds actual lazyload-attributes to an image node.
698
  if ( str_ireplace( $this->get_lazyload_exclusions(), '', $tag ) === $tag ) {
699
+ $tag = $this->maybe_fix_missing_quotes( $tag );
700
+
701
  // store original tag for use in noscript version.
702
  $noscript_tag = '<noscript>' . $tag . '</noscript>';
703
 
704
  // insert lazyload class.
705
  $tag = $this->inject_classes_in_tag( $tag, 'lazyload ' );
706
 
707
+ if ( ! $placeholder || empty( $placeholder ) ) {
708
+ // get image width & heigth for placeholder fun (and to prevent content reflow).
709
+ $_get_size = $this->get_size_from_tag( $tag );
710
+ $width = $_get_size['width'];
711
+ $height = $_get_size['height'];
712
+ if ( false === $width ) {
713
+ $widht = 210; // default width for SVG placeholder.
714
+ }
715
+ if ( false === $height ) {
716
+ $heigth = $width / 3 * 2; // if no height, base it on width using the 3/2 aspect ratio.
717
+ }
718
+
719
+ // insert the actual lazyload stuff.
720
+ // see https://css-tricks.com/preventing-content-reflow-from-lazy-loaded-images/ for great read on why we're using empty svg's.
721
+ $placeholder = apply_filters( 'autoptimize_filter_imgopt_lazyload_placeholder', $this->get_default_lazyload_placeholder( $width, $height ) );
722
  }
723
+ $tag = str_replace( ' src=', ' src=\'' . $placeholder . '\' data-src=', $tag );
724
+ $tag = str_replace( ' srcset=', ' data-srcset=', $tag );
725
 
726
+ // move sizes to data-sizes unless filter says no.
727
+ if ( apply_filters( 'autoptimize_filter_imgopt_lazyload_move_sizes', true ) ) {
728
+ $tag = str_replace( 'sizes=', 'data-sizes=', $tag );
729
+ }
 
730
 
731
  // add the noscript-tag from earlier.
732
  $tag = $noscript_tag . $tag;
764
  $options = $this->options;
765
 
766
  // set default exclusions.
767
+ $exclude_lazyload_array = array( 'skip-lazy', 'data-no-lazy', 'notlazy', 'data-src', 'data-srcset', 'data:image/', 'data-lazyload', 'rev-slidebg' );
768
 
769
  // add from setting.
770
  if ( array_key_exists( 'autoptimize_imgopt_text_field_5', $options ) ) {
810
  return $webp_return;
811
  }
812
 
813
+ public function process_picture_tag( $in, $imgopt = false, $lazy = false ) {
814
+ // check if "<picture" is present and if filter allows us to process <picture>.
815
+ if ( strpos( $in, '<picture' ) === false || apply_filters( 'autoptimize_filter_imgopt_dopicture', true ) === false ) {
816
+ return $in;
817
+ }
818
+
819
+ $_exclusions = $this->get_lazyload_exclusions();
820
+ $to_replace_pict = array();
821
+
822
+ // extract and process each picture-node.
823
+ preg_match_all( '#<picture.*</picture>#Usmi', $in, $_pictures, PREG_SET_ORDER );
824
+ foreach ( $_pictures as $_picture ) {
825
+ $_picture = $this->maybe_fix_missing_quotes( $_picture );
826
+ if ( strpos( $_picture[0], '<source ' ) !== false && preg_match_all( '#<source .*srcset=(?:"|\')(?!data)(.*)(?:"|\').*>#Usmi', $_picture[0], $_sources, PREG_SET_ORDER ) !== false ) {
827
+ foreach ( $_sources as $_source ) {
828
+ $_picture_replacement = $_source[0];
829
+
830
+ // should we optimize the image?
831
+ if ( $imgopt && $this->can_optimize_image( $_source[1] ) ) {
832
+ $_picture_replacement = str_replace( $_source[1], $this->build_imgopt_url( $_source[1] ), $_picture_replacement );
833
+ }
834
+ // should we lazy-load?
835
+ if ( $lazy && str_ireplace( $_exclusions, '', $_picture_replacement ) === $_picture_replacement ) {
836
+ $_picture_replacement = str_replace( ' srcset=', ' data-srcset=', $_picture_replacement );
837
+ }
838
+ $to_replace_pict[ $_source[0] ] = $_picture_replacement;
839
+ }
840
+ }
841
+ }
842
+
843
+ // and return the fully procesed $in.
844
+ $out = str_replace( array_keys( $to_replace_pict ), array_values( $to_replace_pict ), $in );
845
+
846
+ return $out;
847
+ }
848
+
849
+ public function maybe_fix_missing_quotes( $tag_in ) {
850
+ // W3TC's Minify_HTML class removes quotes around attribute value, this re-adds them.
851
+ if ( file_exists( WP_PLUGIN_DIR . '/w3-total-cache/w3-total-cache.php' ) && class_exists( 'Minify_HTML' ) && apply_filters( 'autoptimize_filter_imgopt_fixquotes', true ) ) {
852
+ return preg_replace( '/=([^("|\')]*)(\s|>)/U', '=\'$1\'$2', $tag_in );
853
+ } else {
854
+ return $tag_in;
855
+ }
856
+ }
857
+
858
  /**
859
  * Admin page logic and related functions below.
860
  */
classes/autoptimizeMain.php CHANGED
@@ -108,7 +108,7 @@ class autoptimizeMain
108
  }
109
  }
110
  if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_URL' ) ) {
111
- if ( function_exists( 'domain_mapping_siteurl' ) ) {
112
  define( 'AUTOPTIMIZE_WP_CONTENT_URL', str_replace( get_original_url( AUTOPTIMIZE_WP_SITE_URL ), AUTOPTIMIZE_WP_SITE_URL, content_url() ) );
113
  } else {
114
  define( 'AUTOPTIMIZE_WP_CONTENT_URL', content_url() );
@@ -136,7 +136,7 @@ class autoptimizeMain
136
  // Also requires 'mbstring' extension.
137
  $with_mbstring = apply_filters( 'autoptimize_filter_main_use_mbstring', false );
138
  if ( $with_mbstring ) {
139
- autoptimizeUtils::mbstring_available( \extensions_loaded( 'mbstring' ) );
140
  } else {
141
  autoptimizeUtils::mbstring_available( false );
142
  }
108
  }
109
  }
110
  if ( ! defined( 'AUTOPTIMIZE_WP_CONTENT_URL' ) ) {
111
+ if ( function_exists( 'get_original_url' ) ) {
112
  define( 'AUTOPTIMIZE_WP_CONTENT_URL', str_replace( get_original_url( AUTOPTIMIZE_WP_SITE_URL ), AUTOPTIMIZE_WP_SITE_URL, content_url() ) );
113
  } else {
114
  define( 'AUTOPTIMIZE_WP_CONTENT_URL', content_url() );
136
  // Also requires 'mbstring' extension.
137
  $with_mbstring = apply_filters( 'autoptimize_filter_main_use_mbstring', false );
138
  if ( $with_mbstring ) {
139
+ autoptimizeUtils::mbstring_available( \extension_loaded( 'mbstring' ) );
140
  } else {
141
  autoptimizeUtils::mbstring_available( false );
142
  }
classes/autoptimizeScripts.php CHANGED
@@ -26,6 +26,7 @@ class autoptimizeScripts extends autoptimizeBase
26
  'addthis.com','/afsonline/show_afs_search.js','disqus.js','networkedblogs.com/getnetworkwidget','infolinks.com/js/',
27
  'jd.gallery.js.php','jd.gallery.transitions.js','swfobject.embedSWF(','linkwithin.com/widget.js','tiny_mce.js','tinyMCEPreInit.go'
28
  );
 
29
 
30
  private $aggregate = true;
31
  private $trycatch = false;
26
  'addthis.com','/afsonline/show_afs_search.js','disqus.js','networkedblogs.com/getnetworkwidget','infolinks.com/js/',
27
  'jd.gallery.js.php','jd.gallery.transitions.js','swfobject.embedSWF(','linkwithin.com/widget.js','tiny_mce.js','tinyMCEPreInit.go'
28
  );
29
+ public $cdn_url = '';
30
 
31
  private $aggregate = true;
32
  private $trycatch = false;
classes/autoptimizeStyles.php CHANGED
@@ -44,6 +44,8 @@ class autoptimizeStyles extends autoptimizeBase
44
  private $cssremovables = array();
45
  private $include_inline = false;
46
  private $inject_min_late = '';
 
 
47
  private $minify_excluded = true;
48
 
49
  // public $cdn_url; // Used all over the place implicitly, so will have to be either public or protected :/ .
@@ -183,7 +185,7 @@ class autoptimizeStyles extends autoptimizeBase
183
  } else {
184
  // Link is dynamic (.php etc).
185
  $new_tag = $this->optionally_defer_excluded( $tag, 'none' );
186
- if ( $new_tag !== $tag ) {
187
  $this->content = str_replace( $tag, $new_tag, $this->content );
188
  }
189
  $tag = '';
@@ -207,14 +209,15 @@ class autoptimizeStyles extends autoptimizeBase
207
  // Remove the original style tag.
208
  $this->content = str_replace( $tag, '', $this->content );
209
  } else {
210
- // Excluded CSS, minify that file:
211
- // -> if aggregate is on and exclude minify is on
212
- // -> if aggregate is off and the file is not in dontmove.
213
  if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
214
  $exploded_url = explode( '?', $source[2], 2 );
215
  $url = $exploded_url[0];
216
  $path = $this->getpath( $url );
 
217
 
 
 
 
218
  if ( $path && ( $this->minify_excluded || apply_filters( 'autoptimize_filter_css_minify_excluded', false, $url ) ) ) {
219
  $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false );
220
  if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
@@ -222,15 +225,16 @@ class autoptimizeStyles extends autoptimizeBase
222
  if ( ! empty( $minified_url ) ) {
223
  // Replace orig URL with cached minified URL.
224
  $new_tag = str_replace( $url, $minified_url, $tag );
225
- } else {
226
- $new_tag = $tag;
227
  }
 
 
228
 
229
- $new_tag = $this->optionally_defer_excluded( $new_tag, $url );
 
230
 
231
- // And replace!
232
- $this->content = str_replace( $tag, $new_tag, $this->content );
233
- }
234
  }
235
  }
236
  }
@@ -246,12 +250,12 @@ class autoptimizeStyles extends autoptimizeBase
246
  * Checks if non-optimized CSS is to be preloaded and if so return
247
  * the tag with preload code.
248
  *
249
- * @param string $new_tag (required).
250
  * @param string $url (optional).
251
  *
252
  * @return string $new_tag
253
  */
254
- private function optionally_defer_excluded( $new_tag, $url = '' )
255
  {
256
  // Defer single CSS if "inline & defer" is ON and there is inline CSS.
257
  if ( $this->defer && ! empty( $this->defer_inline ) ) {
@@ -262,15 +266,18 @@ class autoptimizeStyles extends autoptimizeBase
262
  $url
263
  );
264
  // Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
265
- $new_tag = '<noscript>' . $new_tag . '</noscript>' . str_replace(
266
  array(
267
  "rel='stylesheet'",
268
  'rel="stylesheet"',
269
  ),
270
  "rel='preload' as='style' onload=\"" . $_preload_onload . "\"",
271
- $new_tag
272
  );
 
 
273
  }
 
274
  return $new_tag;
275
  }
276
 
44
  private $cssremovables = array();
45
  private $include_inline = false;
46
  private $inject_min_late = '';
47
+ private $dontmove = array();
48
+ private $options = array();
49
  private $minify_excluded = true;
50
 
51
  // public $cdn_url; // Used all over the place implicitly, so will have to be either public or protected :/ .
185
  } else {
186
  // Link is dynamic (.php etc).
187
  $new_tag = $this->optionally_defer_excluded( $tag, 'none' );
188
+ if ( $new_tag !== '' && $new_tag !== $tag ) {
189
  $this->content = str_replace( $tag, $new_tag, $this->content );
190
  }
191
  $tag = '';
209
  // Remove the original style tag.
210
  $this->content = str_replace( $tag, '', $this->content );
211
  } else {
 
 
 
212
  if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) {
213
  $exploded_url = explode( '?', $source[2], 2 );
214
  $url = $exploded_url[0];
215
  $path = $this->getpath( $url );
216
+ $new_tag = $tag;
217
 
218
+ // Excluded CSS, minify that file:
219
+ // -> if aggregate is on and exclude minify is on
220
+ // -> if aggregate is off and the file is not in dontmove.
221
  if ( $path && ( $this->minify_excluded || apply_filters( 'autoptimize_filter_css_minify_excluded', false, $url ) ) ) {
222
  $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false );
223
  if ( ( false === $this->aggregate && str_replace( $this->dontmove, '', $path ) === $path ) || ( true === $this->aggregate && ( false === $consider_minified_array || str_replace( $consider_minified_array, '', $path ) === $path ) ) ) {
225
  if ( ! empty( $minified_url ) ) {
226
  // Replace orig URL with cached minified URL.
227
  $new_tag = str_replace( $url, $minified_url, $tag );
 
 
228
  }
229
+ }
230
+ }
231
 
232
+ // Optionally defer (preload) non-aggregated CSS.
233
+ $new_tag = $this->optionally_defer_excluded( $new_tag, $url );
234
 
235
+ // And replace!
236
+ if ( $new_tag !== '' && $new_tag !== $tag ) {
237
+ $this->content = str_replace( $tag, $new_tag, $this->content );
238
  }
239
  }
240
  }
250
  * Checks if non-optimized CSS is to be preloaded and if so return
251
  * the tag with preload code.
252
  *
253
+ * @param string $tag (required).
254
  * @param string $url (optional).
255
  *
256
  * @return string $new_tag
257
  */
258
+ private function optionally_defer_excluded( $tag, $url = '' )
259
  {
260
  // Defer single CSS if "inline & defer" is ON and there is inline CSS.
261
  if ( $this->defer && ! empty( $this->defer_inline ) ) {
266
  $url
267
  );
268
  // Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback.
269
+ $new_tag = '<noscript>' . $tag . '</noscript>' . str_replace(
270
  array(
271
  "rel='stylesheet'",
272
  'rel="stylesheet"',
273
  ),
274
  "rel='preload' as='style' onload=\"" . $_preload_onload . "\"",
275
+ $tag
276
  );
277
+ } else {
278
+ $new_tag = $tag;
279
  }
280
+
281
  return $new_tag;
282
  }
283
 
classes/autoptimizeUtils.php CHANGED
@@ -45,7 +45,7 @@ class autoptimizeUtils
45
  public static function strpos( $haystack, $needle, $offset = 0, $encoding = null )
46
  {
47
  if ( self::mbstring_available() ) {
48
- return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strlen( $haystack, $needle, $offset, $encoding );
49
  } else {
50
  return \strpos( $haystack, $needle, $offset );
51
  }
@@ -301,9 +301,11 @@ class autoptimizeUtils
301
  /**
302
  * Checks to see if 3rd party services are available and stores result in option
303
  *
 
 
304
  * @param string $return_result should we return resulting service status array (default no).
305
  *
306
- * @return none if $return_result is false (default), array if $return_result is true.
307
  */
308
  public static function check_service_availability( $return_result = false )
309
  {
@@ -319,6 +321,7 @@ class autoptimizeUtils
319
  }
320
  }
321
  }
 
322
  }
323
 
324
  /**
45
  public static function strpos( $haystack, $needle, $offset = 0, $encoding = null )
46
  {
47
  if ( self::mbstring_available() ) {
48
+ return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strpos( $haystack, $needle, $offset, $encoding );
49
  } else {
50
  return \strpos( $haystack, $needle, $offset );
51
  }
301
  /**
302
  * Checks to see if 3rd party services are available and stores result in option
303
  *
304
+ * TODO This should be two separate methods.
305
+ *
306
  * @param string $return_result should we return resulting service status array (default no).
307
  *
308
+ * @return null|array Service status or null.
309
  */
310
  public static function check_service_availability( $return_result = false )
311
  {
321
  }
322
  }
323
  }
324
+ return null;
325
  }
326
 
327
  /**
classes/autoptimizeVersionUpdatesHandler.php CHANGED
@@ -236,12 +236,16 @@ class autoptimizeVersionUpdatesHandler
236
  * Migrate imgopt options from autoptimize_extra_settings to autoptimize_imgopt_settings
237
  */
238
  private function upgrade_from_2_4() {
239
- $extra_settings = get_option( 'autoptimize_extra_settings' );
240
- $imgopt_settings = get_option( 'autoptimize_imgopt_settings' );
241
- if ( empty( $imgopt_settings ) ) {
242
- $imgopt_settings = autoptimizeConfig::get_ao_imgopt_default_options();
243
- $imgopt_settings['autoptimize_imgopt_checkbox_field_1'] = $extra_settings['autoptimize_extra_checkbox_field_5'];
244
- $imgopt_settings['autoptimize_imgopt_select_field_2'] = $extra_settings['autoptimize_extra_select_field_6'];
 
 
 
 
245
  update_option( 'autoptimize_imgopt_settings', $imgopt_settings );
246
  }
247
  }
236
  * Migrate imgopt options from autoptimize_extra_settings to autoptimize_imgopt_settings
237
  */
238
  private function upgrade_from_2_4() {
239
+ $extra_settings = get_option( 'autoptimize_extra_settings', '' );
240
+ $imgopt_settings = get_option( 'autoptimize_imgopt_settings', '' );
241
+ if ( empty( $imgopt_settings ) && ! empty( $extra_settings ) ) {
242
+ $imgopt_settings = autoptimizeConfig::get_ao_imgopt_default_options();
243
+ if ( array_key_exists( 'autoptimize_extra_checkbox_field_5', $extra_settings ) ) {
244
+ $imgopt_settings['autoptimize_imgopt_checkbox_field_1'] = $extra_settings['autoptimize_extra_checkbox_field_5'];
245
+ }
246
+ if ( array_key_exists( 'autoptimize_extra_select_field_6', $extra_settings ) ) {
247
+ $imgopt_settings['autoptimize_imgopt_select_field_2'] = $extra_settings['autoptimize_extra_select_field_6'];
248
+ }
249
  update_option( 'autoptimize_imgopt_settings', $imgopt_settings );
250
  }
251
  }
classes/external/js/lazysizes.min.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! lazysizes - v4.1.7 */
2
- !function(a,b){var c=b(a,a.document);a.lazySizes=c,"object"==typeof module&&module.exports&&(module.exports=c)}(window,function(a,b){"use strict";if(b.getElementsByClassName){var c,d,e=b.documentElement,f=a.Date,g=a.HTMLPictureElement,h="addEventListener",i="getAttribute",j=a[h],k=a.setTimeout,l=a.requestAnimationFrame||k,m=a.requestIdleCallback,n=/^picture$/i,o=["load","error","lazyincluded","_lazyloaded"],p={},q=Array.prototype.forEach,r=function(a,b){return p[b]||(p[b]=new RegExp("(\\s|^)"+b+"(\\s|$)")),p[b].test(a[i]("class")||"")&&p[b]},s=function(a,b){r(a,b)||a.setAttribute("class",(a[i]("class")||"").trim()+" "+b)},t=function(a,b){var c;(c=r(a,b))&&a.setAttribute("class",(a[i]("class")||"").replace(c," "))},u=function(a,b,c){var d=c?h:"removeEventListener";c&&u(a,b),o.forEach(function(c){a[d](c,b)})},v=function(a,d,e,f,g){var h=b.createEvent("Event");return e||(e={}),e.instance=c,h.initEvent(d,!f,!g),h.detail=e,a.dispatchEvent(h),h},w=function(b,c){var e;!g&&(e=a.picturefill||d.pf)?(c&&c.src&&!b[i]("srcset")&&b.setAttribute("srcset",c.src),e({reevaluate:!0,elements:[b]})):c&&c.src&&(b.src=c.src)},x=function(a,b){return(getComputedStyle(a,null)||{})[b]},y=function(a,b,c){for(c=c||a.offsetWidth;c<d.minSize&&b&&!a._lazysizesWidth;)c=b.offsetWidth,b=b.parentNode;return c},z=function(){var a,c,d=[],e=[],f=d,g=function(){var b=f;for(f=d.length?e:d,a=!0,c=!1;b.length;)b.shift()();a=!1},h=function(d,e){a&&!e?d.apply(this,arguments):(f.push(d),c||(c=!0,(b.hidden?k:l)(g)))};return h._lsFlush=g,h}(),A=function(a,b){return b?function(){z(a)}:function(){var b=this,c=arguments;z(function(){a.apply(b,c)})}},B=function(a){var b,c=0,e=d.throttleDelay,g=d.ricTimeout,h=function(){b=!1,c=f.now(),a()},i=m&&g>49?function(){m(h,{timeout:g}),g!==d.ricTimeout&&(g=d.ricTimeout)}:A(function(){k(h)},!0);return function(a){var d;(a=!0===a)&&(g=33),b||(b=!0,d=e-(f.now()-c),d<0&&(d=0),a||d<9?i():k(i,d))}},C=function(a){var b,c,d=99,e=function(){b=null,a()},g=function(){var a=f.now()-c;a<d?k(g,d-a):(m||e)(e)};return function(){c=f.now(),b||(b=k(g,d))}};!function(){var b,c={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",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};d=a.lazySizesConfig||a.lazysizesConfig||{};for(b in c)b in d||(d[b]=c[b]);a.lazySizesConfig=d,k(function(){d.init&&F()})}();var D=function(){var g,l,m,o,p,y,D,F,G,H,I,J,K=/^img$/i,L=/^iframe$/i,M="onscroll"in a&&!/(gle|ing)bot/.test(navigator.userAgent),N=0,O=0,P=0,Q=-1,R=function(a){P--,(!a||P<0||!a.target)&&(P=0)},S=function(a){return null==J&&(J="hidden"==x(b.body,"visibility")),J||"hidden"!=x(a.parentNode,"visibility")&&"hidden"!=x(a,"visibility")},T=function(a,c){var d,f=a,g=S(a);for(F-=c,I+=c,G-=c,H+=c;g&&(f=f.offsetParent)&&f!=b.body&&f!=e;)(g=(x(f,"opacity")||1)>0)&&"visible"!=x(f,"overflow")&&(d=f.getBoundingClientRect(),g=H>d.left&&G<d.right&&I>d.top-1&&F<d.bottom+1);return g},U=function(){var a,f,h,j,k,m,n,p,q,r,s,t,u=c.elements;if((o=d.loadMode)&&P<8&&(a=u.length)){for(f=0,Q++,r=!d.expand||d.expand<1?e.clientHeight>500&&e.clientWidth>500?500:370:d.expand,c._defEx=r,s=r*d.expFactor,t=d.hFac,J=null,O<s&&P<1&&Q>2&&o>2&&!b.hidden?(O=s,Q=0):O=o>1&&Q>1&&P<6?r:N;f<a;f++)if(u[f]&&!u[f]._lazyRace)if(M)if((p=u[f][i]("data-expand"))&&(m=1*p)||(m=O),q!==m&&(y=innerWidth+m*t,D=innerHeight+m,n=-1*m,q=m),h=u[f].getBoundingClientRect(),(I=h.bottom)>=n&&(F=h.top)<=D&&(H=h.right)>=n*t&&(G=h.left)<=y&&(I||H||G||F)&&(d.loadHidden||S(u[f]))&&(l&&P<3&&!p&&(o<3||Q<4)||T(u[f],m))){if(aa(u[f]),k=!0,P>9)break}else!k&&l&&!j&&P<4&&Q<4&&o>2&&(g[0]||d.preloadAfterLoad)&&(g[0]||!p&&(I||H||G||F||"auto"!=u[f][i](d.sizesAttr)))&&(j=g[0]||u[f]);else aa(u[f]);j&&!k&&aa(j)}},V=B(U),W=function(a){var b=a.target;if(b._lazyCache)return void delete b._lazyCache;R(a),s(b,d.loadedClass),t(b,d.loadingClass),u(b,Y),v(b,"lazyloaded")},X=A(W),Y=function(a){X({target:a.target})},Z=function(a,b){try{a.contentWindow.location.replace(b)}catch(c){a.src=b}},$=function(a){var b,c=a[i](d.srcsetAttr);(b=d.customMedia[a[i]("data-media")||a[i]("media")])&&a.setAttribute("media",b),c&&a.setAttribute("srcset",c)},_=A(function(a,b,c,e,f){var g,h,j,l,o,p;(o=v(a,"lazybeforeunveil",b)).defaultPrevented||(e&&(c?s(a,d.autosizesClass):a.setAttribute("sizes",e)),h=a[i](d.srcsetAttr),g=a[i](d.srcAttr),f&&(j=a.parentNode,l=j&&n.test(j.nodeName||"")),p=b.firesLoad||"src"in a&&(h||g||l),o={target:a},s(a,d.loadingClass),p&&(clearTimeout(m),m=k(R,2500),u(a,Y,!0)),l&&q.call(j.getElementsByTagName("source"),$),h?a.setAttribute("srcset",h):g&&!l&&(L.test(a.nodeName)?Z(a,g):a.src=g),f&&(h||l)&&w(a,{src:g})),a._lazyRace&&delete a._lazyRace,t(a,d.lazyClass),z(function(){(!p||a.complete&&a.naturalWidth>1)&&(W(o),a._lazyCache=!0,k(function(){"_lazyCache"in a&&delete a._lazyCache},9))},!0)}),aa=function(a){var b,c=K.test(a.nodeName),e=c&&(a[i](d.sizesAttr)||a[i]("sizes")),f="auto"==e;(!f&&l||!c||!a[i]("src")&&!a.srcset||a.complete||r(a,d.errorClass)||!r(a,d.lazyClass))&&(b=v(a,"lazyunveilread").detail,f&&E.updateElem(a,!0,a.offsetWidth),a._lazyRace=!0,P++,_(a,b,f,e,c))},ba=function(){if(!l){if(f.now()-p<999)return void k(ba,999);var a=C(function(){d.loadMode=3,V()});l=!0,d.loadMode=3,V(),j("scroll",function(){3==d.loadMode&&(d.loadMode=2),a()},!0)}};return{_:function(){p=f.now(),c.elements=b.getElementsByClassName(d.lazyClass),g=b.getElementsByClassName(d.lazyClass+" "+d.preloadClass),j("scroll",V,!0),j("resize",V,!0),a.MutationObserver?new MutationObserver(V).observe(e,{childList:!0,subtree:!0,attributes:!0}):(e[h]("DOMNodeInserted",V,!0),e[h]("DOMAttrModified",V,!0),setInterval(V,999)),j("hashchange",V,!0),["focus","mouseover","click","load","transitionend","animationend","webkitAnimationEnd"].forEach(function(a){b[h](a,V,!0)}),/d$|^c/.test(b.readyState)?ba():(j("load",ba),b[h]("DOMContentLoaded",V),k(ba,2e4)),c.elements.length?(U(),z._lsFlush()):V()},checkElems:V,unveil:aa}}(),E=function(){var a,c=A(function(a,b,c,d){var e,f,g;if(a._lazysizesWidth=d,d+="px",a.setAttribute("sizes",d),n.test(b.nodeName||""))for(e=b.getElementsByTagName("source"),f=0,g=e.length;f<g;f++)e[f].setAttribute("sizes",d);c.detail.dataAttr||w(a,c.detail)}),e=function(a,b,d){var e,f=a.parentNode;f&&(d=y(a,f,d),e=v(a,"lazybeforesizes",{width:d,dataAttr:!!b}),e.defaultPrevented||(d=e.detail.width)&&d!==a._lazysizesWidth&&c(a,f,e,d))},f=function(){var b,c=a.length;if(c)for(b=0;b<c;b++)e(a[b])},g=C(f);return{_:function(){a=b.getElementsByClassName(d.autosizesClass),j("resize",g)},checkElems:g,updateElem:e}}(),F=function(){F.i||(F.i=!0,E._(),D._())};return c={cfg:d,autoSizer:E,loader:D,init:F,uP:w,aC:s,rC:t,hC:r,fire:v,gW:y,rAF:z}}});
1
+ /*! lazysizes - v5.0.0 */
2
+ !function(a,b){var c=b(a,a.document);a.lazySizes=c,"object"==typeof module&&module.exports&&(module.exports=c)}(window,function(a,b){"use strict";if(b.getElementsByClassName){var c,d,e=b.documentElement,f=a.Date,g=a.HTMLPictureElement,h="addEventListener",i="getAttribute",j=a[h],k=a.setTimeout,l=a.requestAnimationFrame||k,m=a.requestIdleCallback,n=/^picture$/i,o=["load","error","lazyincluded","_lazyloaded"],p={},q=Array.prototype.forEach,r=function(a,b){return p[b]||(p[b]=new RegExp("(\\s|^)"+b+"(\\s|$)")),p[b].test(a[i]("class")||"")&&p[b]},s=function(a,b){r(a,b)||a.setAttribute("class",(a[i]("class")||"").trim()+" "+b)},t=function(a,b){var c;(c=r(a,b))&&a.setAttribute("class",(a[i]("class")||"").replace(c," "))},u=function(a,b,c){var d=c?h:"removeEventListener";c&&u(a,b),o.forEach(function(c){a[d](c,b)})},v=function(a,d,e,f,g){var h=b.createEvent("Event");return e||(e={}),e.instance=c,h.initEvent(d,!f,!g),h.detail=e,a.dispatchEvent(h),h},w=function(b,c){var e;!g&&(e=a.picturefill||d.pf)?(c&&c.src&&!b[i]("srcset")&&b.setAttribute("srcset",c.src),e({reevaluate:!0,elements:[b]})):c&&c.src&&(b.src=c.src)},x=function(a,b){return(getComputedStyle(a,null)||{})[b]},y=function(a,b,c){for(c=c||a.offsetWidth;c<d.minSize&&b&&!a._lazysizesWidth;)c=b.offsetWidth,b=b.parentNode;return c},z=function(){var a,c,d=[],e=[],f=d,g=function(){var b=f;for(f=d.length?e:d,a=!0,c=!1;b.length;)b.shift()();a=!1},h=function(d,e){a&&!e?d.apply(this,arguments):(f.push(d),c||(c=!0,(b.hidden?k:l)(g)))};return h._lsFlush=g,h}(),A=function(a,b){return b?function(){z(a)}:function(){var b=this,c=arguments;z(function(){a.apply(b,c)})}},B=function(a){var b,c=0,e=d.throttleDelay,g=d.ricTimeout,h=function(){b=!1,c=f.now(),a()},i=m&&g>49?function(){m(h,{timeout:g}),g!==d.ricTimeout&&(g=d.ricTimeout)}:A(function(){k(h)},!0);return function(a){var d;(a=!0===a)&&(g=33),b||(b=!0,d=e-(f.now()-c),d<0&&(d=0),a||d<9?i():k(i,d))}},C=function(a){var b,c,d=99,e=function(){b=null,a()},g=function(){var a=f.now()-c;a<d?k(g,d-a):(m||e)(e)};return function(){c=f.now(),b||(b=k(g,d))}};!function(){var b,c={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",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};d=a.lazySizesConfig||a.lazysizesConfig||{};for(b in c)b in d||(d[b]=c[b]);k(function(){d.init&&F()})}();var D=function(){var g,l,m,o,p,y,D,F,G,H,I,J,K=/^img$/i,L=/^iframe$/i,M="onscroll"in a&&!/(gle|ing)bot/.test(navigator.userAgent),N=0,O=0,P=0,Q=-1,R=function(a){P--,(!a||P<0||!a.target)&&(P=0)},S=function(a){return null==J&&(J="hidden"==x(b.body,"visibility")),J||"hidden"!=x(a.parentNode,"visibility")&&"hidden"!=x(a,"visibility")},T=function(a,c){var d,f=a,g=S(a);for(F-=c,I+=c,G-=c,H+=c;g&&(f=f.offsetParent)&&f!=b.body&&f!=e;)(g=(x(f,"opacity")||1)>0)&&"visible"!=x(f,"overflow")&&(d=f.getBoundingClientRect(),g=H>d.left&&G<d.right&&I>d.top-1&&F<d.bottom+1);return g},U=function(){var a,f,h,j,k,m,n,p,q,r,s,t,u=c.elements;if((o=d.loadMode)&&P<8&&(a=u.length)){for(f=0,Q++;f<a;f++)if(u[f]&&!u[f]._lazyRace)if(!M||c.prematureUnveil&&c.prematureUnveil(u[f]))aa(u[f]);else if((p=u[f][i]("data-expand"))&&(m=1*p)||(m=O),r||(r=!d.expand||d.expand<1?e.clientHeight>500&&e.clientWidth>500?500:370:d.expand,c._defEx=r,s=r*d.expFactor,t=d.hFac,J=null,O<s&&P<1&&Q>2&&o>2&&!b.hidden?(O=s,Q=0):O=o>1&&Q>1&&P<6?r:N),q!==m&&(y=innerWidth+m*t,D=innerHeight+m,n=-1*m,q=m),h=u[f].getBoundingClientRect(),(I=h.bottom)>=n&&(F=h.top)<=D&&(H=h.right)>=n*t&&(G=h.left)<=y&&(I||H||G||F)&&(d.loadHidden||S(u[f]))&&(l&&P<3&&!p&&(o<3||Q<4)||T(u[f],m))){if(aa(u[f]),k=!0,P>9)break}else!k&&l&&!j&&P<4&&Q<4&&o>2&&(g[0]||d.preloadAfterLoad)&&(g[0]||!p&&(I||H||G||F||"auto"!=u[f][i](d.sizesAttr)))&&(j=g[0]||u[f]);j&&!k&&aa(j)}},V=B(U),W=function(a){var b=a.target;if(b._lazyCache)return void delete b._lazyCache;R(a),s(b,d.loadedClass),t(b,d.loadingClass),u(b,Y),v(b,"lazyloaded")},X=A(W),Y=function(a){X({target:a.target})},Z=function(a,b){try{a.contentWindow.location.replace(b)}catch(c){a.src=b}},$=function(a){var b,c=a[i](d.srcsetAttr);(b=d.customMedia[a[i]("data-media")||a[i]("media")])&&a.setAttribute("media",b),c&&a.setAttribute("srcset",c)},_=A(function(a,b,c,e,f){var g,h,j,l,o,p;(o=v(a,"lazybeforeunveil",b)).defaultPrevented||(e&&(c?s(a,d.autosizesClass):a.setAttribute("sizes",e)),h=a[i](d.srcsetAttr),g=a[i](d.srcAttr),f&&(j=a.parentNode,l=j&&n.test(j.nodeName||"")),p=b.firesLoad||"src"in a&&(h||g||l),o={target:a},s(a,d.loadingClass),p&&(clearTimeout(m),m=k(R,2500),u(a,Y,!0)),l&&q.call(j.getElementsByTagName("source"),$),h?a.setAttribute("srcset",h):g&&!l&&(L.test(a.nodeName)?Z(a,g):a.src=g),f&&(h||l)&&w(a,{src:g})),a._lazyRace&&delete a._lazyRace,t(a,d.lazyClass),z(function(){var b=a.complete&&a.naturalWidth>1;p&&!b||(b&&s(a,"ls-is-cached"),W(o),a._lazyCache=!0,k(function(){"_lazyCache"in a&&delete a._lazyCache},9)),"lazy"==a.loading&&P--},!0)}),aa=function(a){if(!a._lazyRace){var b,c=K.test(a.nodeName),e=c&&(a[i](d.sizesAttr)||a[i]("sizes")),f="auto"==e;(!f&&l||!c||!a[i]("src")&&!a.srcset||a.complete||r(a,d.errorClass)||!r(a,d.lazyClass))&&(b=v(a,"lazyunveilread").detail,f&&E.updateElem(a,!0,a.offsetWidth),a._lazyRace=!0,P++,_(a,b,f,e,c))}},ba=C(function(){d.loadMode=3,V()}),ca=function(){3==d.loadMode&&(d.loadMode=2),ba()},da=function(){if(!l){if(f.now()-p<999)return void k(da,999);l=!0,d.loadMode=3,V(),j("scroll",ca,!0)}};return{_:function(){p=f.now(),c.elements=b.getElementsByClassName(d.lazyClass),g=b.getElementsByClassName(d.lazyClass+" "+d.preloadClass),j("scroll",V,!0),j("resize",V,!0),a.MutationObserver?new MutationObserver(V).observe(e,{childList:!0,subtree:!0,attributes:!0}):(e[h]("DOMNodeInserted",V,!0),e[h]("DOMAttrModified",V,!0),setInterval(V,999)),j("hashchange",V,!0),["focus","mouseover","click","load","transitionend","animationend"].forEach(function(a){b[h](a,V,!0)}),/d$|^c/.test(b.readyState)?da():(j("load",da),b[h]("DOMContentLoaded",V),k(da,2e4)),c.elements.length?(U(),z._lsFlush()):V()},checkElems:V,unveil:aa,_aLSL:ca}}(),E=function(){var a,c=A(function(a,b,c,d){var e,f,g;if(a._lazysizesWidth=d,d+="px",a.setAttribute("sizes",d),n.test(b.nodeName||""))for(e=b.getElementsByTagName("source"),f=0,g=e.length;f<g;f++)e[f].setAttribute("sizes",d);c.detail.dataAttr||w(a,c.detail)}),e=function(a,b,d){var e,f=a.parentNode;f&&(d=y(a,f,d),e=v(a,"lazybeforesizes",{width:d,dataAttr:!!b}),e.defaultPrevented||(d=e.detail.width)&&d!==a._lazysizesWidth&&c(a,f,e,d))},f=function(){var b,c=a.length;if(c)for(b=0;b<c;b++)e(a[b])},g=C(f);return{_:function(){a=b.getElementsByClassName(d.autosizesClass),j("resize",g)},checkElems:g,updateElem:e}}(),F=function(){F.i||(F.i=!0,E._(),D._())};return c={cfg:d,autoSizer:E,loader:D,init:F,uP:w,aC:s,rC:t,hC:r,fire:v,gW:y,rAF:z}}});
classes/external/php/minify-html.php CHANGED
@@ -18,6 +18,9 @@
18
  */
19
  class Minify_HTML {
20
 
 
 
 
21
  /**
22
  * "Minify" an HTML page
23
  *
18
  */
19
  class Minify_HTML {
20
 
21
+ /** @var string */
22
+ private $_html;
23
+
24
  /**
25
  * "Minify" an HTML page
26
  *
readme.txt CHANGED
@@ -1,17 +1,17 @@
1
  === Autoptimize ===
2
  Contributors: futtta, optimizingmatters, zytzagoo, turl
3
- Tags: optimize, minify, performance, pagespeed, images, google fonts
4
  Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.0
6
  Tested up to: 5.2
7
  Requires PHP: 5.3
8
- Stable tag: 2.5.0
9
 
10
- Autoptimize speeds up your website by optimizing JS, CSS, images, HTML, Google Fonts and async-ing JS, removing emoji cruft and more.
11
 
12
  == Description ==
13
 
14
- Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default but can also inline critical CSS and defer the aggregated full CSS, moves and defers scripts to the footer and minifies HTML. You can optimize and lazyload images, optimize Google Fonts, async non-aggregated JavaScript, remove WordPress core emoji cruft and more. As such it can improve your site's performance even when already on HTTP/2! There is extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
15
 
16
  If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/), [HyperCache](http://wordpress.org/plugins/hyper-cache/), [Comet Cache](https://wordpress.org/plugins/comet-cache/) or [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler).
17
 
@@ -36,7 +36,7 @@ It concatenates all scripts and styles, minifies and compresses them, adds expir
36
 
37
  = But I'm on HTTP/2, so I don't need Autoptimize? =
38
 
39
- HTTP/2 is a great step forward for sure, reducing the impact of multiple requests from the same server significantly by using the same connection to perform several concurrent requests. That being said, [concatenation of CSS/ JS can still make a lot of sense](http://engineering.khanacademy.org/posts/js-packaging-http2.htm), as described in [this css-tricks.com article](https://css-tricks.com/http2-real-world-performance-test-analysis/) and this [blogpost from one of the Ebay engineers](http://calendar.perfplanet.com/2015/packaging-for-performance/). The conclusion; configure, test, reconfigure, retest, tweak and look what works best in your context. Maybe it's just HTTP/2, maybe it's HTTP/2 + aggregation and minification, maybe it's HTTP/2 + minification (which AO can do as well, simply untick the "aggregate JS-files" and/ or "aggregate CSS-files" options).
40
 
41
  = Will this work with my blog? =
42
 
@@ -64,15 +64,7 @@ There's no easy solution for that as "above the fold" depends on where the fold
64
 
65
  = Or should you inline all CSS? =
66
 
67
- The short answer: probably not.
68
-
69
- Back in the days CSS optimization was easy; put all CSS in your head, aggregating everything in one CSS-file per media-type and you were good to go. But ever since Google included mobile in PageSpeed Insights and started complaining about render blocking CSS, things got messy (see "deferring CSS" elsewhere in this FAQ). One of the solutions is inlining all your CSS, which as of Autoptimize 1.8.0 is supported.
70
-
71
- Inlining all CSS has one clear advantage (better PageSpeed score) and one big disadvantage; your base HTML-page gets significantly bigger and if the amount of CSS is big, Pagespeed Insights will complain of "roundtrip times". Also when looking at a test that includes multiple requests (let's say 5 pages), performance will be worse, as the CSS-payload is sent over again and again whereas normally the separate CSS-files would not need to be sent any more as they would be in cache.
72
-
73
- So the choice should be based on your answer to some site-specific questions; how much CSS do you have? How many pages per visit do your visitors request? If you have a lot of CSS or a high number of pages/ visit, it's probably not a good idea to inline all CSS.
74
-
75
- You can find more information on this topic [in this blog post](http://blog.futtta.be/2014/02/13/should-you-inline-or-defer-blocking-css/).
76
 
77
  = My cache is getting huge, doesn't Autoptimize purge the cache? =
78
 
@@ -283,6 +275,13 @@ Just [fork Autoptimize on Github](https://github.com/futtta/autoptimize) and cod
283
 
284
  == Changelog ==
285
 
 
 
 
 
 
 
 
286
  = 2.5.0 =
287
  * moved image optimization to a separate tab and move all code to a separate file.
288
  * added lazyloading (using lazysizes)
1
  === Autoptimize ===
2
  Contributors: futtta, optimizingmatters, zytzagoo, turl
3
+ Tags: optimize, minify, performance, pagespeed, images, lazy-load, google fonts
4
  Donate link: http://blog.futtta.be/2013/10/21/do-not-donate-to-me/
5
  Requires at least: 4.0
6
  Tested up to: 5.2
7
  Requires PHP: 5.3
8
+ Stable tag: 2.5.1
9
 
10
+ Autoptimize speeds up your website by optimizing JS, CSS, images (incl. lazy-load), HTML and Google Fonts, asyncing JS, removing emoji cruft and more.
11
 
12
  == Description ==
13
 
14
+ Autoptimize makes optimizing your site really easy. It can aggregate, minify and cache scripts and styles, injects CSS in the page head by default but can also inline critical CSS and defer the aggregated full CSS, moves and defers scripts to the footer and minifies HTML. You can optimize and lazy-load images, optimize Google Fonts, async non-aggregated JavaScript, remove WordPress core emoji cruft and more. As such it can improve your site's performance even when already on HTTP/2! There is extensive API available to enable you to tailor Autoptimize to each and every site's specific needs.
15
 
16
  If you consider performance important, you really should use one of the many caching plugins to do page caching. Some good candidates to complement Autoptimize that way are e.g. [WP Super Cache](http://wordpress.org/plugins/wp-super-cache/), [HyperCache](http://wordpress.org/plugins/hyper-cache/), [Comet Cache](https://wordpress.org/plugins/comet-cache/) or [KeyCDN's Cache Enabler](https://wordpress.org/plugins/cache-enabler).
17
 
36
 
37
  = But I'm on HTTP/2, so I don't need Autoptimize? =
38
 
39
+ HTTP/2 is a great step forward for sure, reducing the impact of multiple requests from the same server significantly by using the same connection to perform several concurrent requests. That being said, [concatenation of CSS/ JS can still make a lot of sense](http://engineering.khanacademy.org/posts/js-packaging-http2.htm), as described in [this css-tricks.com article](https://css-tricks.com/http2-real-world-performance-test-analysis/) and this [blogpost from one of the Ebay engineers](http://calendar.perfplanet.com/2015/packaging-for-performance/). The conclusion; configure, test, reconfigure, retest, tweak and look what works best in your context. Maybe it's just HTTP/2, maybe it's HTTP/2 + aggregation and minification, maybe it's HTTP/2 + minification (which AO can do as well, simply untick the "aggregate JS-files" and/ or "aggregate CSS-files" options). And Autoptimize can do a lot more then "just" optimizing your JS & CSS off course ;-)
40
 
41
  = Will this work with my blog? =
42
 
64
 
65
  = Or should you inline all CSS? =
66
 
67
+ The short answer: probably not. Although inlining all CSS will make the CSS non-render blocking, it will result in your base HTML-page getting significantly bigger thus requiring more "roundtrip times". Moreover when considering multiple pages being requested in a browsing session the inline CSS is sent over each time, whereas when not inlined it would be served from cache.
 
 
 
 
 
 
 
 
68
 
69
  = My cache is getting huge, doesn't Autoptimize purge the cache? =
70
 
275
 
276
  == Changelog ==
277
 
278
+ = 2.5.1 =
279
+ * Images: Also optimize & lazyload &lt;picture>&lt;source>
280
+ * Images: Misc. improvements to lazyload
281
+ * Images: Updated to LazySizes 5.0.0
282
+ * CSS: improvements to the deferring logic for non-aggregated CSS resources.
283
+ * Settings-page: Show "JS, CSS & HTML" advanced options by default (many people did not see the button)
284
+
285
  = 2.5.0 =
286
  * moved image optimization to a separate tab and move all code to a separate file.
287
  * added lazyloading (using lazysizes)