|
|
@@ -4,13 +4,13 @@
|
|
|
*/
|
|
|
|
|
|
import Command from './command.js';
|
|
|
-import TreeWalker from '../../engine/model/treewalker.js';
|
|
|
-import Range from '../../engine/model/range.js';
|
|
|
+import getSchemaValidRanges from './helpers/getschemavalidranges.js';
|
|
|
+import isAttributeAllowedInSelection from './helpers/isattributeallowedinselection.js';
|
|
|
|
|
|
/**
|
|
|
- * 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 engine.model.Document#selection} to
|
|
|
- * decide which nodes (if any) should be changed, and applies or removes attributes from them.
|
|
|
+ * An extension of basic {@link core.command.Command} class, which provides utilities for a command that toggle a single
|
|
|
+ * attribute on a text or element with value `true`. ToggleAttributeCommand uses {@link engine.model.Document#selection}
|
|
|
+ * to decide which nodes (if any) should be changed, and applies or removes attributes from them.
|
|
|
* See {@link engine.view.Converter#execute} for more.
|
|
|
*
|
|
|
* The command checks {@link engine.model.Document#schema} to decide if it should be enabled.
|
|
|
@@ -18,7 +18,7 @@ import Range from '../../engine/model/range.js';
|
|
|
*
|
|
|
* @memberOf core.command
|
|
|
*/
|
|
|
-export default class AttributeCommand extends Command {
|
|
|
+export default class ToggleAttributeCommand extends Command {
|
|
|
/**
|
|
|
* @see core.command.Command
|
|
|
* @param {core.editor.Editor} editor
|
|
|
@@ -30,7 +30,7 @@ export default class AttributeCommand extends Command {
|
|
|
/**
|
|
|
* Attribute that will be set by the command.
|
|
|
*
|
|
|
- * @member {String} core.command.AttributeCommand#attributeKey
|
|
|
+ * @member {String} core.command.ToggleAttributeCommand#attributeKey
|
|
|
*/
|
|
|
this.attributeKey = attributeKey;
|
|
|
|
|
|
@@ -39,7 +39,7 @@ export default class AttributeCommand extends Command {
|
|
|
* the command's attribute set. For range selection it means that all nodes inside have the attribute applied.
|
|
|
*
|
|
|
* @observable
|
|
|
- * @member {Boolean} core.command.AttributeCommand#value
|
|
|
+ * @member {Boolean} core.command.ToggleAttributeCommand#value
|
|
|
*/
|
|
|
this.set( 'value', false );
|
|
|
|
|
|
@@ -49,47 +49,15 @@ export default class AttributeCommand extends Command {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Checks {@link engine.model.Document#schema} to decide if the command should be enabled:
|
|
|
- * * if selection is on range, the command is enabled if any of nodes in that range can have bold,
|
|
|
- * * if selection is collapsed, the command is enabled if text with bold is allowed in that node.
|
|
|
+ * Checks if {@link engine.model.Document#schema} allows to create attribute in {@link engine.model.Document#selection}
|
|
|
*
|
|
|
* @private
|
|
|
* @returns {Boolean}
|
|
|
*/
|
|
|
_checkEnabled() {
|
|
|
- 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.check( { name: '$text', inside: selection.getFirstPosition(), attributes: this.attributeKey } );
|
|
|
- } 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 ) {
|
|
|
- // If returned item does not have name property, it is a model.TextFragment.
|
|
|
- const name = step.value.item.name || '$text';
|
|
|
-
|
|
|
- if ( schema.check( { name: name, inside: last, attributes: this.attributeKey } ) ) {
|
|
|
- // If we found a node that is allowed to have the attribute, return true.
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- last = walker.position;
|
|
|
- step = walker.next();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ const document = this.editor.document;
|
|
|
|
|
|
- // If we haven't found such node, return false.
|
|
|
- return false;
|
|
|
+ return isAttributeAllowedInSelection( this.attributeKey, document.selection, document.schema );
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -125,7 +93,7 @@ export default class AttributeCommand extends Command {
|
|
|
} else {
|
|
|
// If selection has non-collapsed ranges, we change attribute on nodes inside those ranges.
|
|
|
document.enqueueChanges( () => {
|
|
|
- const ranges = this._getSchemaValidRanges( selection.getRanges() );
|
|
|
+ const ranges = getSchemaValidRanges( this.attributeKey, selection.getRanges(), document.schema );
|
|
|
|
|
|
// Keep it as one undo step.
|
|
|
const batch = document.batch();
|
|
|
@@ -140,46 +108,4 @@ 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.
|
|
|
- *
|
|
|
- * @private
|
|
|
- * @param {Array.<engine.model.Range>} ranges Ranges to be validated.
|
|
|
- * @returns {Array.<engine.model.Range>} Ranges without invalid parts.
|
|
|
- */
|
|
|
- _getSchemaValidRanges( ranges ) {
|
|
|
- const validRanges = [];
|
|
|
-
|
|
|
- for ( let range of ranges ) {
|
|
|
- const walker = new TreeWalker( { boundaries: range, mergeCharacters: true } );
|
|
|
- let step = walker.next();
|
|
|
-
|
|
|
- let last = range.start;
|
|
|
- let from = range.start;
|
|
|
- let to = range.end;
|
|
|
-
|
|
|
- while ( !step.done ) {
|
|
|
- const name = step.value.item.name || '$text';
|
|
|
-
|
|
|
- if ( !this.editor.document.schema.check( { name: name, inside: last, attributes: this.attributeKey } ) ) {
|
|
|
- if ( !from.isEqual( last ) ) {
|
|
|
- validRanges.push( new Range( from, last ) );
|
|
|
- }
|
|
|
-
|
|
|
- from = walker.position;
|
|
|
- }
|
|
|
-
|
|
|
- last = walker.position;
|
|
|
- step = walker.next();
|
|
|
- }
|
|
|
-
|
|
|
- if ( from && !from.isEqual( to ) ) {
|
|
|
- validRanges.push( new Range( from, to ) );
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return validRanges;
|
|
|
- }
|
|
|
}
|