import Module from '../__module'; import { CriticalError } from '../errors/critical'; /** * @module ReadOnly * * Has one important method: * - {Function} toggleReadonly - Set read-only mode or toggle current state * @version 1.0.0 * @typedef {ReadOnly} ReadOnly * @property {boolean} readOnlyEnabled - read-only state */ export default class ReadOnly extends Module { /** * Array of tools name which don't support read-only mode */ private toolsDontSupportReadOnly: string[] = []; /** * Value to track read-only state * * @type {boolean} */ private readOnlyEnabled = false; /** * Returns state of read only mode */ public get isEnabled(): boolean { return this.readOnlyEnabled; } /** * Set initial state */ public async prepare(): Promise { const { Tools } = this.Editor; const { blockTools } = Tools; const toolsDontSupportReadOnly: string[] = []; Array .from(blockTools.entries()) .forEach(([name, tool]) => { if (!tool.isReadOnlySupported) { toolsDontSupportReadOnly.push(name); } }); this.toolsDontSupportReadOnly = toolsDontSupportReadOnly; if (this.config.readOnly === true && toolsDontSupportReadOnly.length > 0) { this.throwCriticalError(); } this.toggle(this.config.readOnly, true); } /** * Set read-only mode or toggle current state * Call all Modules `toggleReadOnly` method and re-render Editor * * @param state - (optional) read-only state or toggle * @param isInitial - (optional) true when editor is initializing */ public async toggle(state = !this.readOnlyEnabled, isInitial = false): Promise { if (state && this.toolsDontSupportReadOnly.length > 0) { this.throwCriticalError(); } const oldState = this.readOnlyEnabled; this.readOnlyEnabled = state; for (const module of Object.values(this.Editor)) { /** * Verify module has method `toggleReadOnly` method */ if (module === null || module === undefined) { continue; } if (typeof (module as { toggleReadOnly?: unknown }).toggleReadOnly !== 'function') { continue; } /** * set or toggle read-only state */ (module as { toggleReadOnly: (state: boolean) => void }).toggleReadOnly(state); } /** * If new state equals old one, do not re-render blocks */ if (oldState === state) { return this.readOnlyEnabled; } /** * Do not re-render blocks if it's initial call */ if (isInitial) { return this.readOnlyEnabled; } /** * Mutex for modifications observer to prevent onChange call when read-only mode is enabled */ this.Editor.ModificationsObserver.disable(); /** * Save current Editor Blocks and render again */ const savedBlocks = await this.Editor.Saver.save(); await this.Editor.BlockManager.clear(); await this.Editor.Renderer.render(savedBlocks.blocks); this.Editor.ModificationsObserver.enable(); return this.readOnlyEnabled; } /** * Throws an error about tools which don't support read-only mode */ private throwCriticalError(): never { throw new CriticalError( `To enable read-only mode all connected tools should support it. Tools ${this.toolsDontSupportReadOnly.join(', ')} don't support read-only mode.` ); } }