Files
ai.interactive.fiction/public/js/paragraph-layout.js
T

120 lines
3.9 KiB
JavaScript

/**
* 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<boolean>} - 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<number>} 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;