Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit 4e4f5c3

Browse files
authored
Merge pull request #1221 from ckeditor/t/532
Other: Rewritten the Schema API. Closes #532.
2 parents aea6119 + f7d2c80 commit 4e4f5c3

40 files changed

+3483
-2135
lines changed

src/controller/datacontroller.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import ModelRange from '../model/range';
3232
* * {@link module:engine/conversion/modelconversiondispatcher~ModelConversionDispatcher model to view} and
3333
* * {@link module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher view to model} converters.
3434
*
35-
* @mixes module:utils/emittermixin~ObservableMixin
35+
* @mixes module:utils/observablemixin~ObservableMixin
3636
*/
3737
export default class DataController {
3838
/**

src/conversion/buildviewconverter.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -291,10 +291,7 @@ class ViewConverterBuilder {
291291
continue;
292292
}
293293

294-
// Check whether generated structure is okay with `Schema`.
295-
const keys = Array.from( modelElement.getAttributeKeys() );
296-
297-
if ( !conversionApi.schema.check( { name: modelElement.name, attributes: keys, inside: data.context } ) ) {
294+
if ( !conversionApi.schema.checkChild( data.context, modelElement ) ) {
298295
continue;
299296
}
300297

@@ -518,16 +515,7 @@ function setAttributeOn( toChange, attribute, data, conversionApi ) {
518515
return;
519516
}
520517

521-
const keys = Array.from( toChange.getAttributeKeys() );
522-
keys.push( attribute.key );
523-
524-
const schemaQuery = {
525-
name: toChange.name || '$text',
526-
attributes: keys,
527-
inside: data.context
528-
};
529-
530-
if ( conversionApi.schema.check( schemaQuery ) ) {
518+
if ( conversionApi.schema.checkAttribute( toChange, attribute.key ) ) {
531519
conversionApi.writer.setAttribute( attribute.key, attribute.value, toChange );
532520
}
533521
}

src/conversion/view-to-model-converters.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,7 @@ export function convertToModelFragment() {
4141
*/
4242
export function convertText() {
4343
return ( evt, data, consumable, conversionApi ) => {
44-
const schemaQuery = {
45-
name: '$text',
46-
inside: data.context
47-
};
48-
49-
if ( conversionApi.schema.check( schemaQuery ) ) {
44+
if ( conversionApi.schema.checkChild( data.context, '$text' ) ) {
5045
if ( consumable.consume( data.input ) ) {
5146
data.output = conversionApi.writer.createText( data.input.data );
5247
}

src/conversion/viewconversiondispatcher.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,8 @@ import log from '@ckeditor/ckeditor5-utils/src/log';
5050
* // Converter for paragraphs (<p>).
5151
* viewDispatcher.on( 'element:p', ( evt, data, consumable, conversionApi ) => {
5252
* const paragraph = new ModelElement( 'paragraph' );
53-
* const schemaQuery = {
54-
* name: 'paragraph',
55-
* inside: data.context
56-
* };
5753
*
58-
* if ( conversionApi.schema.check( schemaQuery ) ) {
54+
* if ( conversionApi.schema.checkChild( data.context, paragraph ) ) {
5955
* if ( !consumable.consume( data.input, { name: true } ) ) {
6056
* // Before converting this paragraph's children we have to update their context by this paragraph.
6157
* data.context.push( paragraph );
@@ -81,7 +77,7 @@ import log from '@ckeditor/ckeditor5-utils/src/log';
8177
* inside: data.context
8278
* };
8379
*
84-
* if ( conversionApi.schema.check( schemaQuery ) ) {
80+
* if ( conversionApi.schema.checkAttribute( [ ...data.context, '$text' ], 'link' ) ) {
8581
* item.setAttribute( 'link', data.input.getAttribute( 'href' ) );
8682
* }
8783
* }

src/dev-utils/model.js

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export function stringify( node, selectionOrPositionOrRange = null ) {
243243
* @param {Object} [options={}] Additional configuration.
244244
* @param {Array<Object>} [options.selectionAttributes] List of attributes which will be passed to the selection.
245245
* @param {Boolean} [options.lastRangeBackward=false] If set to true last range will be added as backward.
246-
* @param {module:engine/model/schema~SchemaPath} [options.context=[ '$root' ]] The conversion context.
246+
* @param {module:engine/model/schema~SchemaContextDefinition} [options.context=[ '$root' ]] The conversion context.
247247
* If not provided default `[ '$root' ]` will be used.
248248
* @returns {module:engine/model/element~Element|module:engine/model/text~Text|
249249
* module:engine/model/documentfragment~DocumentFragment|Object} Returns parsed model node or
@@ -329,14 +329,10 @@ function convertToModelFragment() {
329329

330330
function convertToModelElement() {
331331
return ( evt, data, consumable, conversionApi ) => {
332-
const schemaQuery = {
333-
name: data.input.name,
334-
attributes: Array.from( data.input.getAttributeKeys() ),
335-
inside: data.context
336-
};
337-
338-
if ( !conversionApi.schema.check( schemaQuery ) ) {
339-
throw new Error( `Element '${ schemaQuery.name }' not allowed in context ${ JSON.stringify( data.context ) }.` );
332+
const elementName = data.input.name;
333+
334+
if ( !conversionApi.schema.checkChild( data.context, elementName ) ) {
335+
throw new Error( `Element '${ elementName }' was not allowed in context ${ JSON.stringify( data.context ) }.` );
340336
}
341337

342338
// View attribute value is a string so we want to typecast it to the original type.
@@ -356,13 +352,8 @@ function convertToModelElement() {
356352

357353
function convertToModelText( withAttributes = false ) {
358354
return ( evt, data, consumable, conversionApi ) => {
359-
const schemaQuery = {
360-
name: '$text',
361-
inside: data.context
362-
};
363-
364-
if ( !conversionApi.schema.check( schemaQuery ) ) {
365-
throw new Error( `Element '${ schemaQuery.name }' not allowed in context ${ JSON.stringify( data.context ) }.` );
355+
if ( !conversionApi.schema.checkChild( data.context, '$text' ) ) {
356+
throw new Error( `Text was not allowed in context ${ JSON.stringify( data.context ) }.` );
366357
}
367358

368359
let node;

src/model/document.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ export default class Document {
248248
const schema = this.model.schema;
249249

250250
// Return collapsed range if provided position is valid.
251-
if ( schema.check( { name: '$text', inside: position } ) ) {
251+
if ( schema.checkChild( position, '$text' ) ) {
252252
return new Range( position );
253253
}
254254

@@ -266,11 +266,11 @@ export default class Document {
266266
const type = ( data.walker == backwardWalker ? 'elementEnd' : 'elementStart' );
267267
const value = data.value;
268268

269-
if ( value.type == type && schema.objects.has( value.item.name ) ) {
269+
if ( value.type == type && schema.isObject( value.item ) ) {
270270
return Range.createOn( value.item );
271271
}
272272

273-
if ( schema.check( { name: '$text', inside: value.nextPosition } ) ) {
273+
if ( schema.checkChild( value.nextPosition, '$text' ) ) {
274274
return new Range( value.nextPosition );
275275
}
276276
}

src/model/documentselection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ export default class DocumentSelection extends Selection {
621621
// ...look for a first character node in that range and take attributes from it.
622622
for ( const value of range ) {
623623
// If the item is an object, we don't want to get attributes from its children.
624-
if ( value.item.is( 'element' ) && schema.objects.has( value.item.name ) ) {
624+
if ( value.item.is( 'element' ) && schema.isObject( value.item ) ) {
625625
break;
626626
}
627627

src/model/model.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import getSelectedContent from './utils/getselectedcontent';
3232
* {@link module:engine/model/model~Model#document}, and all detached nodes, used to data manipulation. All of them are
3333
* created and modified by the {@link module:engine/model/writer~Writer}, which can be get using
3434
* {@link module:engine/model/model~Model#change} or {@link module:engine/model/model~Model#enqueueChange} methods.
35+
*
36+
* @mixes module:utils/observablemixin~ObservableMixin
3537
*/
3638
export default class Model {
3739
constructor() {
@@ -68,6 +70,23 @@ export default class Model {
6870

6971
[ 'insertContent', 'deleteContent', 'modifySelection', 'getSelectedContent', 'applyOperation' ]
7072
.forEach( methodName => this.decorate( methodName ) );
73+
74+
// Register some default abstract entities.
75+
this.schema.register( '$root', {
76+
isLimit: true
77+
} );
78+
this.schema.register( '$block', {
79+
allowIn: '$root',
80+
isBlock: true
81+
} );
82+
this.schema.register( '$text', {
83+
allowIn: '$block'
84+
} );
85+
this.schema.register( '$clipboardHolder', {
86+
allowContentOf: '$root',
87+
isLimit: true
88+
} );
89+
this.schema.extend( '$text', { allowIn: '$clipboardHolder' } );
7190
}
7291

7392
/**
@@ -296,7 +315,7 @@ export default class Model {
296315

297316
for ( const item of rangeOrElement.getItems() ) {
298317
// Remember, `TreeWalker` returns always `textProxy` nodes.
299-
if ( item.is( 'textProxy' ) || this.schema.objects.has( item.name ) ) {
318+
if ( item.is( 'textProxy' ) || this.schema.isObject( item ) ) {
300319
return true;
301320
}
302321
}

0 commit comments

Comments
 (0)