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

Commit 26673a0

Browse files
authored
Merge pull request #1449 from ckeditor/t/1443-2
Fix: Element to attribute upcast should set an attribute on all the elements inside the converted element. See #1443.
2 parents de614ac + e42b4c2 commit 26673a0

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

src/conversion/upcast-converters.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ export function upcastElementToElement( config ) {
7474
* This conversion results in setting an attribute on a model node. For example, view `<strong>Foo</strong>` becomes
7575
* `Foo` {@link module:engine/model/text~Text model text node} with `bold` attribute set to `true`.
7676
*
77+
* This helper is meant to set a model attribute on all the elements that are inside the converted element:
78+
*
79+
* <strong>Foo</strong> --> <strong><p>Foo</p></strong> --> <paragraph><$text bold="true">Foo</$text></paragraph>
80+
*
81+
* Above is a sample of HTML code, that goes through autoparagraphing (first step) and then is converted (second step).
82+
* Even though `<strong>` is over `<p>` element, `bold="true"` was added to the text. See
83+
* {@link module:engine/model/upcast-converters~upcastAttributeToAttribute} for comparison.
84+
*
7785
* Keep in mind that the attribute will be set only if it is allowed by {@link module:engine/model/schema~Schema schema} configuration.
7886
*
7987
* upcastElementToAttribute( { view: 'strong', model: 'bold' } );
@@ -138,7 +146,7 @@ export function upcastElementToAttribute( config ) {
138146

139147
_normalizeModelAttributeConfig( config );
140148

141-
const converter = _prepareToAttributeConverter( config );
149+
const converter = _prepareToAttributeConverter( config, false );
142150

143151
const elementName = _getViewElementNameFromConfig( config );
144152
const eventName = elementName ? 'element:' + elementName : 'element';
@@ -154,6 +162,19 @@ export function upcastElementToAttribute( config ) {
154162
* This conversion results in setting an attribute on a model node. For example, view `<img src="foo.jpg"></img>` becomes
155163
* `<image source="foo.jpg"></image>` in the model.
156164
*
165+
* This helper is meant to convert view attributes from view elements which got converted to the model, so the view attribute
166+
* is set only on the corresponding model node:
167+
*
168+
* <div class="dark"><div>foo</div></div> --> <div dark="true"><div>foo</div></div>
169+
*
170+
* Above, `class="dark"` attribute is added only to the `<div>` elements that has it. This is in contrary to
171+
* {@link module:engine/conversion/upcast-converters~upcastElementToAttribute} which sets attributes for all the children in the model:
172+
*
173+
* <strong>Foo</strong> --> <strong><p>Foo</p></strong> --> <paragraph><$text bold="true">Foo</$text></paragraph>
174+
*
175+
* Above is a sample of HTML code, that goes through autoparagraphing (first step) and then is converted (second step).
176+
* Even though `<strong>` is over `<p>` element, `bold="true"` was added to the text.
177+
*
157178
* Keep in mind that the attribute will be set only if it is allowed by {@link module:engine/model/schema~Schema schema} configuration.
158179
*
159180
* upcastAttributeToAttribute( { view: 'src', model: 'source' } );
@@ -223,7 +244,7 @@ export function upcastAttributeToAttribute( config ) {
223244

224245
_normalizeModelAttributeConfig( config, viewKey );
225246

226-
const converter = _prepareToAttributeConverter( config );
247+
const converter = _prepareToAttributeConverter( config, true );
227248

228249
return dispatcher => {
229250
dispatcher.on( 'element', converter, { priority: config.converterPriority || 'low' } );
@@ -442,9 +463,9 @@ function _normalizeModelAttributeConfig( config, viewAttributeKeyToCopy = null )
442463
//
443464
// @param {String} modelAttributeKey The key of the model attribute to set on a model node.
444465
// @param {Object|Array.<Object>} config Conversion configuration. It is possible to provide multiple configurations in an array.
445-
// @param {Boolean} consumeName If set to `true` converter will try to consume name. If set to `false` converter will not try to
446-
// consume name. This flag overwrites parameter returned by `Matcher#match`.
447-
function _prepareToAttributeConverter( config ) {
466+
// @param {Boolean} shallow If set to `true` the attribute will be set only on top-level nodes. Otherwise, it will be set
467+
// on all elements in the range.
468+
function _prepareToAttributeConverter( config, shallow ) {
448469
const matcher = new Matcher( config.view );
449470

450471
return ( evt, data, conversionApi ) => {
@@ -483,7 +504,7 @@ function _prepareToAttributeConverter( config ) {
483504
}
484505

485506
// Set attribute on current `output`. `Schema` is checked inside this helper function.
486-
const attributeWasSet = _setAttributeOn( data.modelRange, { key: modelKey, value: modelValue }, conversionApi );
507+
const attributeWasSet = _setAttributeOn( data.modelRange, { key: modelKey, value: modelValue }, shallow, conversionApi );
487508

488509
if ( attributeWasSet ) {
489510
conversionApi.consumable.consume( data.viewItem, match.match );
@@ -509,12 +530,14 @@ function _onlyViewNameIsDefined( config ) {
509530
// @param {module:engine/model/range~Range} modelRange Model range on which attribute should be set.
510531
// @param {Object} modelAttribute Model attribute to set.
511532
// @param {Object} conversionApi Conversion API.
533+
// @param {Boolean} shallow If set to `true` the attribute will be set only on top-level nodes. Otherwise, it will be set
534+
// on all elements in the range.
512535
// @returns {Boolean} `true` if attribute was set on at least one node from given `modelRange`.
513-
function _setAttributeOn( modelRange, modelAttribute, conversionApi ) {
536+
function _setAttributeOn( modelRange, modelAttribute, shallow, conversionApi ) {
514537
let result = false;
515538

516539
// Set attribute on each item in range according to Schema.
517-
for ( const node of Array.from( modelRange.getItems( { shallow: true } ) ) ) {
540+
for ( const node of Array.from( modelRange.getItems( { shallow } ) ) ) {
518541
if ( conversionApi.schema.checkAttribute( node, modelAttribute.key ) ) {
519542
conversionApi.writer.setAttribute( modelAttribute.key, modelAttribute.value, node );
520543

tests/conversion/upcast-converters.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,27 @@ describe( 'upcast-helpers', () => {
349349
'<$text attribB="true" bold="true">foo</$text>'
350350
);
351351
} );
352+
353+
// #1443.
354+
it( 'should set attributes on the element\'s children', () => {
355+
const helperBold = upcastElementToAttribute( {
356+
model: 'bold',
357+
view: { name: 'strong' }
358+
} );
359+
360+
const helperP = upcastElementToElement( { view: 'p', model: 'paragraph' } );
361+
362+
conversion.for( 'upcast' ).add( helperP ).add( helperBold );
363+
364+
expectResult(
365+
new ViewAttributeElement(
366+
'strong',
367+
null,
368+
new ViewContainerElement( 'p', null, new ViewText( 'Foo' ) )
369+
),
370+
'<paragraph><$text bold="true">Foo</$text></paragraph>'
371+
);
372+
} );
352373
} );
353374

354375
describe( 'upcastAttributeToAttribute', () => {

0 commit comments

Comments
 (0)