should_enqueue_assets() ) { return; } $this->enqueue_frontend_styles(); $this->enqueue_frontend_scripts(); $this->configure_frontend_scripts(); $this->add_frontend_localization(); } /** * Determines if frontend assets should be enqueued. * * @return bool */ private function should_enqueue_assets() { if ( ! is_singular() ) { return false; } $post_id = get_the_ID(); if ( ! $post_id ) { return false; } // Check if post has generated audio (either by URL or generated flag). $audio_url = get_post_meta( $post_id, 'accessibility_audio_tts_url', true ); $audio_generated = get_post_meta( $post_id, 'accessibility_audio_tts_generated', true ); return ! empty( $audio_url ) || ! empty( $audio_generated ); } /** * Enqueues frontend CSS styles. */ private function enqueue_frontend_styles() { wp_enqueue_style( 'accessibility-audio-tts-audio-player', ACCESSIBILITY_AUDIO_TTS_ASSETS_URL . 'public/player.min.css', array(), accessibility_audio_tts_get_asset_version( 'assets/public/player.min.css' ) ); } /** * Enqueues frontend JavaScript modules. */ private function enqueue_frontend_scripts() { // Enqueue Service Worker manager first. wp_enqueue_script( 'accessibility-audio-tts-service-worker-manager', ACCESSIBILITY_AUDIO_TTS_ASSETS_URL . 'public/core/service-worker-manager.min.js', array(), accessibility_audio_tts_get_asset_version( 'assets/public/core/service-worker-manager.min.js' ), true ); wp_enqueue_script( 'accessibility-audio-tts-audio-player', ACCESSIBILITY_AUDIO_TTS_ASSETS_URL . 'public/player/player-core.min.js', array( 'jquery', 'accessibility-audio-tts-service-worker-manager' ), accessibility_audio_tts_get_asset_version( 'assets/public/player/player-core.min.js' ), true ); // Anonymous measurement is server-side only: no browser telemetry or consent bridge. if ( $this->can_track_public_post() ) { wp_enqueue_script( 'accessibility-audio-tts-listening-tracker', ACCESSIBILITY_AUDIO_TTS_ASSETS_URL . 'public/core/listening-tracker.min.js', array(), accessibility_audio_tts_get_asset_version( 'assets/public/core/listening-tracker.min.js' ), true ); } } /** Only public, unprotected articles are eligible for browser measurement or Google events. */ private function can_track_public_post() { if ( 'consented' !== \Accessibility_Audio_TTS\Core\Privacy_Settings::get( 'analytics_mode' ) || is_admin() || is_preview() ) { return false; } $post = get_post( get_the_ID() ); return $post && 'publish' === $post->post_status && '' === (string) $post->post_password && is_post_type_viewable( $post->post_type ); } /** * Configures frontend script attributes. */ private function configure_frontend_scripts() { add_filter( 'script_loader_tag', function ( $tag, $handle ) { if ( in_array( $handle, array( 'accessibility-audio-tts-audio-player', 'accessibility-audio-tts-service-worker-manager' ), true ) ) { return str_replace( ' src', ' type="module" src', $tag ); } return $tag; }, 10, 2 ); } /** * Adds localization data for frontend player. */ private function add_frontend_localization() { wp_localize_script( 'accessibility-audio-tts-audio-player', 'accessibilityAudioTtsPlayerL10n', array( 'play' => __( 'Play', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'pause' => __( 'Pause', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'mute' => __( 'Mute', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'unmute' => __( 'Unmute', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'loading' => __( 'Loading...', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'cached' => __( 'Available offline', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'offline' => __( 'Offline mode', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'offline_unavailable' => __( 'Audio unavailable offline', 'accessibility-audio-tts-text-to-speech-for-articles' ), ) ); // Localize script with AJAX data. wp_localize_script( 'accessibility-audio-tts-audio-player', 'accessibility_audio_tts_frontend', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'accessibility_audio_tts_frontend_nonce' ), ) ); wp_localize_script( 'accessibility-audio-tts-audio-player', 'accessibilityAudioTtsPlayback', array( 'i18n' => $this->get_frontend_translations() ) ); if ( $this->can_track_public_post() ) { wp_localize_script( 'accessibility-audio-tts-listening-tracker', 'accessibilityAudioTtsPublicAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'listening_nonce' => wp_create_nonce( 'accessibility_audio_tts_listening_nonce' ), 'tracking_enabled' => true, 'analytics_mode' => 'consented', 'public_post_ids' => array( get_the_ID() ), 'ga4_enabled' => (bool) \Accessibility_Audio_TTS\Core\Privacy_Settings::get( 'ga4_enabled' ), 'ga4_transport' => \Accessibility_Audio_TTS\Core\Privacy_Settings::get( 'ga4_transport' ), 'ga4_measurement_id' => \Accessibility_Audio_TTS\Core\Privacy_Settings::get( 'ga4_measurement_id' ), // Override automatic page metadata: article URLs may contain personal query values. 'ga4_page_location' => home_url( '/' ), 'post_id' => get_the_ID(), 'i18n' => $this->get_frontend_translations(), ) ); } // Add Service Worker configuration. wp_localize_script( 'accessibility-audio-tts-service-worker-manager', 'accessibilityAudioTtsServiceWorker', array( 'swPath' => ACCESSIBILITY_AUDIO_TTS_ASSETS_URL . 'public/core/aat-service-worker.js', 'pluginUrl' => ACCESSIBILITY_AUDIO_TTS_PLUGIN_URL, 'enabled' => true, ) ); } /** * Gets frontend translations for JavaScript. * * @return array */ private function get_frontend_translations() { return array( // Audio player error messages. 'audio_loading_failed' => __( 'Audio loading failed', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'audio_format_not_supported' => __( 'Audio format not supported', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'audio_loading_aborted' => __( 'Audio loading was aborted', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'network_error_loading_audio' => __( 'Network error while loading audio', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'audio_decoding_failed' => __( 'Audio decoding failed', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'loading_stalled' => __( 'Loading stalled', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'loading_timeout' => __( 'Loading timeout', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'manual_retry' => __( 'Manual retry', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'playback_failed' => __( 'Playback failed', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'playback_aborted' => __( 'Playback aborted', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'playback_blocked_by_browser' => __( 'Playback blocked by browser (user needs to interact first)', 'accessibility-audio-tts-text-to-speech-for-articles' ), 'audio_source_not_found' => __( 'Audio source not found', 'accessibility-audio-tts-text-to-speech-for-articles' ), // Listening tracker messages. 'failed_to_get_post_stats' => __( 'Failed to get post stats', 'accessibility-audio-tts-text-to-speech-for-articles' ), ); } }