UNPKG

ckeditor5-image-upload-base64

Version:

The development environment of CKEditor 5 – the best browser-based rich text editor.

245 lines (224 loc) 8.06 kB
/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /** * @module core/command */ import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin'; import mix from '@ckeditor/ckeditor5-utils/src/mix'; /** * The base class for CKEditor commands. * * Commands are the main way to manipulate editor contents and state. They are mostly used by UI elements (or by other * commands) to make changes in the model. Commands are available in every part of code that has access to * the {@link module:core/editor/editor~Editor editor} instance. * * Instances of registered commands can be retrieved from {@link module:core/editor/editor~Editor#commands `editor.commands`}. * The easiest way to execute a command is through {@link module:core/editor/editor~Editor#execute `editor.execute()`}. * * By default commands are disabled when the editor is in {@link module:core/editor/editor~Editor#isReadOnly read-only} mode. * * @mixes module:utils/observablemixin~ObservableMixin */ export default class Command { /** * Creates a new `Command` instance. * * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used. */ constructor( editor ) { /** * The editor on which this command will be used. * * @readonly * @member {module:core/editor/editor~Editor} */ this.editor = editor; /** * The value of the command. A concrete command class should define what it represents for it. * * For example, the `'bold'` command's value indicates whether the selection starts in a bolded text. * And the value of the `'link'` command may be an object with links details. * * It is possible for a command to have no value (e.g. for stateless actions such as `'imageUpload'`). * * A concrete command class should control this value by overriding the {@link #refresh `refresh()`} method. * * @observable * @readonly * @member #value */ this.set( 'value', undefined ); /** * Flag indicating whether a command is enabled or disabled. * A disabled command will do nothing when executed. * * A concrete command class should control this value by overriding the {@link #refresh `refresh()`} method. * * It is possible to disable a command from "outside". For instance, in your integration you may want to disable * a certain set of commands for the time being. To do that, you can use the fact that `isEnabled` is observable * and it fires the `set:isEnabled` event every time anyone tries to modify its value: * * function disableCommand( cmd ) { * cmd.on( 'set:isEnabled', forceDisable, { priority: 'highest' } ); * * cmd.isEnabled = false; * * // Make it possible to enable the command again. * return () => { * cmd.off( 'set:isEnabled', forceDisable ); * cmd.refresh(); * }; * * function forceDisable( evt ) { * evt.return = false; * evt.stop(); * } * } * * // Usage: * * // Disabling the command. * const enableBold = disableCommand( editor.commands.get( 'bold' ) ); * * // Enabling the command again. * enableBold(); * * @observable * @readonly * @member {Boolean} #isEnabled */ this.set( 'isEnabled', false ); /** * Holds identifiers for {@link #forceDisabled} mechanism. * * @type {Set.<String>} * @private */ this._disableStack = new Set(); this.decorate( 'execute' ); // By default every command is refreshed when changes are applied to the model. this.listenTo( this.editor.model.document, 'change', () => { this.refresh(); } ); this.on( 'execute', evt => { if ( !this.isEnabled ) { evt.stop(); } }, { priority: 'high' } ); // By default commands are disabled when the editor is in read-only mode. this.listenTo( editor, 'change:isReadOnly', ( evt, name, value ) => { if ( value ) { this.forceDisabled( 'readOnlyMode' ); } else { this.clearForceDisabled( 'readOnlyMode' ); } } ); } /** * Refreshes the command. The command should update its {@link #isEnabled} and {@link #value} properties * in this method. * * This method is automatically called when * {@link module:engine/model/document~Document#event:change any changes are applied to the document}. */ refresh() { this.isEnabled = true; } /** * Disables the command. * * Command may be disabled by multiple features or algorithms (at once). When disabling a command, unique id should be passed * (e.g. feature name). The same identifier should be used when {@link #clearForceDisabled enabling back} the command. * The command becomes enabled only after all features {@link #clearForceDisabled enabled it back}. * * Disabling and enabling a command: * * command.isEnabled; // -> true * command.forceDisabled( 'MyFeature' ); * command.isEnabled; // -> false * command.clearForceDisabled( 'MyFeature' ); * command.isEnabled; // -> true * * Command disabled by multiple features: * * command.forceDisabled( 'MyFeature' ); * command.forceDisabled( 'OtherFeature' ); * command.clearForceDisabled( 'MyFeature' ); * command.isEnabled; // -> false * command.clearForceDisabled( 'OtherFeature' ); * command.isEnabled; // -> true * * Multiple disabling with the same identifier is redundant: * * command.forceDisabled( 'MyFeature' ); * command.forceDisabled( 'MyFeature' ); * command.clearForceDisabled( 'MyFeature' ); * command.isEnabled; // -> true * * **Note:** some commands or algorithms may have more complex logic when it comes to enabling or disabling certain commands, * so the command might be still disabled after {@link #clearForceDisabled} was used. * * @param {String} id Unique identifier for disabling. Use the same id when {@link #clearForceDisabled enabling back} the command. */ forceDisabled( id ) { this._disableStack.add( id ); if ( this._disableStack.size == 1 ) { this.on( 'set:isEnabled', forceDisable, { priority: 'highest' } ); this.isEnabled = false; } } /** * Clears forced disable previously set through {@link #forceDisabled}. See {@link #forceDisabled}. * * @param {String} id Unique identifier, equal to the one passed in {@link #forceDisabled} call. */ clearForceDisabled( id ) { this._disableStack.delete( id ); if ( this._disableStack.size == 0 ) { this.off( 'set:isEnabled', forceDisable ); this.refresh(); } } /** * Executes the command. * * A command may accept parameters. They will be passed from {@link module:core/editor/editor~Editor#execute `editor.execute()`} * to the command. * * The `execute()` method will automatically abort when the command is disabled ({@link #isEnabled} is `false`). * This behavior is implemented by a high priority listener to the {@link #event:execute} event. * * In order to see how to disable a command from "outside" see the {@link #isEnabled} documentation. * * This method may return a value, which would be forwarded all the way down to the * {@link module:core/editor/editor~Editor#execute `editor.execute()`}. * * @fires execute */ execute() {} /** * Destroys the command. */ destroy() { this.stopListening(); } /** * Event fired by the {@link #execute} method. The command action is a listener to this event so it's * possible to change/cancel the behavior of the command by listening to this event. * * See {@link module:utils/observablemixin~ObservableMixin#decorate} for more information and samples. * * **Note:** This event is fired even if command is disabled. However, it is automatically blocked * by a high priority listener in order to prevent command execution. * * @event execute */ } mix( Command, ObservableMixin ); // Helper function that forces command to be disabled. function forceDisable( evt ) { evt.return = false; evt.stop(); }