PluginProbe
Autoptimize / 2.7.0
Autoptimize v2.7.0
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
autoptimize / classes / autoptimizeExtra.php

autoptimizeExtra.php in Autoptimize 2.7.0, at classes/autoptimizeExtra.php

585 lines 29.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles autoptimizeExtra frontend features + admin options page
4 */
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit;
8 }
9
10 class autoptimizeExtra
11 {
12 /**
13 * Options
14 *
15 * @var array
16 */
17 protected $options = array();
18
19 /**
20 * Singleton instance.
21 *
22 * @var self|null
23 */
24 protected static $instance = null;
25
26 /**
27 * Creates an instance and calls run().
28 *
29 * @param array $options Optional. Allows overriding options without having to specify them via admin options page.
30 */
31 public function __construct( $options = array() )
32 {
33 if ( empty( $options ) ) {
34 $options = self::fetch_options();
35 }
36
37 $this->options = $options;
38 }
39
40 /**
41 * Helper for getting a singleton instance. While being an
42 * anti-pattern generally, it comes in handy for now from a
43 * readability/maintainability perspective, until we get some
44 * proper dependency injection going.
45 *
46 * @return self
47 */
48 public static function instance()
49 {
50 if ( null === self::$instance ) {
51 self::$instance = new self();
52 }
53
54 return self::$instance;
55 }
56
57 public function run()
58 {
59 if ( is_admin() ) {
60 if ( is_multisite() && is_network_admin() && autoptimizeOptionWrapper::is_ao_active_for_network() ) {
61 add_action( 'network_admin_menu', array( $this, 'admin_menu' ) );
62 } else {
63 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
64 }
65 add_filter( 'autoptimize_filter_settingsscreen_tabs', array( $this, 'add_extra_tab' ) );
66 } else {
67 add_action( 'wp', array( $this, 'run_on_frontend' ) );
68 }
69 }
70
71 public function set_options( array $options )
72 {
73 $this->options = $options;
74
75 return $this;
76 }
77
78 public static function fetch_options()
79 {
80 $value = autoptimizeOptionWrapper::get_option( 'autoptimize_extra_settings' );
81 if ( empty( $value ) ) {
82 // Fallback to returning defaults when no stored option exists yet.
83 $value = autoptimizeConfig::get_ao_extra_default_options();
84 }
85
86 return $value;
87 }
88
89 public function disable_emojis()
90 {
91 // Removing all actions related to emojis!
92 remove_action( 'admin_print_styles', 'print_emoji_styles' );
93 remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
94 remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
95 remove_action( 'wp_print_styles', 'print_emoji_styles' );
96 remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );
97 remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
98 remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
99
100 // Removes TinyMCE emojis.
101 add_filter( 'tiny_mce_plugins', array( $this, 'filter_disable_emojis_tinymce' ) );
102
103 // Removes emoji dns-preftech.
104 add_filter( 'wp_resource_hints', array( $this, 'filter_remove_emoji_dns_prefetch' ), 10, 2 );
105 }
106
107 public function filter_disable_emojis_tinymce( $plugins )
108 {
109 if ( is_array( $plugins ) ) {
110 return array_diff( $plugins, array( 'wpemoji' ) );
111 } else {
112 return array();
113 }
114 }
115
116 public function filter_remove_qs( $src )
117 {
118 if ( strpos( $src, '?ver=' ) ) {
119 $src = remove_query_arg( 'ver', $src );
120 }
121
122 return $src;
123 }
124
125 public function extra_async_js( $in )
126 {
127 $exclusions = array();
128 if ( ! empty( $in ) ) {
129 $exclusions = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $in ) ) ), '' );
130 }
131
132 $settings = $this->options['autoptimize_extra_text_field_3'];
133 $async = array_fill_keys( array_filter( array_map( 'trim', explode( ',', $settings ) ) ), '' );
134 $attr = apply_filters( 'autoptimize_filter_extra_async', 'async' );
135 foreach ( $async as $k => $v ) {
136 $async[ $k ] = $attr;
137 }
138
139 // Merge exclusions & asyncs in one array and return to AO API.
140 $merged = array_merge( $exclusions, $async );
141
142 return $merged;
143 }
144
145 public function run_on_frontend()
146 {
147 // only run the Extra optimizations on frontend if general conditions
148 // for optimizations are met, this to ensure e.g. removing querystrings
149 // is not done when optimizing for logged in users is off, breaking
150 // some pagebuilders (Divi & Elementor).
151 if ( false === autoptimizeMain::should_buffer() ) {
152 return;
153 }
154
155 $options = $this->options;
156
157 // Disable emojis if specified.
158 if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) ) {
159 $this->disable_emojis();
160 }
161
162 // Remove version query parameters.
163 if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) ) {
164 add_filter( 'script_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
165 add_filter( 'style_loader_src', array( $this, 'filter_remove_qs' ), 15, 1 );
166 }
167
168 // Avoiding conflicts of interest when async-javascript plugin is active!
169 $async_js_plugin_active = autoptimizeUtils::is_plugin_active( 'async-javascript/async-javascript.php' );
170 if ( ! empty( $options['autoptimize_extra_text_field_3'] ) && ! $async_js_plugin_active ) {
171 add_filter( 'autoptimize_filter_js_exclude', array( $this, 'extra_async_js' ), 10, 1 );
172 }
173
174 // Optimize google fonts!
175 if ( ! empty( $options['autoptimize_extra_radio_field_4'] ) && ( '1' !== $options['autoptimize_extra_radio_field_4'] ) ) {
176 add_filter( 'wp_resource_hints', array( $this, 'filter_remove_gfonts_dnsprefetch' ), 10, 2 );
177 add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_optimize_google_fonts' ), 10, 1 );
178 add_filter( 'autoptimize_extra_filter_tobepreconn', array( $this, 'filter_preconnect_google_fonts' ), 10, 1 );
179 }
180
181 // Preconnect!
182 if ( ! empty( $options['autoptimize_extra_text_field_2'] ) || has_filter( 'autoptimize_extra_filter_tobepreconn' ) ) {
183 add_filter( 'wp_resource_hints', array( $this, 'filter_preconnect' ), 10, 2 );
184 }
185
186 // Preload!
187 if ( ! empty( $options['autoptimize_extra_text_field_7'] ) ) {
188 add_filter( 'autoptimize_html_after_minify', array( $this, 'filter_preload' ), 10, 2 );
189 }
190 }
191
192 public function filter_remove_emoji_dns_prefetch( $urls, $relation_type )
193 {
194 $emoji_svg_url = apply_filters( 'emoji_svg_url', 'https://s.w.org/images/core/emoji/' );
195
196 return $this->filter_remove_dns_prefetch( $urls, $relation_type, $emoji_svg_url );
197 }
198
199 public function filter_remove_gfonts_dnsprefetch( $urls, $relation_type )
200 {
201 return $this->filter_remove_dns_prefetch( $urls, $relation_type, 'fonts.googleapis.com' );
202 }
203
204 public function filter_remove_dns_prefetch( $urls, $relation_type, $url_to_remove )
205 {
206 $url_to_remove = (string) $url_to_remove;
207
208 if ( ! empty( $url_to_remove ) && 'dns-prefetch' === $relation_type ) {
209 $cnt = 0;
210 foreach ( $urls as $url ) {
211 if ( false !== strpos( $url, $url_to_remove ) ) {
212 unset( $urls[ $cnt ] );
213 }
214 $cnt++;
215 }
216 }
217
218 return $urls;
219 }
220
221 public function filter_optimize_google_fonts( $in )
222 {
223 // Extract fonts, partly based on wp rocket's extraction code.
224 $markup = preg_replace( '/<!--(.*)-->/Uis', '', $in );
225 preg_match_all( '#<link(?:\s+(?:(?!href\s*=\s*)[^>])+)?(?:\s+href\s*=\s*([\'"])((?:https?:)?\/\/fonts\.googleapis\.com\/css(?:(?!\1).)+)\1)(?:\s+[^>]*)?>#iU', $markup, $matches );
226
227 $fonts_collection = array();
228 if ( ! $matches[2] ) {
229 return $in;
230 }
231
232 // Store them in $fonts array.
233 $i = 0;
234 foreach ( $matches[2] as $font ) {
235 if ( ! preg_match( '/rel=["\']dns-prefetch["\']/', $matches[0][ $i ] ) ) {
236 // Get fonts name.
237 $font = str_replace( array( '%7C', '%7c' ), '|', $font );
238 if ( strpos( $font, 'fonts.googleapis.com/css2' ) !== false ) {
239 // (Somewhat) change Google Fonts APIv2 syntax back to v1.
240 $font = str_replace( array( 'wght@', 'wght%40', ';', '%3B' ), array( '', '', ',', ',' ), $font );
241 }
242 $font = explode( 'family=', $font );
243 $font = ( isset( $font[1] ) ) ? explode( '&', $font[1] ) : array();
244 // Add font to $fonts[$i] but make sure not to pollute with an empty family!
245 $_thisfont = array_values( array_filter( explode( '|', reset( $font ) ) ) );
246 if ( ! empty( $_thisfont ) ) {
247 $fonts_collection[ $i ]['fonts'] = $_thisfont;
248 // And add subset if any!
249 $subset = ( is_array( $font ) ) ? end( $font ) : '';
250 if ( false !== strpos( $subset, 'subset=' ) ) {
251 $subset = str_replace( array( '%2C', '%2c' ), ',', $subset );
252 $subset = explode( 'subset=', $subset );
253 $fonts_collection[ $i ]['subsets'] = explode( ',', $subset[1] );
254 }
255 }
256 // And remove Google Fonts.
257 $in = str_replace( $matches[0][ $i ], '', $in );
258 }
259 $i++;
260 }
261
262 $options = $this->options;
263 $fonts_markup = '';
264 if ( '2' === $options['autoptimize_extra_radio_field_4'] ) {
265 // Remove Google Fonts.
266 unset( $fonts_collection );
267 return $in;
268 } elseif ( '3' === $options['autoptimize_extra_radio_field_4'] || '5' === $options['autoptimize_extra_radio_field_4'] ) {
269 // Aggregate & link!
270 $fonts_string = '';
271 $subset_string = '';
272 foreach ( $fonts_collection as $font ) {
273 $fonts_string .= '|' . trim( implode( '|', $font['fonts'] ), '|' );
274 if ( ! empty( $font['subsets'] ) ) {
275 $subset_string .= ',' . trim( implode( ',', $font['subsets'] ), ',' );
276 }
277 }
278
279 if ( ! empty( $subset_string ) ) {
280 $subset_string = str_replace( ',', '%2C', ltrim( $subset_string, ',' ) );
281 $fonts_string = $fonts_string . '&#038;subset=' . $subset_string;
282 }
283
284 $fonts_string = apply_filters( 'autoptimize_filter_extra_gfont_fontstring', str_replace( '|', '%7C', ltrim( $fonts_string, '|' ) ) );
285 // only add display parameter if there is none in $fonts_string (by virtue of the filter).
286 if ( strpos( $fonts_string, 'display=' ) === false ) {
287 $fonts_string .= apply_filters( 'autoptimize_filter_extra_gfont_display', '&amp;display=swap' );
288 }
289
290 if ( ! empty( $fonts_string ) ) {
291 if ( '5' === $options['autoptimize_extra_radio_field_4'] ) {
292 $rel_string = 'rel="preload" as="style" onload="' . autoptimizeConfig::get_ao_css_preload_onload() . '"';
293 } else {
294 $rel_string = 'rel="stylesheet"';
295 }
296 $fonts_markup = '<link ' . $rel_string . ' id="ao_optimized_gfonts" href="https://fonts.googleapis.com/css?family=' . $fonts_string . '" />';
297 }
298 } elseif ( '4' === $options['autoptimize_extra_radio_field_4'] ) {
299 // Aggregate & load async (webfont.js impl.)!
300 $fonts_array = array();
301 foreach ( $fonts_collection as $_fonts ) {
302 if ( ! empty( $_fonts['subsets'] ) ) {
303 $_subset = implode( ',', $_fonts['subsets'] );
304 foreach ( $_fonts['fonts'] as $key => $_one_font ) {
305 $_one_font = $_one_font . ':' . $_subset;
306 $_fonts['fonts'][ $key ] = $_one_font;
307 }
308 }
309 $fonts_array = array_merge( $fonts_array, $_fonts['fonts'] );
310 }
311
312 $fonts_array = array_map( 'urldecode', $fonts_array );
313 $fonts_array = array_map(
314 function( $_f ) {
315 return trim( $_f, ',' );
316 },
317 $fonts_array
318 );
319
320 // type attrib on <script not added by default.
321 $type_js = '';
322 if ( apply_filters( 'autoptimize_filter_cssjs_addtype', false ) ) {
323 $type_js = 'type="text/javascript" ';
324 }
325
326 $fonts_markup = '<script ' . $type_js . 'data-cfasync="false" id="ao_optimized_gfonts_config">WebFontConfig={google:{families:' . wp_json_encode( $fonts_array ) . ' },classes:false, events:false, timeout:1500};</script>';
327 $fonts_library_markup = '<script ' . $type_js . 'data-cfasync="false" id="ao_optimized_gfonts_webfontloader">(function() {var wf = document.createElement(\'script\');wf.src=\'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js\';wf.type=\'text/javascript\';wf.async=\'true\';var s=document.getElementsByTagName(\'script\')[0];s.parentNode.insertBefore(wf, s);})();</script>';
328 $in = substr_replace( $in, $fonts_library_markup . '</head>', strpos( $in, '</head>' ), strlen( '</head>' ) );
329 }
330
331 // Replace back in markup.
332 $inject_point = apply_filters( 'autoptimize_filter_extra_gfont_injectpoint', '<link' );
333 $out = substr_replace( $in, $fonts_markup . $inject_point, strpos( $in, $inject_point ), strlen( $inject_point ) );
334 unset( $fonts_collection );
335
336 // and insert preload polyfill if "link preload" and if the polyfill isn't there yet (courtesy of inline&defer).
337 $preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill();
338 if ( '5' === $options['autoptimize_extra_radio_field_4'] && strpos( $out, $preload_polyfill ) === false ) {
339 $out = str_replace( '</body>', $preload_polyfill . '</body>', $out );
340 }
341 return $out;
342 }
343
344 public function filter_preconnect( $hints, $relation_type )
345 {
346 $options = $this->options;
347 $preconns = array();
348
349 // Get settings and store in array.
350 if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) {
351 $preconns = array_filter( array_map( 'trim', explode( ',', $options['autoptimize_extra_text_field_2'] ) ) );
352 }
353 $preconns = apply_filters( 'autoptimize_extra_filter_tobepreconn', $preconns );
354
355 // Walk array, extract domain and add to new array with crossorigin attribute.
356 foreach ( $preconns as $preconn ) {
357 $domain = '';
358 $parsed = parse_url( $preconn );
359 if ( is_array( $parsed ) && ! empty( $parsed['host'] ) && empty( $parsed['scheme'] ) ) {
360 $domain = '//' . $parsed['host'];
361 } elseif ( is_array( $parsed ) && ! empty( $parsed['host'] ) ) {
362 $domain = $parsed['scheme'] . '://' . $parsed['host'];
363 }
364
365 if ( ! empty( $domain ) ) {
366 $hint = array( 'href' => $domain );
367 // Fonts don't get preconnected unless crossorigin flag is set, non-fonts don't get preconnected if origin flag is set
368 // so hardcode fonts.gstatic.com to come with crossorigin and have filter to add other domains if needed.
369 $crossorigins = apply_filters( 'autoptimize_extra_filter_preconn_crossorigin', array( 'https://fonts.gstatic.com' ) );
370 if ( in_array( $domain, $crossorigins ) ) {
371 $hint['crossorigin'] = 'anonymous';
372 }
373 $new_hints[] = $hint;
374 }
375 }
376
377 // Merge in WP's preconnect hints.
378 if ( 'preconnect' === $relation_type && ! empty( $new_hints ) ) {
379 $hints = array_merge( $hints, $new_hints );
380 }
381
382 return $hints;
383 }
384
385 public function filter_preconnect_google_fonts( $in )
386 {
387 if ( '2' !== $this->options['autoptimize_extra_radio_field_4'] ) {
388 // Preconnect to fonts.gstatic.com unless we remove gfonts.
389 $in[] = 'https://fonts.gstatic.com';
390 }
391
392 if ( '4' === $this->options['autoptimize_extra_radio_field_4'] ) {
393 // Preconnect even more hosts for webfont.js!
394 $in[] = 'https://ajax.googleapis.com';
395 $in[] = 'https://fonts.googleapis.com';
396 }
397
398 return $in;
399 }
400
401 public function filter_preload( $in ) {
402 // make array from comma separated list.
403 $options = $this->options;
404 $preloads = array();
405 if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) {
406 $preloads = array_filter( array_map( 'trim', explode( ',', $options['autoptimize_extra_text_field_7'] ) ) );
407 }
408 $preloads = apply_filters( 'autoptimize_filter_extra_tobepreloaded', $preloads );
409
410 // immediately return if nothing to be preloaded.
411 if ( empty( $preloads ) ) {
412 return $in;
413 }
414
415 // iterate through array and add preload link to tmp string.
416 $preload_output = '';
417 foreach ( $preloads as $preload ) {
418 $crossorigin = '';
419 $preload_as = '';
420 $mime_type = '';
421 $_preload = strtok( $preload, '?' );
422
423 if ( autoptimizeUtils::str_ends_in( $_preload, '.css' ) ) {
424 $preload_as = 'style';
425 } elseif ( autoptimizeUtils::str_ends_in( $_preload, '.js' ) ) {
426 $preload_as = 'script';
427 } elseif ( autoptimizeUtils::str_ends_in( $_preload, '.woff' ) || autoptimizeUtils::str_ends_in( $_preload, '.woff2' ) || autoptimizeUtils::str_ends_in( $_preload, '.ttf' ) || autoptimizeUtils::str_ends_in( $_preload, '.eot' ) ) {
428 $preload_as = 'font';
429 $crossorigin = ' crossorigin';
430 $mime_type = ' type="font/' . pathinfo( $_preload, PATHINFO_EXTENSION ) . '"';
431 if ( ' type="font/eot"' === $mime_type ) {
432 $mime_type = 'application/vnd.ms-fontobject';
433 }
434 } elseif ( autoptimizeUtils::str_ends_in( $_preload, '.jpeg' ) || autoptimizeUtils::str_ends_in( $_preload, '.jpg' ) || autoptimizeUtils::str_ends_in( $_preload, '.webp' ) || autoptimizeUtils::str_ends_in( $_preload, '.png' ) || autoptimizeUtils::str_ends_in( $_preload, '.gif' ) ) {
435 $preload_as = 'image';
436 } else {
437 $preload_as = 'other';
438 }
439
440 $preload_output .= '<link rel="preload" href="' . $preload . '" as="' . $preload_as . '"' . $mime_type . $crossorigin . '>';
441 }
442 $preload_output = apply_filters( 'autoptimize_filter_extra_preload_output', $preload_output );
443
444 // add string to head (before first link node by default).
445 $preload_inject = apply_filters( 'autoptimize_filter_extra_preload_inject', '<link' );
446 $position = autoptimizeUtils::strpos( $in, $preload_inject );
447
448 return autoptimizeUtils::substr_replace( $in, $preload_output . $preload_inject, $position, strlen( $preload_inject ) );
449 }
450
451 public function admin_menu()
452 {
453 // no acces if multisite and not network admin and no site config allowed.
454 if ( autoptimizeConfig::should_show_menu_tabs() ) {
455 add_submenu_page(
456 null,
457 'autoptimize_extra',
458 'autoptimize_extra',
459 'manage_options',
460 'autoptimize_extra',
461 array( $this, 'options_page' )
462 );
463 }
464 register_setting( 'autoptimize_extra_settings', 'autoptimize_extra_settings' );
465 }
466
467 public function add_extra_tab( $in )
468 {
469 if ( autoptimizeConfig::should_show_menu_tabs() ) {
470 $in = array_merge( $in, array( 'autoptimize_extra' => __( 'Extra', 'autoptimize' ) ) );
471 }
472
473 return $in;
474 }
475
476 public function options_page()
477 {
478 // Working with actual option values from the database here.
479 // That way any saves are still processed as expected, but we can still
480 // override behavior by using `new autoptimizeExtra($custom_options)` and not have that custom
481 // behavior being persisted in the DB even if save is done here.
482 $options = $this->fetch_options();
483 $gfonts = $options['autoptimize_extra_radio_field_4'];
484 ?>
485 <style>
486 #ao_settings_form {background: white;border: 1px solid #ccc;padding: 1px 15px;margin: 15px 10px 10px 0;}
487 #ao_settings_form .form-table th {font-weight: normal;}
488 #autoptimize_extra_descr{font-size: 120%;}
489 </style>
490 <script>document.title = "Autoptimize: <?php _e( 'Extra', 'autoptimize' ); ?> " + document.title;</script>
491 <div class="wrap">
492 <h1><?php _e( 'Autoptimize Settings', 'autoptimize' ); ?></h1>
493 <?php echo autoptimizeConfig::ao_admin_tabs(); ?>
494 <?php if ( 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_js' ) && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_css' ) && 'on' !== autoptimizeOptionWrapper::get_option( 'autoptimize_html' ) && ! autoptimizeImages::imgopt_active() ) { ?>
495 <div class="notice-warning notice"><p>
496 <?php _e( 'Most of below Extra optimizations require at least one of HTML, JS, CSS or Image autoptimizations being active.', 'autoptimize' ); ?>
497 </p></div>
498 <?php } ?>
499
500 <form id='ao_settings_form' action='<?php echo admin_url( 'options.php' ); ?>' method='post'>
501 <?php settings_fields( 'autoptimize_extra_settings' ); ?>
502 <h2><?php _e( 'Extra Auto-Optimizations', 'autoptimize' ); ?></h2>
503 <span id='autoptimize_extra_descr'><?php _e( 'The following settings can improve your site\'s performance even more.', 'autoptimize' ); ?></span>
504 <table class="form-table">
505 <tr>
506 <th scope="row"><?php _e( 'Google Fonts', 'autoptimize' ); ?></th>
507 <td>
508 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="1" <?php if ( ! in_array( $gfonts, array( 2, 3, 4, 5 ) ) ) { echo 'checked'; } ?> ><?php _e( 'Leave as is', 'autoptimize' ); ?><br/>
509 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="2" <?php checked( 2, $gfonts, true ); ?> ><?php _e( 'Remove Google Fonts', 'autoptimize' ); ?><br/>
510 <?php // translators: "display:swap" should remain untranslated, will be shown in code tags. ?>
511 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="3" <?php checked( 3, $gfonts, true ); ?> ><?php echo __( 'Combine and link in head (fonts load fast but are render-blocking)', 'autoptimize' ) . ', ' . sprintf( __( 'includes %1$sdisplay:swap%2$s.', 'autoptimize' ), '<code>', '</code>' ); ?><br/>
512 <?php // translators: "display:swap" should remain untranslated, will be shown in code tags. ?>
513 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="5" <?php checked( 5, $gfonts, true ); ?> ><?php echo __( 'Combine and preload in head (fonts load late, but are not render-blocking)', 'autoptimize' ) . ', ' . sprintf( __( 'includes %1$sdisplay:swap%2$s.', 'autoptimize' ), '<code>', '</code>' ); ?><br/>
514 <input type="radio" name="autoptimize_extra_settings[autoptimize_extra_radio_field_4]" value="4" <?php checked( 4, $gfonts, true ); ?> ><?php _e( 'Combine and load fonts asynchronously with <a href="https://github.com/typekit/webfontloader#readme" target="_blank">webfont.js</a>', 'autoptimize' ); ?><br/>
515 </td>
516 </tr>
517 <tr>
518 <th scope="row"><?php _e( 'Remove emojis', 'autoptimize' ); ?></th>
519 <td>
520 <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_1]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_1'] ) && '1' === $options['autoptimize_extra_checkbox_field_1'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Removes WordPress\' core emojis\' inline CSS, inline JavaScript, and an otherwise un-autoptimized JavaScript file.', 'autoptimize' ); ?></label>
521 </td>
522 </tr>
523 <tr>
524 <th scope="row"><?php _e( 'Remove query strings from static resources', 'autoptimize' ); ?></th>
525 <td>
526 <label><input type='checkbox' name='autoptimize_extra_settings[autoptimize_extra_checkbox_field_0]' <?php if ( ! empty( $options['autoptimize_extra_checkbox_field_0'] ) && '1' === $options['autoptimize_extra_checkbox_field_0'] ) { echo 'checked="checked"'; } ?> value='1'><?php _e( 'Removing query strings (or more specifically the <code>ver</code> parameter) will not improve load time, but might improve performance scores.', 'autoptimize' ); ?></label>
527 </td>
528 </tr>
529 <tr>
530 <th scope="row"><?php _e( 'Preconnect to 3rd party domains <em>(advanced users)</em>', 'autoptimize' ); ?></th>
531 <td>
532 <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_2]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_2', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_2'] ); } ?>'><br /><?php _e( 'Add 3rd party domains you want the browser to <a href="https://www.keycdn.com/support/preconnect/#primary" target="_blank">preconnect</a> to, separated by comma\'s. Make sure to include the correct protocol (HTTP or HTTPS).', 'autoptimize' ); ?></label>
533 </td>
534 </tr>
535 <tr>
536 <th scope="row"><?php _e( 'Preload specific requests <em>(advanced users)</em>', 'autoptimize' ); ?></th>
537 <td>
538 <label><input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_7]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_7', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_7'] ); } ?>'><br /><?php _e( 'Comma-separated list with full URL\'s of to to-be-preloaded resources. To be used sparingly!', 'autoptimize' ); ?></label>
539 </td>
540 </tr>
541 <tr>
542 <th scope="row"><?php _e( 'Async Javascript-files <em>(advanced users)</em>', 'autoptimize' ); ?></th>
543 <td>
544 <?php
545 if ( autoptimizeUtils::is_plugin_active( 'async-javascript/async-javascript.php' ) ) {
546 // translators: link points Async Javascript settings page.
547 printf( __( 'You have "Async JavaScript" installed, %1$sconfiguration of async javascript is best done there%2$s.', 'autoptimize' ), '<a href="' . 'options-general.php?page=async-javascript' . '">', '</a>' );
548 } else {
549 ?>
550 <input type='text' style='width:80%' name='autoptimize_extra_settings[autoptimize_extra_text_field_3]' value='<?php if ( array_key_exists( 'autoptimize_extra_text_field_3', $options ) ) { echo esc_attr( $options['autoptimize_extra_text_field_3'] ); } ?>'>
551 <br />
552 <?php
553 _e( 'Comma-separated list of local or 3rd party JS-files that should loaded with the <code>async</code> flag. JS-files from your own site will be automatically excluded if added here. ', 'autoptimize' );
554 // translators: %s will be replaced by a link to the "async javascript" plugin.
555 echo sprintf( __( 'Configuration of async javascript is easier and more flexible using the %s plugin.', 'autoptimize' ), '"<a href="https://wordpress.org/plugins/async-javascript" target="_blank">Async Javascript</a>"' );
556 $asj_install_url = network_admin_url() . 'plugin-install.php?s=async+javascript&tab=search&type=term';
557 echo sprintf( ' <a href="' . $asj_install_url . '">%s</a>', __( 'Click here to install and activate it.', 'autoptimize' ) );
558 }
559 ?>
560 </td>
561 </tr>
562 <tr>
563 <th scope="row"><?php _e( 'Optimize YouTube videos', 'autoptimize' ); ?></th>
564 <td>
565 <?php
566 if ( autoptimizeUtils::is_plugin_active( 'wp-youtube-lyte/wp-youtube-lyte.php' ) ) {
567 _e( 'Great, you have WP YouTube Lyte installed.', 'autoptimize' );
568 $lyte_config_url = 'options-general.php?page=lyte_settings_page';
569 echo sprintf( ' <a href="' . $lyte_config_url . '">%s</a>', __( 'Click here to configure it.', 'autoptimize' ) );
570 } else {
571 // translators: %s will be replaced by a link to "wp youtube lyte" plugin.
572 echo sprintf( __( '%s allows you to “lazy load” your videos, by inserting responsive “Lite YouTube Embeds". ', 'autoptimize' ), '<a href="https://wordpress.org/plugins/wp-youtube-lyte" target="_blank">WP YouTube Lyte</a>' );
573 $lyte_install_url = network_admin_url() . 'plugin-install.php?s=lyte&tab=search&type=term';
574 echo sprintf( ' <a href="' . $lyte_install_url . '">%s</a>', __( 'Click here to install and activate it.', 'autoptimize' ) );
575 }
576 ?>
577 </td>
578 </tr>
579 </table>
580 <p class="submit"><input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Changes', 'autoptimize' ); ?>" /></p>
581 </form>
582 <?php
583 }
584 }
585