/** * ParagraphLayout Module * Interfaces with the Knuth-Plass line breaking algorithm to calculate optimal line breaks. */ import { BaseModule } from './base-module.js'; import { moduleRegistry } from './module-registry.js'; class ParagraphLayoutModule extends BaseModule { /** * Create a new ParagraphLayout */ constructor() { super('paragraph-layout', 'Paragraph Layout'); this.kapAlgorithm = null; this.measureText = null; } /** * Load module dependencies * @returns {Promise} - Resolves when dependencies are loaded */ async loadDependencies() { try { // First load linebreak.js if needed if (!window.linebreak) { await this.loadScript('/js/linebreak.js'); this.reportProgress(40, "Linebreak algorithm loaded"); } // Then load knuth-and-plass.js if needed if (!window.kap) { await this.loadScript('/js/knuth-and-plass.js'); this.reportProgress(60, "KAP algorithm loaded"); } this.kapAlgorithm = window.kap; return true; } catch (error) { console.error("Error loading paragraph layout dependencies:", error); return false; } } /** * Load a script dynamically * @param {string} src - Script source URL * @returns {Promise} - Resolves when script is loaded */ loadScript(src) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.onload = () => resolve(); script.onerror = () => reject(new Error(`Failed to load script: ${src}`)); document.head.appendChild(script); }); } /** * Initialize the module * @returns {Promise} - Resolves with success status */ async initialize() { try { // The measureText function will be provided by the game controller later this.reportProgress(100, "Paragraph layout initialized"); return true; } catch (error) { console.error("Error initializing paragraph layout:", error); return false; } } /** * Calculate layout for a paragraph * @param {string} processedText - The pre-processed text (with SmartyPants and hyphenation) * @param {Array} measures - Array of line width measurements * @param {boolean} hyphenate - Whether to enable hyphenation * @param {Function} [measureFunc] - Optional specific measurement function for this call * @returns {Object} Layout data with nodes and breaks */ calculateLayout(processedText, measures, hyphenate = true, measureFunc = null) { const measure = measureFunc || this.measureText; // Use provided func or fallback to instance default if (typeof measure !== 'function') { throw new Error('No text measurement function available'); } return this.kapAlgorithm(processedText, measure, measures, hyphenate); } /** * Set a new text measurement function * @param {Function} measureFunc - The new measurement function */ setMeasureFunction(measureFunc) { this.measureText = measureFunc; } /** * Set a new Knuth and Plass algorithm implementation * @param {Function} kapFunc - The new KAP algorithm function */ setKapAlgorithm(kapFunc) { this.kapAlgorithm = kapFunc; } } // Create the singleton instance const ParagraphLayout = new ParagraphLayoutModule(); // Register with the module registry moduleRegistry.register(ParagraphLayout); // Export the module export { ParagraphLayout }; // Keep a reference in window for loader system window.ParagraphLayout = ParagraphLayout;