diff --git a/classes/autoptimizeConfig.php b/classes/autoptimizeConfig.php index f5cf60c2..07501ea5 100644 --- a/classes/autoptimizeConfig.php +++ b/classes/autoptimizeConfig.php @@ -866,6 +866,7 @@ public static function get_ao_extra_default_options() 'autoptimize_extra_text_field_3' => '', 'autoptimize_extra_text_field_7' => '', 'autoptimize_extra_checkbox_field_8' => '0', + 'autoptimize_extra_checkbox_field_9' => '1', ); return $defaults; diff --git a/classes/autoptimizeExtra.php b/classes/autoptimizeExtra.php index af0e080c..9014224b 100644 --- a/classes/autoptimizeExtra.php +++ b/classes/autoptimizeExtra.php @@ -35,6 +35,11 @@ public function __construct( $options = array() ) } $this->options = $options; + + // Potentially load quicklink. + if ( ! empty( $options['autoptimize_extra_checkbox_field_9'] ) ) { + require_once dirname( __FILE__ ) . '/external/php/quicklink/quicklink.php'; + } } /** @@ -565,6 +570,12 @@ public function options_page() >webfont.js', 'autoptimize' ) . ' ' . __( '(deprecated)', 'autoptimize' ); ?>
+ + + + + + diff --git a/classes/external/php/quicklink/plugin-compatibility/woocommerce.php b/classes/external/php/quicklink/plugin-compatibility/woocommerce.php new file mode 100644 index 00000000..9ebeacd5 --- /dev/null +++ b/classes/external/php/quicklink/plugin-compatibility/woocommerce.php @@ -0,0 +1,48 @@ +0&&(n.shift()(),r++)}return[function(e){n.push(e)>1||t()},function(){r--,t()}]}(e.throttle||1/0),r=n[0],a=n[1],u=e.limit||1/0,l=e.origins||[location.hostname],d=e.ignores||[],h=e.delay||0,p=[],m=e.timeoutFn||t,w="function"==typeof e.hrefFn&&e.hrefFn,g=e.prerender||!1;c=e.prerenderAndPrefetch||!1;var v=new IntersectionObserver(function(n){n.forEach(function(n){if(n.isIntersecting)p.push((n=n.target).href),function(e,n){n?setTimeout(e,n):e()}(function(){-1!==p.indexOf(n.href)&&(v.unobserve(n),(c||g)&&i.size<1?f(w?w(n):n.href).catch(function(n){if(!e.onError)throw n;e.onError(n)}):o.size-1&&p.splice(t)}})},{threshold:e.threshold||0});return m(function(){(e.el||document).querySelectorAll("a").forEach(function(e){l.length&&!l.includes(e.hostname)||function e(n,r){return Array.isArray(r)?r.some(function(r){return e(n,r)}):(r.test||r).call(r,n.href,n)}(e,d)||v.observe(e)})},{timeout:e.timeout||2e3}),function(){o.clear(),v.disconnect()}}}function s(n,t,u){var s=a(navigator.connection);return s instanceof Error?Promise.reject(new Error("Cannot prefetch, "+s.message)):(i.size>0&&!c&&console.warn("[Warning] You are using both prefetching and prerendering on the same document"),Promise.all([].concat(n).map(function(n){if(!o.has(n))return o.add(n),(t?function(n){return window.fetch?fetch(n,{credentials:"include"}):e(n)}:r)(new URL(n,location.href).toString())})))}function f(e,n){var r=a(navigator.connection);if(r instanceof Error)return Promise.reject(new Error("Cannot prerender, "+r.message));if(!HTMLScriptElement.supports("speculationrules"))return s(e),Promise.reject(new Error("This browser does not support the speculation rules API. Falling back to prefetch."));if(document.querySelector('script[type="speculationrules"]'))return Promise.reject(new Error("Speculation Rules is already defined and cannot be altered."));for(var t=0,u=[].concat(e);t0&&!c&&console.warn("[Warning] You are using both prefetching and prerendering on the same document");var l=function(e){var n=document.createElement("script");n.type="speculationrules",n.text='{"prerender":[{"source": "list","urls": ["'+Array.from(e).join('","')+'"]}]}';try{document.head.appendChild(n)}catch(e){return e}return!0}(i);return!0===l?Promise.resolve():Promise.reject(l)} + +;// CONCATENATED MODULE: ./build/js/quicklink.js + + +// Move quicklink to the global scope +window.quicklink = quicklink_namespaceObject; +__webpack_require__.g.addEventListener('load', () => { + const exportedOptions = window.quicklinkOptions || {}; + const listenerOptions = {}; + + // el: Convert selector into element reference. + if ('string' === typeof exportedOptions.el && exportedOptions.el) { + listenerOptions.el = document.querySelector(exportedOptions.el); + } + + // timeout: Verify we actually get an int for milliseconds. + if ('number' === typeof exportedOptions.timeout) { + listenerOptions.timeout = exportedOptions.timeout; + } + + // limit: Verify we actually get an int. + if ('number' === typeof exportedOptions.limit && exportedOptions.limit > 0) { + listenerOptions.limit = exportedOptions.limit; + } + + // throttle: Verify we actually get an int. + if ('number' === typeof exportedOptions.throttle && exportedOptions.throttle > 0) { + listenerOptions.throttle = exportedOptions.throttle; + } + + // timeoutFn: Obtain function reference as opposed to function string, if it is not the default. + if ('string' === typeof exportedOptions.timeoutFn && 'requestIdleCallback' !== exportedOptions.timeoutFn && typeof 'function' === window[exportedOptions.timeoutFn]) { + const timeoutFn = window[exportedOptions.timeoutFn]; + listenerOptions.timeoutFn = function () { + return timeoutFn.apply(window, arguments); + }; + } + + // onError: Obtain function reference as opposed to function string, if it is not the default. + if ('string' === typeof exportedOptions.onError && typeof 'function' === window[exportedOptions.onError]) { + const onError = window[exportedOptions.onError]; + listenerOptions.onError = function () { + return onError.apply(window, arguments); + }; + } + + // priority: Obtain priority. + if ('boolean' === typeof exportedOptions.priority) { + listenerOptions.priority = exportedOptions.priority; + } + + // origins: Verify we don't get an empty array, as that would turn off quicklink. + if (Array.isArray(exportedOptions.origins) && 0 < exportedOptions.origins.length) { + listenerOptions.origins = exportedOptions.origins; + } + + // ignores: Convert strings to regular expressions. + if (Array.isArray(exportedOptions.ignores) && 0 < exportedOptions.ignores.length) { + listenerOptions.ignores = exportedOptions.ignores.map(ignore => { + return new RegExp(ignore); + }); + } + u(listenerOptions); + + /** + * The option to prefetch urls from the options is deprecated as of version 0.8.0. + */ + if (Array.isArray(exportedOptions.urls) && 0 < exportedOptions.urls.length) { + s(exportedOptions.urls); + } +}); +/******/ })() +; \ No newline at end of file diff --git a/classes/external/php/quicklink/quicklink.min.js b/classes/external/php/quicklink/quicklink.min.js new file mode 100644 index 00000000..ee512920 --- /dev/null +++ b/classes/external/php/quicklink/quicklink.min.js @@ -0,0 +1 @@ +!function(){"use strict";var e={d:function(r,n){for(var t in n)e.o(n,t)&&!e.o(r,t)&&Object.defineProperty(r,t,{enumerable:!0,get:n[t]})}};e.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),e.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},e.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var r={};function n(e){return new Promise((function(r,n,t){(t=new XMLHttpRequest).open("GET",e,t.withCredentials=!0),t.onload=function(){200===t.status?r():n()},t.send()}))}e.r(r),e.d(r,{listen:function(){return l},prefetch:function(){return f},prerender:function(){return d}});var t,o=(t=document.createElement("link")).relList&&t.relList.supports&&t.relList.supports("prefetch")?function(e){return new Promise((function(r,n,t){(t=document.createElement("link")).rel="prefetch",t.href=e,t.onload=r,t.onerror=n,document.head.appendChild(t)}))}:n,i=window.requestIdleCallback||function(e){var r=Date.now();return setTimeout((function(){e({didTimeout:!1,timeRemaining:function(){return Math.max(0,50-(Date.now()-r))}})}),1)},u=new Set,c=new Set,s=!1;function a(e){if(e){if(e.saveData)return new Error("Save-Data is enabled");if(/2g/.test(e.effectiveType))return new Error("network conditions are poor")}return!0}function l(e){if(e||(e={}),window.IntersectionObserver){var r=function(e){e=e||1;var r=[],n=0;function t(){n0&&(r.shift()(),n++)}return[function(e){r.push(e)>1||t()},function(){n--,t()}]}(e.throttle||1/0),n=r[0],t=r[1],o=e.limit||1/0,a=e.origins||[location.hostname],l=e.ignores||[],p=e.delay||0,h=[],m=e.timeoutFn||i,w="function"==typeof e.hrefFn&&e.hrefFn,g=e.prerender||!1;s=e.prerenderAndPrefetch||!1;var y=new IntersectionObserver((function(r){r.forEach((function(r){if(r.isIntersecting)h.push((r=r.target).href),function(e,r){r?setTimeout(e,r):e()}((function(){-1!==h.indexOf(r.href)&&(y.unobserve(r),(s||g)&&c.size<1?d(w?w(r):r.href).catch((function(r){if(!e.onError)throw r;e.onError(r)})):u.size-1&&h.splice(i)}}))}),{threshold:e.threshold||0});return m((function(){(e.el||document).querySelectorAll("a").forEach((function(e){a.length&&!a.includes(e.hostname)||function e(r,n){return Array.isArray(n)?n.some((function(n){return e(r,n)})):(n.test||n).call(n,r.href,r)}(e,l)||y.observe(e)}))}),{timeout:e.timeout||2e3}),function(){u.clear(),y.disconnect()}}}function f(e,r,t){var i=a(navigator.connection);return i instanceof Error?Promise.reject(new Error("Cannot prefetch, "+i.message)):(c.size>0&&!s&&console.warn("[Warning] You are using both prefetching and prerendering on the same document"),Promise.all([].concat(e).map((function(e){if(!u.has(e))return u.add(e),(r?function(e){return window.fetch?fetch(e,{credentials:"include"}):n(e)}:o)(new URL(e,location.href).toString())}))))}function d(e,r){var n=a(navigator.connection);if(n instanceof Error)return Promise.reject(new Error("Cannot prerender, "+n.message));if(!HTMLScriptElement.supports("speculationrules"))return f(e),Promise.reject(new Error("This browser does not support the speculation rules API. Falling back to prefetch."));if(document.querySelector('script[type="speculationrules"]'))return Promise.reject(new Error("Speculation Rules is already defined and cannot be altered."));for(var t=0,o=[].concat(e);t0&&!s&&console.warn("[Warning] You are using both prefetching and prerendering on the same document");var l=function(e){var r=document.createElement("script");r.type="speculationrules",r.text='{"prerender":[{"source": "list","urls": ["'+Array.from(e).join('","')+'"]}]}';try{document.head.appendChild(r)}catch(e){return e}return!0}(c);return!0===l?Promise.resolve():Promise.reject(l)}window.quicklink=r,e.g.addEventListener("load",(()=>{const e=window.quicklinkOptions||{},r={};if("string"==typeof e.el&&e.el&&(r.el=document.querySelector(e.el)),"number"==typeof e.timeout&&(r.timeout=e.timeout),"number"==typeof e.limit&&e.limit>0&&(r.limit=e.limit),"number"==typeof e.throttle&&e.throttle>0&&(r.throttle=e.throttle),"string"==typeof e.timeoutFn&&"requestIdleCallback"!==e.timeoutFn&&"string"===window[e.timeoutFn]){const n=window[e.timeoutFn];r.timeoutFn=function(){return n.apply(window,arguments)}}if("string"==typeof e.onError&&"string"===window[e.onError]){const n=window[e.onError];r.onError=function(){return n.apply(window,arguments)}}"boolean"==typeof e.priority&&(r.priority=e.priority),Array.isArray(e.origins)&&0new RegExp(e)))),l(r),Array.isArray(e.urls)&&0 '', + + // Static array of URLs to prefetch. Deprecated since plugin version 0.8.0. + 'urls' => array(), + + // Integer for the `requestIdleCallback` timeout. A time in milliseconds by which the browser must execute prefetching. Defaults to 2 seconds. + 'timeout' => 2000, + + // Function for specifying a timeout. Defaults to `requestIdleCallback`. Can also be swapped out for a custom function like [networkIdleCallback](https://github.com/pastelsky/network-idle-callback) (see demos). + 'timeoutFn' => 'requestIdleCallback', + + // Boolean specifying preferred priority for fetches. Defaults to `false`. `true` will attempt to use the `fetch()` API where supported (rather than rel=prefetch). + 'priority' => false, + + // Static array of URL hostname strings that are allowed to be prefetched. Defaults to the same domain origin, which prevents _any_ cross-origin requests. + 'origins' => array( + wp_parse_url( home_url(), PHP_URL_HOST ), + ), + + // Array of regex strings that further determines if a URL should be prefetched. These execute _after_ origin matching. + 'ignores' => array( + // Do not preload feed links. + preg_quote( 'feed=', '/' ), + preg_quote( '/feed/', '/' ), + + // Do not preload self, including self with hash. + '^https?:\/\/[^\/]+' . preg_quote( wp_unslash( $_SERVER['REQUEST_URI'] ), '/' ) . '(#.*)?$', // phpcs:ignore + + // Don't pre-fetch links to the admin since they could be nonce links. + '^' . preg_quote( admin_url(), '/' ), + + // Don't pre-fetch links to PHP files, like wp-login.php. + '^' . preg_quote( site_url(), '/' ) . '[^?#]+\.php', + + // Do not preload wp-content items (like downloads). + preg_quote( wp_parse_url( content_url(), PHP_URL_PATH ), '/' ), + + // Do not preload links with get parameters. + '.*\?.+', + ), + ); + + /** + * Filters Quicklink options. + * + * @link https://getquick.link/api/ + * @param array { + * Configuration options for Quicklink. + * + * @param string $el CSS selector for the DOM element to observe for in-viewport links to prefetch. + * @param int $limit The total requests that can be prefetched while observing the $el container. + * @param int $throttle The concurrency limit for simultaneous requests while observing the $el container. + * @param int $timeout Timeout after which prefetching will occur. + * @param string $timeoutFn Custom timeout function. Must refer to a named global function in JS. + * @param bool $priority Attempt higher priority fetch (low or high). Default false. + * @param string[] $origins Allowed origins to prefetch (empty allows all). Defaults to host for current home URL. + * @param string[] $ignores Regular expression patterns to determine whether a URL is ignored. Runs after origin checks. + * @param string $onError Custom error handler. Must refer to a named global function in JS. + * @param string[] $urls Array of URLs to prefetch. Deprecated as of plugin version 0.8.0 + * } + */ + $options = apply_filters( 'quicklink_options', $options ); + + /** + * Add a deprecation warning for the $options[urls] option. + * + * @since 0.8.0 + */ + if ( isset( $options['urls'] ) && ! empty( $options['urls'] ) ) { + _deprecated_argument( + __FUNCTION__, + '0.8.0', + esc_attr( __( 'The "urls" argument has been deprecated in favor of the dedicated `quicklink_prefetch()` function.', 'quicklink' ) ) + ); + } + + wp_add_inline_script( + 'quicklink', + sprintf( 'var quicklinkOptions = %s;', wp_json_encode( $options ) ), + 'before' + ); +} +add_action( 'wp_enqueue_scripts', 'quicklink_enqueue_scripts' ); + +/** + * Add async attribute to Quicklink script tag. + * + * @link https://github.com/WordPress/twentynineteen/pull/646 + * @link https://github.com/wprig/wprig/blob/9a7c23d8d3db735259de6c338ddbb7cb7fd0ada1/dev/inc/template-functions.php#L41-L70 + * @link https://core.trac.wordpress.org/ticket/12009 + * + * @param string $tag Script tag. + * @param string $handle Script handle. + * @return string Script tag. + */ +function quicklink_add_async_attr_to_script_loader_tag( $tag, $handle ) { + if ( 'quicklink' === $handle && false === strpos( $tag, 'async' ) ) { + $tag = preg_replace( ':(?=>):', ' async', $tag ); + } + return $tag; +} +add_filter( 'script_loader_tag', 'quicklink_add_async_attr_to_script_loader_tag', 10, 2 ); + +/** + * Add some plugin compatibility files when everything is ready. + * + * @return void + */ +function quicklink_plugin_compatibility_files() { + if ( class_exists( 'WooCommerce' ) ) { + require_once QUICKLINK_PATH . '/plugin-compatibility/woocommerce.php'; + } +} +add_action( 'init', 'quicklink_plugin_compatibility_files' ); + +/** + * Add quicklink to the default scripts to make it available earlier in the runtime. + * + * @param WP_Scripts $scripts The WP_Scripts instance. + * + * @return void + */ +function quicklink_to_default_scripts( $scripts ) { + $scripts->add( 'quicklink', QUICKLINK_URL . 'quicklink.min.js', array(), '0.10.0' ); +} +add_action( 'wp_default_scripts', 'quicklink_to_default_scripts' ); + +/** + * Prefetch a url with quicklink. + * + * @param string $url The URL to be prefetched. + * @param boolean $is_priority Whether or not the URL should be treated as high priority target. + * + * @return void + */ +function quicklink_prefetch( $url, $is_priority = false ) { + wp_add_inline_script( + 'quicklink', + sprintf( + 'window.addEventListener( "load", function() { quicklink.prefetch( %s, %s ); } );', + wp_json_encode( $url ), + wp_json_encode( $is_priority ) + ), + 'after' + ); +} diff --git a/classes/external/php/quicklink/readme.txt b/classes/external/php/quicklink/readme.txt new file mode 100644 index 00000000..12fdc165 --- /dev/null +++ b/classes/external/php/quicklink/readme.txt @@ -0,0 +1,100 @@ +=== Quicklink for WordPress === + +Contributors: wpmunich, google, luehrsen, westonruter +Tags: performance, speed, fast, prefetch, seo, http2, preconnect, optimization +Requires at least: 4.9 +Tested up to: 6.2 +Requires PHP: 5.6 +Stable tag: 0.10.0 +License: GPLv2 or later +License URI: http://www.gnu.org/licenses/gpl-2.0.html + +⚡️ Faster subsequent page-loads by prefetching in-viewport links during idle time. + +== Description == + +Quicklink for WordPress attempts to make navigation to subsequent pages load faster. Embedded with the plugin is a javascript library, which detects links in the viewport, waits until the browser is idle and prefetches the URLs for these links. The library also tries to detect, if the user is on a slow connection or on a data plan. + +This plugin builds heavily on the amazing work done by [Google Chrome Labs](https://github.com/GoogleChromeLabs/quicklink). + +More information about [Quicklink on the official Website](https://getquick.link). + += How it works = + +* **Detects links within the viewport** (using [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)) +* **Waits until the browser is idle** (using [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)) +* **Checks if the user isn't on a slow connection** (using `navigator.connection.effectiveType`) or has data-saver enabled (using `navigator.connection.saveData`) +* **Prefetches URLs to the links** (using [``](https://www.w3.org/TR/resource-hints/#prefetch) or XHR). Provides some control over the request priority (can switch to `fetch()` if supported). + +If you are a developer, we encourage you to follow along or [contribute](https://github.com/luehrsenheinrich/wp-quicklink) to the development of this plugin [on GitHub](https://github.com/luehrsenheinrich/wp-quicklink). + +== Installation == + += From within WordPress = + +1. Visit 'Plugins > Add New' +1. Search for 'Quicklink for WordPress' +1. Activate 'Quicklink for WordPress' from your Plugins page. + += Manually = + +1. Upload the `quicklink` folder to the `/wp-content/plugins/` directory +1. Activate the Quicklink for WordPress plugin through the 'Plugins' menu in WordPress + +== Frequently Asked Questions == + += Will this make my website faster? = +Yes and no. This plugin has no impact on the actual performance of your website. But navigating the website will feel faster, because potential navigation targets of the user have been prefetched in the users browser. + += Will this make my website slower? = +Slowing down the site is highly unlikely, but possible. If this plugin is used with a caching plugin, the additional hits on the server should not impact performance. But if resource intensive, uncached targets are being prefetched, a performance loss is to be expected. + += What can I do if my website is getting slower? = +You should fist check, that a good caching plugin like "WP Super Cache", "W3 Total Cache" or "WP Rocket" is enabled. If this is not enough you can always add exception rules to the Quicklink configuration by modifying the 'quicklink_options' filter. + +== Changelog == + += 0.10.0 = +* General maintenance for the repository +* Updated Quicklink dependency to version 2.3 +* Tested for WordPress 6.2 + += 0.9.0 = +* General maintenance for the repository +* Updated Quicklink dependency to version 2.2 +* Tested for WordPress 5.8 + += 0.8.0 = +* Updated Quicklink dependency to version 2.0 + += 0.7.3 = +* Made a function have a less generic name + += 0.7.1 = +* Fix some more deprecations with WooCommerce + += 0.7.0 = +* Changed the defaults to ignore links with get requests to improve compatibility +* Removed some deprecated functions for WooCommerce + += 0.6.0 = +* Updated Quicklink to version 1.0.1 + += 0.5.0 = +* Added rules and compatibility for WooCommerce + += 0.4.0 = +* Updated the script loading to load asynchronously +* Updated the plugin assets + += 0.3.0 = +* Added compatibility with AMP +* Added amazing contributors +* Added the new WordPress filter 'quicklink_options' to modify quicklink settings + += 0.2.0 = +* Release for the plugin repository +* Tuned quicklink ignores for WordPress + += 0.1.0 = +* Initial release