true.
*/
private static function get_non_deferrable_handles( $exclude ) {
static $cache = null;
if ( null !== $cache ) {
return $cache;
}
$wp_scripts = wp_scripts();
$non_deferrable = array();
// First pass: find all explicitly excluded handles.
foreach ( $wp_scripts->registered as $handle => $dep_obj ) {
if ( self::matches_exclusion_pattern( $handle, $exclude ) ) {
$non_deferrable[ $handle ] = true;
}
}
// Second pass: for each excluded handle, mark all its dependencies
// as non-deferrable too (breadth-first).
$to_process = array_keys( $non_deferrable );
while ( ! empty( $to_process ) ) {
$current = array_pop( $to_process );
if ( ! isset( $wp_scripts->registered[ $current ] ) ) {
continue;
}
foreach ( $wp_scripts->registered[ $current ]->deps as $dep ) {
if ( ! isset( $non_deferrable[ $dep ] ) ) {
$non_deferrable[ $dep ] = true;
$to_process[] = $dep;
}
}
}
$cache = $non_deferrable;
return $cache;
}
/**
* Speculative loading configuration.
*
* @param array $config Existing config.
*
* @return array
*/
public static function speculation_rules( $config ) {
if ( ! I365_PO_Plugin::is_frontend_context() ) {
return $config;
}
// Check per-page override.
if ( I365_PO_Meta_Box::is_override_active( 'disable_speculation' ) ) {
return $config;
}
$settings = I365_PO_Plugin::get_settings();
if ( I365_PO_Plugin::is_speculation_excluded( $settings['speculation_exclusions'] ) ) {
return $config;
}
if ( 'auto' !== $settings['speculation_mode'] ) {
$config['mode'] = $settings['speculation_mode'];
}
$config['eagerness'] = $settings['speculation_eagerness'];
if ( ! empty( $settings['speculation_exclusions'] ) ) {
$config['excludes'] = array();
foreach ( $settings['speculation_exclusions'] as $path ) {
$config['excludes'][] = array(
'path' => $path,
);
}
}
return $config;
}
/**
* Output preconnect and preload hints.
*
* @return void
*/
public static function output_preloads() {
if ( ! I365_PO_Plugin::is_frontend_context() ) {
return;
}
$settings = I365_PO_Plugin::get_settings();
foreach ( $settings['preconnect_hosts'] as $host ) {
$is_cross = ( wp_parse_url( $host, PHP_URL_HOST ) !== wp_parse_url( home_url(), PHP_URL_HOST ) );
$cross = $is_cross ? ' crossorigin' : '';
printf( '%3$s', esc_url( $host ), $cross, "\n" );
}
if ( ! empty( $settings['preload_font'] ) ) {
printf(
'%s',
esc_url( $settings['preload_font'] ),
"\n"
);
}
if ( ! empty( $settings['preload_hero'] ) ) {
if ( ! $settings['hero_preload_on_front'] || is_front_page() ) {
printf(
'%s',
esc_url( $settings['preload_hero'] ),
"\n"
);
}
}
}
/**
* Clean REST and oEmbed link tags on the front end.
*
* @return void
*/
public static function clean_head_links() {
if ( is_admin() ) {
return;
}
$settings = I365_PO_Plugin::get_settings();
if ( $settings['remove_rest_link'] ) {
remove_action( 'wp_head', 'rest_output_link_wp_head' );
}
if ( $settings['remove_oembed_link'] ) {
remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
}
}
/**
* Mark LCP images as high priority when applicable.
*
* @param array $attr Attributes.
* @param WP_Post $attachment Attachment.
* @param string|array $size Size.
*
* @return array
*/
public static function lcp_priority( $attr, $attachment, $size ) {
if ( ! I365_PO_Plugin::is_frontend_context() ) {
return $attr;
}
if ( empty( $attr['loading'] ) && empty( $attr['fetchpriority'] ) ) {
$attr['fetchpriority'] = 'high';
}
return $attr;
}
/**
* Disable lazy-loading on front page images for sanity.
*
* @param bool $default Default value.
* @param string $tag Tag name.
*
* @return bool
*/
public static function lazy_loading_sanity( $default, $tag ) {
if ( ! I365_PO_Plugin::is_frontend_context() ) {
return $default;
}
// Check per-page override to force lazy-loading.
if ( I365_PO_Meta_Box::is_override_active( 'force_lazy_load' ) ) {
return $default; // Keep default (lazy-loading enabled).
}
if ( 'img' === $tag && is_front_page() ) {
return false;
}
return $default;
}
/**
* Enqueue stylesheet with preload metadata to satisfy plugin checks.
*
* @return void
*/
public static function enqueue_stylesheet_preload() {
if ( ! I365_PO_Plugin::is_frontend_context() ) {
return;
}
$settings = I365_PO_Plugin::get_settings();
if ( empty( $settings['preload_stylesheet'] ) ) {
return;
}
$url = esc_url_raw( $settings['preload_stylesheet'] );
$handle = 'i365-po-preload-style';
wp_enqueue_style( $handle, $url, array(), I365_PO_VERSION );
// Inform WordPress this should be preloaded.
wp_style_add_data( $handle, 'preload', true );
}
/*
|--------------------------------------------------------------------------
| JavaScript Delay Until Interaction
|--------------------------------------------------------------------------
*/
/**
* Delay non-critical scripts until user interaction.
*
* @param string $tag Script tag HTML.
* @param string $handle Script handle.
* @param string $src Script URL.
*
* @return string
*/
public static function delay_scripts( $tag, $handle, $src ) {
if ( ! I365_PO_Plugin::is_frontend_context() ) {
return $tag;
}
// Check per-page override.
if ( I365_PO_Meta_Box::is_override_active( 'disable_delay_js' ) ) {
return $tag;
}
$settings = I365_PO_Plugin::get_settings();
$exclude = $settings['delay_js_exclude'];
// Merge per-page excluded scripts.
$page_excluded = I365_PO_Meta_Box::get_excluded_scripts();
if ( ! empty( $page_excluded ) ) {
$exclude = array_merge( $exclude, $page_excluded );
}
// Check if this script should be excluded from delay.
if ( self::is_delay_excluded( $handle, $exclude ) ) {
return $tag;
}
// Don't delay scripts that are already deferred/async or modules.
if ( false !== stripos( $tag, ' async' ) || false !== stripos( $tag, ' type="module"' ) ) {
return $tag;
}
// Don't delay inline scripts (no src).
if ( empty( $src ) ) {
return $tag;
}
// Transform the script tag to delay loading.
// Change src to data-i365-src and add data-i365-delay marker.
$tag = preg_replace(
'/(\n";
}
/*
|--------------------------------------------------------------------------
| Heartbeat API Control
|--------------------------------------------------------------------------
*/
/**
* Control WordPress Heartbeat API behavior.
*
* @return void
*/
public static function control_heartbeat() {
// This method is a placeholder for initialization.
// Actual work is done by deregister_heartbeat and modify_heartbeat_settings.
}
/**
* Deregister the heartbeat script.
*
* @return void
*/
public static function deregister_heartbeat() {
wp_deregister_script( 'heartbeat' );
}
/**
* Modify heartbeat settings to reduce frequency.
*
* @param array $settings Heartbeat settings.
*
* @return array
*/
public static function modify_heartbeat_settings( $settings ) {
$opts = I365_PO_Plugin::get_settings();
$settings['interval'] = absint( $opts['heartbeat_interval'] );
return $settings;
}
/*
|--------------------------------------------------------------------------
| Query String Removal
|--------------------------------------------------------------------------
*/
/**
* Remove version query strings from static resources.
*
* @param string $src Resource URL.
* @param string $handle Resource handle.
*
* @return string
*/
public static function remove_query_strings( $src, $handle ) {
// Never strip version strings from admin assets.
if ( is_admin() ) {
return $src;
}
// Only process if there's a version query string.
if ( strpos( $src, '?ver=' ) !== false || strpos( $src, '&ver=' ) !== false ) {
$src = remove_query_arg( 'ver', $src );
}
return $src;
}
}