Refactored modules and updated loader.
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* Text Processor Module
|
||||
* Handles text formatting and typography enhancements like smart quotes and hyphenation
|
||||
*/
|
||||
import { BaseModule } from './base-module.js';
|
||||
import Hyphenopoly from './hyphenopoly.module.js';
|
||||
|
||||
class TextProcessorModule extends BaseModule {
|
||||
constructor() {
|
||||
super('text-processor', 'Text Processor');
|
||||
this.smartyPants = null;
|
||||
this.smartypantsu = null;
|
||||
this.hyphenator = null;
|
||||
this.hyphenatorReady = false;
|
||||
this.locale = 'en-us';
|
||||
|
||||
// Add localization as a dependency
|
||||
this.dependencies = ['localization'];
|
||||
|
||||
// Bind methods using parent's bindMethods utility
|
||||
this.bindMethods([
|
||||
'loadSmartyPantsScript',
|
||||
'initializeHyphenation',
|
||||
'process',
|
||||
'isHyphenationAvailable',
|
||||
'hyphenate',
|
||||
'setLocale',
|
||||
'handleLocaleChanged'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the module
|
||||
* @returns {Promise<boolean>} - Resolves with success status
|
||||
*/
|
||||
async initialize() {
|
||||
try {
|
||||
this.reportProgress(10, "Initializing text processor");
|
||||
|
||||
// Get locale from Localization module if available
|
||||
const localizationModule = this.getModule('localization');
|
||||
if (!localizationModule) {
|
||||
console.error("Localization module not found, required dependency missing");
|
||||
this.reportProgress(100, "Text processor initialization failed - missing localization");
|
||||
return false;
|
||||
}
|
||||
|
||||
this.locale = localizationModule.getLocale();
|
||||
|
||||
// Register for locale changes using the proper event pattern
|
||||
this.addEventListener(document, 'locale-changed', this.handleLocaleChanged);
|
||||
|
||||
this.reportProgress(30, `Locale set to ${this.locale}`);
|
||||
|
||||
// Ensure global locale is set for SmartyPants
|
||||
window.locale = this.locale;
|
||||
|
||||
// Load SmartyPants - critical dependency
|
||||
this.reportProgress(40, "Loading SmartyPants");
|
||||
try {
|
||||
await this.loadSmartyPantsScript();
|
||||
|
||||
// Verify SmartyPants is properly loaded
|
||||
if (!this.smartyPants || typeof this.smartyPants !== 'function') {
|
||||
throw new Error('SmartyPants not properly loaded');
|
||||
}
|
||||
|
||||
this.reportProgress(70, "SmartyPants loaded successfully");
|
||||
} catch (error) {
|
||||
console.error("Failed to load SmartyPants:", error);
|
||||
this.reportProgress(100, "Text processor initialization failed - SmartyPants not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize hyphenation (non-critical)
|
||||
this.reportProgress(80, "Initializing hyphenation");
|
||||
try {
|
||||
await this.initializeHyphenation();
|
||||
this.reportProgress(100, "Text processor ready");
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.warn("Failed to initialize hyphenation:", error);
|
||||
// Continue without hyphenation, still mark as successful
|
||||
this.reportProgress(100, "Text processor ready (without hyphenation)");
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error initializing text processor:", error);
|
||||
this.reportProgress(100, "Text processor initialization failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle locale changed event
|
||||
* @param {CustomEvent} event - The locale-changed event
|
||||
*/
|
||||
handleLocaleChanged(event) {
|
||||
if (event && event.detail && event.detail.locale) {
|
||||
this.setLocale(event.detail.locale);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the locale for the text processor
|
||||
* @param {string} locale - The locale to set
|
||||
*/
|
||||
setLocale(locale) {
|
||||
this.locale = locale;
|
||||
console.log(`Text processor locale set to ${locale}`);
|
||||
|
||||
// Reinitialize hyphenation with new locale if needed
|
||||
if (this.hyphenatorReady) {
|
||||
this.initializeHyphenation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the SmartyPants script dynamically and wait for it to be ready
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
loadSmartyPantsScript() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Check if already loaded globally
|
||||
if (typeof window.SmartyPants === 'object' && typeof window.SmartyPants.smartypants === 'function') {
|
||||
this.smartyPants = window.SmartyPants.smartypants;
|
||||
this.smartypantsu = window.SmartyPants.smartypantsu;
|
||||
console.log("SmartyPants already loaded globally");
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create script element
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = '/js/smartypants.js'; // Use relative URL
|
||||
script.async = true;
|
||||
|
||||
// Set up load and error handlers
|
||||
script.onload = () => {
|
||||
if (typeof window.SmartyPants === 'object' && typeof window.SmartyPants.smartypants === 'function') {
|
||||
this.smartyPants = window.SmartyPants.smartypants;
|
||||
this.smartypantsu = window.SmartyPants.smartypantsu;
|
||||
console.log("SmartyPants loaded successfully");
|
||||
resolve();
|
||||
} else {
|
||||
const error = new Error('SmartyPants loaded but functions not found');
|
||||
console.error(error);
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
script.onerror = (error) => {
|
||||
console.error("Error loading SmartyPants script:", error);
|
||||
reject(new Error('Failed to load SmartyPants script'));
|
||||
};
|
||||
|
||||
// Add script to document
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize hyphenation using Hyphenopoly module
|
||||
* @returns {Promise<boolean>} - Resolves when hyphenation is initialized
|
||||
*/
|
||||
initializeHyphenation() {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
console.log("Initializing hyphenation with Hyphenopoly module");
|
||||
|
||||
// Configure Hyphenopoly with our requirements
|
||||
const hyphenatorPromise = Hyphenopoly.config({
|
||||
require: [this.locale],
|
||||
hyphen: '\u00AD', // Soft hyphen character
|
||||
minWordLength: 5,
|
||||
leftmin: 2,
|
||||
rightmin: 2,
|
||||
compound: "hyphen",
|
||||
// Define a custom loader for the patterns
|
||||
loader: (file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Determine correct pattern file based on locale
|
||||
let patternFile = file;
|
||||
|
||||
// Special handling for 'en' locale - use en-us.wasm if available
|
||||
if (file === 'en.wasm') {
|
||||
patternFile = 'en-us.wasm';
|
||||
}
|
||||
|
||||
const patternPath = `/js/patterns/${patternFile}`;
|
||||
console.log(`Loading hyphenation pattern: ${patternPath}`);
|
||||
|
||||
fetch(patternPath)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load ${file}: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
return response.arrayBuffer();
|
||||
})
|
||||
.then(arrayBuffer => {
|
||||
resolve(arrayBuffer);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Error loading hyphenation pattern ${file}:`, error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
handleEvent: {
|
||||
error: (e) => {
|
||||
console.warn(`Hyphenopoly error: ${e.msg}`);
|
||||
},
|
||||
engineReady: (e) => {
|
||||
console.log(`Hyphenopoly engine ready for ${e.msg}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Get the hyphenator for our locale
|
||||
hyphenatorPromise.get(this.locale)
|
||||
.then(hyphenator => {
|
||||
this.hyphenator = hyphenator;
|
||||
this.hyphenatorReady = true;
|
||||
console.log(`Hyphenator ready for ${this.locale}`);
|
||||
|
||||
// Dispatch event that hyphenation is ready
|
||||
document.dispatchEvent(new CustomEvent('hyphenation-loaded'));
|
||||
resolve(true); // Successfully initialized
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(`Failed to initialize hyphenator for ${this.locale}:`, error);
|
||||
// Try to fall back to en-us if the current locale failed
|
||||
if (this.locale !== 'en-us') {
|
||||
console.log("Falling back to en-us hyphenation");
|
||||
return hyphenatorPromise.get('en-us');
|
||||
}
|
||||
throw error;
|
||||
})
|
||||
.then(fallbackHyphenator => {
|
||||
if (fallbackHyphenator) {
|
||||
this.hyphenator = fallbackHyphenator;
|
||||
this.hyphenatorReady = true;
|
||||
console.log("Using fallback en-us hyphenator");
|
||||
|
||||
// Dispatch event that hyphenation is ready
|
||||
document.dispatchEvent(new CustomEvent('hyphenation-loaded'));
|
||||
resolve(true); // Successfully initialized with fallback
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Failed to initialize hyphenation even with fallback:", error);
|
||||
reject(error); // Failed to initialize
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error setting up hyphenation:", error);
|
||||
reject(error); // Failed to initialize
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if hyphenation is available
|
||||
* @returns {boolean} - True if hyphenation is available
|
||||
*/
|
||||
isHyphenationAvailable() {
|
||||
return this.hyphenatorReady && this.hyphenator !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hyphenate a text using the Hyphenopoly module
|
||||
* @param {string} text - The text to hyphenate
|
||||
* @returns {string} - The hyphenated text
|
||||
*/
|
||||
hyphenate(text) {
|
||||
if (!this.isHyphenationAvailable()) {
|
||||
return text;
|
||||
}
|
||||
|
||||
try {
|
||||
return this.hyphenator(text);
|
||||
} catch (error) {
|
||||
console.error("Error hyphenating text:", error);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process text with typography enhancements
|
||||
* @param {string} text - The text to process
|
||||
* @param {Object} options - Processing options
|
||||
* @param {boolean} [options.smartypants=true] - Whether to apply SmartyPants processing
|
||||
* @param {boolean} [options.hyphenate=true] - Whether to apply hyphenation
|
||||
* @returns {string} - The processed text
|
||||
*/
|
||||
process(text, options = {}) {
|
||||
const opts = {
|
||||
smartypants: true,
|
||||
hyphenate: true,
|
||||
...options
|
||||
};
|
||||
|
||||
let result = text;
|
||||
|
||||
// Apply SmartyPants if available and requested
|
||||
if (opts.smartypants && this.smartyPants) {
|
||||
result = this.smartyPants(result);
|
||||
}
|
||||
|
||||
// Apply hyphenation if available and requested
|
||||
if (opts.hyphenate && this.isHyphenationAvailable()) {
|
||||
result = this.hyphenate(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the singleton instance
|
||||
const TextProcessor = new TextProcessorModule();
|
||||
|
||||
// Export the module
|
||||
export { TextProcessor };
|
||||
Reference in New Issue
Block a user