/** * Descomplicar® Crescimento Digital * https://descomplicar.pt */ admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('care_booking_admin'), 'strings' => [ 'error' => __('An error occurred. Please try again.', 'care-booking-block'), 'success_update' => __('Updated successfully.', 'care-booking-block'), 'success_bulk' => __('Bulk operation completed.', 'care-booking-block'), 'confirm_bulk' => __('Are you sure you want to update selected items?', 'care-booking-block') ] ]; } /** * Add async/defer attributes to scripts for better performance * * @param string $tag Script tag * @param string $handle Script handle * @param string $src Script source * @return string Modified script tag */ public static function add_script_attributes($tag, $handle, $src) { // Add async to non-critical frontend scripts if ($handle === 'care-booking-frontend' && !is_admin()) { // Only add async if jQuery is already loaded or loading if (wp_script_is('jquery', 'done') || wp_script_is('jquery', 'to_do')) { $tag = str_replace(' src', ' async src', $tag); } } return $tag; } /** * Preload critical assets for better performance */ public static function preload_critical_assets() { if (!self::should_load_frontend_assets()) { return; } $version = self::get_asset_version(); $min_suffix = self::get_min_suffix(); // Preload critical CSS $css_url = CARE_BOOKING_BLOCK_PLUGIN_URL . "public/css/frontend{$min_suffix}.css?ver={$version}"; echo "\n"; // Fallback for browsers that don't support preload echo "\n"; } /** * Optimize script source URLs * * @param string $src Script source * @param string $handle Script handle * @return string Optimized source */ public static function optimize_script_src($src, $handle) { // Add cache busting and CDN optimization for Care Booking scripts if (strpos($handle, 'care-booking') === 0) { // Add integrity checking for security if (!is_admin() && defined('CARE_BOOKING_ENABLE_SRI') && CARE_BOOKING_ENABLE_SRI) { add_filter('script_loader_tag', function($tag, $h, $s) use ($handle, $src) { if ($h === $handle) { $integrity = self::get_file_integrity($src); if ($integrity) { $tag = str_replace('>', " integrity='{$integrity}' crossorigin='anonymous'>", $tag); } } return $tag; }, 10, 3); } } return $src; } /** * Optimize style source URLs * * @param string $src Style source * @param string $handle Style handle * @return string Optimized source */ public static function optimize_style_src($src, $handle) { // Add performance optimizations for Care Booking styles if (strpos($handle, 'care-booking') === 0) { // Ensure proper media attribute for optimal loading add_filter('style_loader_tag', function($html, $h, $href, $media) use ($handle) { if ($h === $handle && $media === 'all') { // Add performance attributes $html = str_replace("media='all'", "media='all' data-optimized='true'", $html); } return $html; }, 10, 4); } return $src; } /** * Get asset version with intelligent cache busting * * @return string Asset version */ private static function get_asset_version() { $versions = get_transient(self::ASSET_VERSION_KEY); if ($versions === false) { $versions = self::generate_asset_versions(); set_transient(self::ASSET_VERSION_KEY, $versions, self::ASSET_CACHE_DURATION); } return $versions['global'] ?? CARE_BOOKING_BLOCK_VERSION; } /** * Generate asset versions based on file modification times * * @return array Asset versions */ private static function generate_asset_versions() { $versions = ['global' => CARE_BOOKING_BLOCK_VERSION]; $asset_files = [ 'frontend_css' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/css/frontend.css', 'frontend_js' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'public/js/frontend.js', 'admin_css' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/css/admin-style.css', 'admin_js' => CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/js/admin-script.js' ]; foreach ($asset_files as $key => $file) { if (file_exists($file)) { $versions[$key] = filemtime($file); } } // Generate global version from all file versions $versions['global'] = md5(serialize($versions)); return $versions; } /** * Get minification suffix based on environment * * @return string Empty string or '.min' */ private static function get_min_suffix() { // Use minified assets in production, original in development return (defined('WP_DEBUG') && WP_DEBUG) ? '' : '.min'; } /** * Check if frontend assets should be loaded * * @return bool True if should load */ private static function should_load_frontend_assets() { global $post; // Load on pages with KiviCare content if ($post && ( has_shortcode($post->post_content, 'kivicare') || has_block('kivicare/booking', $post->post_content) )) { return true; } // Load on specific templates $template = get_page_template_slug(); if (in_array($template, ['page-booking.php', 'page-appointment.php'])) { return true; } return false; } /** * Check if current admin page is Care Booking related * * @param string $hook Admin page hook * @return bool True if Care Booking admin page */ private static function is_care_booking_admin_page($hook) { $care_booking_pages = [ 'tools_page_care-booking-control', 'admin_page_care-booking-settings' ]; return in_array($hook, $care_booking_pages); } /** * Get file integrity hash for Subresource Integrity * * @param string $file_url File URL * @return string|null Integrity hash */ private static function get_file_integrity($file_url) { // Convert URL to file path $file_path = str_replace( CARE_BOOKING_BLOCK_PLUGIN_URL, CARE_BOOKING_BLOCK_PLUGIN_DIR, $file_url ); if (file_exists($file_path)) { $hash = hash('sha384', file_get_contents($file_path), true); return 'sha384-' . base64_encode($hash); } return null; } /** * Output combined assets for maximum performance */ public static function output_combined_assets() { // Only combine assets if not in debug mode if (defined('WP_DEBUG') && WP_DEBUG) { return; } // This would combine multiple CSS/JS files into single requests // For now, we rely on the individual optimizations above self::output_performance_markers(); } /** * Output performance markers for monitoring */ private static function output_performance_markers() { if (defined('WP_DEBUG') && WP_DEBUG) { echo "\n\n"; $memory = memory_get_usage(); $peak_memory = memory_get_peak_usage(); echo "\n"; } } /** * Generate minified CSS from source files * * @param string $source_file Source CSS file * @param string $output_file Output minified file * @return bool Success status */ public static function generate_minified_css($source_file, $output_file) { if (!file_exists($source_file)) { return false; } $css = file_get_contents($source_file); $minified_css = self::minify_css($css); return file_put_contents($output_file, $minified_css) !== false; } /** * Generate minified JavaScript from source files * * @param string $source_file Source JS file * @param string $output_file Output minified file * @return bool Success status */ public static function generate_minified_js($source_file, $output_file) { if (!file_exists($source_file)) { return false; } $js = file_get_contents($source_file); $minified_js = self::minify_js($js); return file_put_contents($output_file, $minified_js) !== false; } /** * Minify CSS content * * @param string $css CSS content * @return string Minified CSS */ public static function minify_css($css) { // Remove comments $css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css); // Remove whitespace $css = str_replace(["\r\n", "\r", "\n", "\t"], '', $css); // Remove extra spaces $css = preg_replace('/\s+/', ' ', $css); // Remove spaces around specific characters $css = str_replace(['; ', ' {', '{ ', ' }', '} ', ': ', ', ', ' ,'], [';', '{', '{', '}', '}', ':', ',', ','], $css); // Remove trailing semicolon before } $css = str_replace(';}', '}', $css); return trim($css); } /** * Basic JavaScript minification * * @param string $js JavaScript content * @return string Minified JavaScript */ public static function minify_js($js) { // Basic minification - remove comments and extra whitespace // Note: For production, consider using a proper JS minifier // Remove single-line comments (but preserve URLs) $js = preg_replace('#(? 'admin/css/admin-style.min.css', 'admin-script.js' => 'admin/js/admin-script.min.js', 'frontend.css' => 'public/css/frontend.min.css', 'frontend.js' => 'public/js/frontend.min.js' ]; $results = []; foreach ($assets as $source => $target) { $source_path = CARE_BOOKING_BLOCK_PLUGIN_DIR . str_replace('.min', '', $target); $target_path = CARE_BOOKING_BLOCK_PLUGIN_DIR . $target; $extension = pathinfo($source, PATHINFO_EXTENSION); if ($extension === 'css') { $results[$source] = self::generate_minified_css($source_path, $target_path); } elseif ($extension === 'js') { $results[$source] = self::generate_minified_js($source_path, $target_path); } } return $results; } } // Initialize asset optimizer Care_Booking_Asset_Optimizer::init();