浏览代码

Docs: Documentation for core.command.Command and core.command.AttributeCommand.

Szymon Cofalik 9 年之前
父节点
当前提交
efdfc8443f

+ 63 - 4
packages/ckeditor5-engine/src/command/attributecommand.js

@@ -10,44 +10,76 @@ import TreeWalker from '../treemodel/treewalker.js';
 import Range from '../treemodel/range.js';
 
 /**
- * An extension of basic {@link core.command.Command} class, which provides utilities typical of commands that sets an
- * attribute on specific elements.
+ * An extension of basic {@link core.command.Command} class, which provides utilities for a command that sets a single
+ * attribute on a text or element with value `true`. AttributeCommand uses {@link treeModel.Document#selection} to
+ * decide which nodes (if any) should be changed, and applies or removes attributes from them. See {@link #execute} for more.
+ *
+ * The command checks {@link treeModel.Document#schema} to decide if it should be enabled. See {@link #checkSchema} for more.
  *
  * @class core.command.AttributeCommand
  */
 
 export default class AttributeCommand extends Command {
+	/**
+	 * @see core.command.Command
+	 * @param editor {core.Editor}
+	 * @param attributeKey {String} Attribute that will be set by the command.
+	 */
 	constructor( editor, attributeKey ) {
 		super( editor );
 
+		/**
+		 * Attribute that will be set by the command.
+		 *
+		 * @type {String}
+		 */
 		this.attributeKey = attributeKey;
 	}
 
+	/**
+	 * Flag indicating whether command is active. For collapsed selection it means that typed characters will have
+	 * the attribute set. For range selection it means that all nodes inside have the attribute applied.
+	 *
+	 * @returns {Boolean}
+	 */
 	get value() {
 		return this.editor.document.selection.hasAttribute( this.attributeKey );
 	}
 
+	/**
+	 * Checks {@link treeModel.Document#schema} to decide if the command should be enabled:
+	 * * if selection is on range, command is enabled if any of nodes in that range can have bold,
+	 * * if selection is collapsed, command is enabled if text with bold is allowed in that node.
+	 *
+	 * @see core.command.Command#checkSchema
+	 * @returns {Boolean}
+	 */
 	checkSchema() {
 		const selection = this.editor.document.selection;
 		const schema = this.editor.document.schema;
 
 		if ( selection.isCollapsed ) {
+			// Check whether schema allows for a test with `attributeKey` in caret position.
 			return schema.checkAtPosition( { name: '$text', attribute: this.attributeKey }, selection.getFirstPosition() );
 		} else {
 			const ranges = selection.getRanges();
 
+			// For all ranges, check nodes in them until you find a node that is allowed to have `attributeKey` attribute.
 			for ( let range of ranges ) {
 				const walker = new TreeWalker( { boundaries: range, mergeCharacters: true } );
 				let last = walker.position;
 				let step = walker.next();
 
+				// Walk the range.
 				while ( !step.done ) {
 					const query = {
+						// If returned item does not have name property, it is a treeModel.TextFragment.
 						name: step.value.item.name || '$text',
 						attribute: this.attributeKey
 					};
 
 					if ( schema.checkAtPosition( query, last ) ) {
+						// If we found a node that is allowed to have the attribute, return true.
 						return true;
 					}
 
@@ -57,14 +89,33 @@ export default class AttributeCommand extends Command {
 			}
 		}
 
+		// If we haven't found such node, return false.
 		return false;
 	}
 
-	execute( param ) {
+	/**
+	 * Executes the command: adds or removes attributes to nodes or selection.
+	 *
+	 * If the command is active (`value == true`), it will remove attributes. Otherwise, it will set attributes.
+	 *
+	 * The execution result differs, depending on the {@link treeModel.Document#selection}:
+	 * * if selection is on a range, the command applies the attribute on all nodes in that ranges
+	 * (if they are allowed to have this attribute by the{@link treeModel.Schema schema}),
+	 * * if selection is collapsed in non-empty node, the command applies attribute to the {@link treeModel.Document#selection}
+	 * itself (note that typed characters copy attributes from selection),
+	 * * if selection is collapsed in empty node, the command applies attribute to the parent node of selection (note
+	 * that selection inherits all attributes from a node if it is in empty node).
+	 *
+	 * If the command is disabled (`isEnabled == false`) when it is executed, nothing will happen.
+	 *
+	 * @param [forceValue] {Boolean} If set it will force command behavior. If `true`, command will apply attribute,
+	 * otherwise command will remove attribute. If not set, command will look for it's current value to decide what it should do.
+	 */
+	execute( forceValue ) {
 		if ( this.isEnabled ) {
 			let document = this.editor.document;
 			let selection = document.selection;
-			let value = ( param === undefined ) ? !this.value : param;
+			let value = ( forceValue === undefined ) ? !this.value : forceValue;
 
 			if ( selection.isCollapsed ) {
 				// If selection is collapsed change only selection attribute.
@@ -90,6 +141,14 @@ export default class AttributeCommand extends Command {
 		}
 	}
 
+	/**
+	 * Walks through given array of ranges and removes parts of them that are not allowed by schema to have the
+	 * attribute set. This is done by breaking a range in two and omitting the not allowed part.
+	 *
+	 * @param ranges {Array.<treeModel.Range>} Ranges to be validated.
+	 * @returns {Array} Ranges without invalid parts.
+	 * @private
+	 */
 	_getSchemaValidRanges( ranges ) {
 		let validRanges = [];
 

+ 52 - 1
packages/ckeditor5-engine/src/command/command.js

@@ -11,15 +11,37 @@ import utils from '../utils.js';
 /**
  * The base class for CKEditor commands.
  *
+ * Commands are main way to manipulate editor contents and state. They are mostly used by UI elements (or by other
+ * commands) to make changes in Tree Model. Commands are available in every part of code that has access to
+ * {@link core.Editor} instance, since they are registered in it and executed through {@link core.Editor#execute}.
+ * Commands instances are available through {@link core.Editor#commands}.
+ *
+ * This is an abstract base class for all commands.
+ *
  * @class core.command.Command
  */
 
 export default class Command {
 	/**
 	 * Creates a new Command instance.
+	 *
+	 * @param {core.Editor} editor Editor on which this command will be used.
+	 * @constructor
 	 */
 	constructor( editor ) {
+		/**
+		 * Editor on which this command will be used.
+		 *
+		 * @type {core.Editor}
+		 */
 		this.editor = editor;
+
+		/**
+		 * Flag indicating whether a command is enabled or disabled.
+		 * A disabled command should do nothing upon it's execution.
+		 *
+		 * @type {Boolean}
+		 */
 		this.isEnabled = true;
 
 		// If schema checking function is specified, add it to the `refreshState` listeners.
@@ -33,22 +55,51 @@ export default class Command {
 		}
 	}
 
+	/**
+	 * Checks if a command should be enabled according to schema. This method can be defined in child class (but is not
+	 * obligatory). If it is defined, it will be added as a callback to `refreshState` event.
+	 *
+	 * @method checkSchema
+	 * @returns {Boolean} `true` if command should be enabled according to {@link treeModel.Document#schema}. `false` otherwise.
+	 */
+
+	/**
+	 * Fires `refreshState` event and checks it's resolve value to decide whether command should be enabled or not.
+	 * Other parts of code might listen to `refreshState` event on this command and add their callbacks. This
+	 * way the responsibility of deciding whether a command should be enabled is shared.
+	 */
 	refreshState() {
 		this.isEnabled = this.fire( 'refreshState' ) !== false;
 	}
 
+	/**
+	 * Disables the command. This should be used only by the command itself. Other parts of code should add
+	 * listeners to `refreshState` event.
+	 *
+	 * @private
+	 */
 	_disable() {
 		this.on( 'refreshState', disableCallback );
 		this.refreshState();
 	}
 
+	/**
+	 * Enables the command (internally). This should be used only by the command itself. Command will be enabled if
+	 * other listeners does not return false on `refreshState` event callbacks. Firing {@link core.command.Command#_enable}
+	 * does not guarantee that {@link core.command.Command#isEnabled} will be set to true, as it depends on other listeners.
+	 *
+	 * @private
+	 */
 	_enable() {
 		this.off( 'refreshState', disableCallback );
 		this.refreshState();
 	}
 
+	/**
+	 * Executes command.
+	 * This is an abstract method that should be overwritten in child classes.
+	 */
 	execute() {
-		// Should be overwritten in child class.
 	}
 }