/** * TTS Handler Base Class * Abstract base class defining the interface for all TTS handlers */ export class TTSHandler { constructor() { this.voiceOptions = {}; this.isReady = false; // Set up event dispatcher this.eventTarget = document.createElement('div'); } /** * Get handler ID * @returns {string} - Handler identifier */ getId() { throw new Error('getId() must be implemented by subclass'); } /** * Initialize the TTS handler * @param {Function} progressCallback - Optional progress callback * @returns {Promise} - Resolves with success status */ async initialize(progressCallback = null) { throw new Error('initialize() must be implemented by subclass'); } /** * Check if this TTS handler is available * @returns {boolean} - True if handler is ready to use */ isAvailable() { return this.isReady; } /** * Check if voice is currently speaking * @returns {boolean} - True if speaking */ isSpeaking() { return false; // Default implementation } /** * Speak text using this handler * @param {string} text - The text to speak * @param {Function} callback - Optional callback when speech completes */ speak(text, callback = null) { throw new Error('speak() must be implemented by subclass'); } /** * Stop speech */ stop() { throw new Error('stop() must be implemented by subclass'); } /** * Set voice options * @param {Object} options - Voice options */ setVoiceOptions(options = {}) { // Default implementation merges options this.voiceOptions = { ...this.voiceOptions, ...options }; } /** * Get available voices * @returns {Promise} - Resolves with array of voice objects */ async getVoices() { return []; } /** * Dispatch a custom event * @param {string} eventName - Name of the event * @param {Object} detail - Event details */ dispatchEvent(eventName, detail = {}) { const event = new CustomEvent(eventName, { detail: { handlerId: this.getId(), ...detail }, bubbles: true }); this.eventTarget.dispatchEvent(event); } /** * Add event listener * @param {string} eventName - Name of the event * @param {Function} callback - Event handler function */ addEventListener(eventName, callback) { this.eventTarget.addEventListener(eventName, callback); } /** * Remove event listener * @param {string} eventName - Name of the event * @param {Function} callback - Event handler function */ removeEventListener(eventName, callback) { this.eventTarget.removeEventListener(eventName, callback); } /** * Bind methods to this instance * @param {Array} methodNames - Array of method names to bind */ bindMethods(methodNames) { if (!Array.isArray(methodNames)) return; methodNames.forEach(methodName => { if (typeof this[methodName] === 'function') { this[methodName] = this[methodName].bind(this); } }); } }