array( 'moderate' => __( 'Prerender on hover (moderate)', 'ai-eshop-optimizer' ), 'conservative' => __( 'Prerender on pointer down (conservative)', 'ai-eshop-optimizer' ), 'eager' => __( 'Prerender eagerly', 'ai-eshop-optimizer' ), 'off' => __( 'Off', 'ai-eshop-optimizer' ), ), 'third_party' => array( 'delay' => __( 'Load after the first human interaction and consent', 'ai-eshop-optimizer' ), 'off' => __( 'Load as the plugins enqueue them', 'ai-eshop-optimizer' ), ), 'cart_mode' => array( 'blocks' => __( 'WooCommerce Cart block: shows after its scripts ran', 'ai-eshop-optimizer' ), 'classic' => __( 'Classic cart, rendered on the server: shows at once (fastest)', 'ai-eshop-optimizer' ), ), 'checkout_mode' => array( 'blocks' => __( 'WooCommerce Checkout block (some payment methods support only this one)', 'ai-eshop-optimizer' ), 'classic' => __( 'Classic checkout, rendered on the server: check that every payment method supports it', 'ai-eshop-optimizer' ), ), ); } private static function switches() { return array( 'device_variants' => __( 'One DOM per device: serve the -mobile template parts to phones (requires a cache that varies on Sec-CH-UA-Mobile or a separate mobile cache)', 'ai-eshop-optimizer' ), 'defer' => __( 'Deferred layer: regions marked "Load later" leave the first HTML and arrive after the first human interaction', 'ai-eshop-optimizer' ), 'inline_critical_css' => __( 'Inline small stylesheets: the base sheet when it is under 8 KB gzipped, and the local block and plugin sheets a page needs, up to 160 KB in total, so the first paint waits for no stylesheet', 'ai-eshop-optimizer' ), 'used_css' => __( 'Used CSS per page: visitors get only the style rules each page uses, inline; the full stylesheets follow at the first touch, scroll, key or pointer move (worked out on this site, kept in the uploads folder; for visitors who are not logged in)', 'ai-eshop-optimizer' ), 'defer_scripts' => __( 'Defer classic scripts: every script a plugin puts in the head loads with defer (WordPress keeps the ones that cannot be deferred blocking), and script modules load at low priority', 'ai-eshop-optimizer' ), 'delay_scripts' => __( 'Load plugin scripts after the first interaction: classic scripts wait for the first touch, scroll, key or pointer move and then run in order (not on the checkout, nor scripts that render the page with React)', 'ai-eshop-optimizer' ), 'delay_modules' => __( 'Load interactive blocks after the page: script modules (menus, galleries, add to cart, filters) load once the page has loaded or at the first interaction, which they then answer (not on the checkout)', 'ai-eshop-optimizer' ), 'trim_products_state' => __( 'Trim the products data of listings: on a shop, category or home page, the product data WooCommerce prints for its blocks keeps only what the add to cart buttons read (the full data comes with the rest of the page after the first interaction; product pages keep it whole)', 'ai-eshop-optimizer' ), ); } public function ajax_save() { AIEO_DMM::verify_ajax_request(); if ( ! class_exists( 'AIEO_Engine_Settings' ) ) { wp_send_json_error( array( 'message' => __( 'The performance engine runs under the Rooster Zero theme only.', 'ai-eshop-optimizer' ) ) ); } $before = AIEO_Engine_Settings::all(); $values = array(); foreach ( array_keys( self::switches() ) as $key ) { $values[ $key ] = ! empty( $_POST[ $key ] ); } foreach ( self::choices() as $key => $options ) { $v = isset( $_POST[ $key ] ) ? sanitize_key( wp_unslash( $_POST[ $key ] ) ) : ''; $values[ $key ] = isset( $options[ $v ] ) ? $v : $before[ $key ]; } $allow = isset( $_POST['third_party_allow'] ) ? (string) wp_unslash( $_POST['third_party_allow'] ) : ''; $values['third_party_allow'] = array_values( array_unique( array_filter( array_map( static function ( $line ) { return (string) preg_replace( '/[^A-Za-z0-9._\/-]/', '', trim( $line ) ); }, preg_split( '/\r\n|\r|\n|,/', $allow ) ) ) ) ); AIEO_Engine_Settings::save( $values ); if ( is_callable( array( '\Rooster\Core\UsedCss', 'clear' ) ) ) { \Rooster\Core\UsedCss::clear(); } do_action( 'aieo_purge_page_cache' ); wp_send_json_success( array( 'message' => __( 'Performance settings saved.', 'ai-eshop-optimizer' ) ) ); } public function render_subtab() { if ( ! class_exists( 'AIEO_Engine_Settings' ) ) { list( $open, $close ) = AIEO_DMM_UI::card( array( 'icon' => 'gauge', 'title' => $this->get_label() ) ); echo $open; echo \Rooster\Admin\Ui::notice( esc_html__( 'The performance engine runs under the Rooster Zero theme only. With another theme, its caching and optimisation plugins do this work.', 'ai-eshop-optimizer' ), 'info', array( 'inline' => true ) ); echo $close; return; } $s = AIEO_Engine_Settings::all(); $row = static function ( $name, $label, $control, $help = '' ) { return \Rooster\Admin\Ui::field_row( array( 'id' => 'aieo-perf-' . str_replace( '_', '-', $name ), 'label' => $label, 'control' => $control, 'help' => $help ) ); }; $switch = static function ( $name, $text ) use ( $s ) { return \Rooster\Admin\Ui::toggle( $name, ! empty( $s[ $name ] ), array( 'id' => 'aieo-perf-' . str_replace( '_', '-', $name ), 'hidden' => false, 'label' => $text ) ); }; $choices = self::choices(); $select = static function ( $name ) use ( $s, $choices ) { return \Rooster\Admin\Ui::select( $name, $choices[ $name ], (string) $s[ $name ], array( 'id' => 'aieo-perf-' . str_replace( '_', '-', $name ) ) ); }; $switches = self::switches(); $card = static function ( $anchor, $icon, $title, $col, $fields, $extra = array() ) { $form_id = 'aieo-card-' . $anchor . '-form'; list( $open, $close ) = AIEO_DMM_UI::card( array( 'anchor' => $anchor, 'icon' => $icon, 'title' => $title, 'col' => $col, 'footer' => class_exists( '\Rooster\Admin\States' ) ? \Rooster\Admin\States::save_button( $form_id ) : '', ) + $extra ); echo $open; echo AIEO_DMM_UI::card_form( 'aieo_dmm_performance_save', 'performance', $form_id, '
' . $fields . '
' ); echo $close; }; rooster_admin_band_open( array( 'cols' => 2 ) ); $fields = $row( 'device_variants', __( 'Device variants', 'ai-eshop-optimizer' ), $switch( 'device_variants', $switches['device_variants'] ) ); $fields .= $row( 'defer', __( 'Deferred layer', 'ai-eshop-optimizer' ), $switch( 'defer', $switches['defer'] ) ); $fields .= $row( 'speculation', __( 'Speculative loading of the next page', 'ai-eshop-optimizer' ), $select( 'speculation' ), __( 'The browser starts loading the page a visitor is about to open, so it appears at once; eagerly uses the most data.', 'ai-eshop-optimizer' ) ); $fields .= $row( 'cart_mode', __( 'Cart page', 'ai-eshop-optimizer' ), $select( 'cart_mode' ), __( 'The two carts are laid out differently: look at the cart page after a change.', 'ai-eshop-optimizer' ) ); $fields .= $row( 'checkout_mode', __( 'Checkout page', 'ai-eshop-optimizer' ), $select( 'checkout_mode' ), __( 'After a change, place a test order with each payment method you offer.', 'ai-eshop-optimizer' ) ); $card( 'performance-pages', 'layout-template', __( 'Pages', 'ai-eshop-optimizer' ), 1, $fields, array( 'description' => __( 'The Rooster Zero theme\'s performance engine, carried by Rooster One. Every save works the used CSS out again and purges the page caches.', 'ai-eshop-optimizer' ) ) ); $fields = $row( 'inline_critical_css', __( 'Inline small stylesheets', 'ai-eshop-optimizer' ), $switch( 'inline_critical_css', $switches['inline_critical_css'] ) ); $fields .= $row( 'used_css', __( 'Used CSS per page', 'ai-eshop-optimizer' ), $switch( 'used_css', $switches['used_css'] ) ); $fields .= $row( 'defer_scripts', __( 'Defer classic scripts', 'ai-eshop-optimizer' ), $switch( 'defer_scripts', $switches['defer_scripts'] ) ); $fields .= $row( 'delay_scripts', __( 'Plugin scripts after the first interaction', 'ai-eshop-optimizer' ), $switch( 'delay_scripts', $switches['delay_scripts'] ) ); $fields .= $row( 'delay_modules', __( 'Interactive blocks after the page', 'ai-eshop-optimizer' ), $switch( 'delay_modules', $switches['delay_modules'] ) ); $fields .= $row( 'trim_products_state', __( 'Trim the products data of listings', 'ai-eshop-optimizer' ), $switch( 'trim_products_state', $switches['trim_products_state'] ) ); $card( 'performance-assets', 'file-code', __( 'Stylesheets and scripts', 'ai-eshop-optimizer' ), 2, $fields, array( 'description' => __( 'Choose which styles load inline with the page and which scripts wait for the visitor\'s first interaction, so pages appear sooner.', 'ai-eshop-optimizer' ) ) ); $fields = $row( 'third_party', __( 'Third-party scripts (analytics, pixels, chat, reCAPTCHA)', 'ai-eshop-optimizer' ), $select( 'third_party' ), __( 'Delayed scripts still run, once the visitor first touches, scrolls or types and has given consent.', 'ai-eshop-optimizer' ) ); $fields .= $row( 'third_party_allow', __( 'Allow-list: script handles that may load on first view, one per line', 'ai-eshop-optimizer' ), \Rooster\Admin\Ui::textarea( 'third_party_allow', implode( "\n", (array) $s['third_party_allow'] ), array( 'id' => 'aieo-perf-third-party-allow', 'rows' => 4 ) ), __( 'A handle is the name WordPress knows a script by: its id in the page source, without the -js ending.', 'ai-eshop-optimizer' ) ); $card( 'performance-third-parties', 'shield-check', __( 'Third parties', 'ai-eshop-optimizer' ), 1, $fields, array( 'description' => __( 'Delay analytics, pixels, chat and other outside scripts until the visitor interacts and consents, and name the ones allowed to load at once.', 'ai-eshop-optimizer' ) ) ); rooster_admin_band_close(); } }