diff --git a/src/model/schema.js b/src/model/schema.js index f1fdc4ad5..cb990057d 100644 --- a/src/model/schema.js +++ b/src/model/schema.js @@ -39,6 +39,14 @@ export default class Schema { constructor() { this._sourceDefinitions = {}; + /** + * A map containing attribute's properties. + * + * @private + * @member {Map.} + */ + this._attributeProperties = {}; + this.decorate( 'checkChild' ); this.decorate( 'checkAttribute' ); @@ -140,19 +148,6 @@ export default class Schema { this._clearCache(); } - /** - * Returns all registered items. - * - * @returns {Object.} - */ - getDefinitions() { - if ( !this._compiledDefinitions ) { - this._compile(); - } - - return this._compiledDefinitions; - } - /** * Returns a definition of the given item or `undefined` if item is not registered. * @@ -475,6 +470,70 @@ export default class Schema { }, { priority: 'high' } ); } + /** + * Registers custom properties to a given attribute. + * + * It can be used to mark the attributes relation and handle them in a common way. + * + * // Mark blockQuote as a formatting attribute. + * schema.setAttributeProperties( 'blockQuote', { + * isFormatting: true + * } ); + * + * // Override code not to be considered a formatting markup. + * schema.setAttributeProperties( 'code', { + * isFormatting: false + * } ); + * + * You can also use custom attributes: + * + * schema.setAttributeProperties( 'blockQuote', { + * customAttribute: 'value' + * } ); + * + * Subsequent calls to the same attributes will add up the value: + * + * schema.setAttributeProperties( 'blockQuote', { + * one: 1 + * } ); + * + * schema.setAttributeProperties( 'blockQuote', { + * two: 2 + * } ); + * + * console.log( schema.getAttributeProperties( 'blockQuote' ) ); + * // Logs: {one: 1, two: 2} + * + * @param {String} attributeName Name of the attribute to receive properties. + * @param {module:engine/model/schema~AttributeProperties} properties A dictionary of properties. + */ + setAttributeProperties( attributeName, properties ) { + this._attributeProperties[ attributeName ] = Object.assign( this._attributeProperties[ attributeName ] || {}, properties ); + } + + /** + * Returns properties assigned to a given attribute. + * + * @param {String} attributeName Name of the attribute. + * @returns {module:engine/model/schema~AttributeProperties} + */ + getAttributeProperties( attributeName ) { + return this._attributeProperties[ attributeName ]; + } + + /** + * Returns all registered items. + * + * @returns {Object.} + */ + getDefinitions() { + if ( !this._compiledDefinitions ) { + this._compile(); + } + + return this._compiledDefinitions; + } + /** * Returns the lowest {@link module:engine/model/schema~Schema#isLimit limit element} containing the entire * selection/range/position or the root otherwise. @@ -1285,6 +1344,15 @@ export class SchemaContext { * @typedef {Object} module:engine/model/schema~SchemaContextItem */ +/** + * A structure containing additional metadata describing the attribute. + * + * See {@link module:engine/model/schema~Schema#setAttributeProperties `Schema#setAttributeProperties()`} for usage examples. + * + * @typedef {Object} module:engine/model/schema~AttributeProperties + * @property {Boolean} [isFormatting] Indicates that the attribute should be considered as a visual formatting. + */ + function compileBaseItemRule( sourceItemRules, itemName ) { const itemRule = { name: itemName, diff --git a/tests/model/schema.js b/tests/model/schema.js index a01a6e54b..883112705 100644 --- a/tests/model/schema.js +++ b/tests/model/schema.js @@ -96,6 +96,46 @@ describe( 'Schema', () => { } ); } ); + describe( 'setAttributeProperties()', () => { + beforeEach( () => { + schema.register( '$root' ); + schema.register( 'paragraph', { + allowIn: '$root' + } ); + schema.register( '$text', { + allowIn: 'paragraph' + } ); + schema.extend( '$text', { allowAttributes: 'testAttribute' } ); + } ); + + it( 'allows registering new properties', () => { + schema.setAttributeProperties( 'testAttribute', { + foo: 'bar', + baz: 'bom' + } ); + + expect( schema.getAttributeProperties( 'testAttribute' ) ).to.deep.equal( { + foo: 'bar', + baz: 'bom' + } ); + } ); + + it( 'support adding properties in subsequent calls', () => { + schema.setAttributeProperties( 'testAttribute', { + first: 'foo' + } ); + + schema.setAttributeProperties( 'testAttribute', { + second: 'bar' + } ); + + expect( schema.getAttributeProperties( 'testAttribute' ) ).to.deep.equal( { + first: 'foo', + second: 'bar' + } ); + } ); + } ); + describe( 'getDefinitions()', () => { it( 'returns compiled definitions', () => { schema.register( '$root' );