@ckeditor/ckeditor5-html-support 32.0.0 → 34.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +2 -2
- package/README.md +2 -1
- package/build/html-support.js +2 -2
- package/build/translations/el.js +1 -0
- package/build/translations/en-au.js +1 -0
- package/build/translations/hr.js +1 -0
- package/build/translations/jv.js +1 -0
- package/build/translations/lv.js +1 -0
- package/build/translations/ur.js +1 -0
- package/lang/translations/el.po +21 -0
- package/lang/translations/en-au.po +21 -0
- package/lang/translations/hr.po +21 -0
- package/lang/translations/jv.po +21 -0
- package/lang/translations/lv.po +21 -0
- package/lang/translations/ur.po +21 -0
- package/package.json +31 -31
- package/src/conversionutils.js +48 -5
- package/src/converters.js +41 -22
- package/src/datafilter.js +141 -17
- package/src/dataschema.js +3 -0
- package/src/generalhtmlsupport.js +231 -1
- package/src/integrations/codeblock.js +6 -4
- package/src/integrations/documentlist.js +200 -0
- package/src/integrations/dualcontent.js +12 -3
- package/src/integrations/image.js +57 -27
- package/src/integrations/mediaembed.js +32 -8
- package/src/integrations/script.js +2 -1
- package/src/integrations/style.js +74 -0
- package/src/integrations/table.js +29 -7
- package/src/schemadefinitions.js +67 -67
- package/theme/datafilter.css +5 -0
package/src/conversionutils.js
CHANGED
|
@@ -10,12 +10,30 @@
|
|
|
10
10
|
import { cloneDeep } from 'lodash-es';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Helper function for downcast converter.
|
|
13
|
+
* Helper function for the downcast converter. Updates attributes on the given view element.
|
|
14
14
|
*
|
|
15
|
-
* @param {module:engine/view/downcastwriter~DowncastWriter} writer
|
|
16
|
-
* @param {Object}
|
|
17
|
-
* @param {
|
|
15
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer.
|
|
16
|
+
* @param {Object} oldViewAttributes The previous GHS attribute value.
|
|
17
|
+
* @param {Object} newViewAttributes The current GHS attribute value.
|
|
18
|
+
* @param {module:engine/view/element~Element} viewElement The view element to update.
|
|
18
19
|
*/
|
|
20
|
+
export function updateViewAttributes( writer, oldViewAttributes, newViewAttributes, viewElement ) {
|
|
21
|
+
if ( oldViewAttributes ) {
|
|
22
|
+
removeViewAttributes( writer, oldViewAttributes, viewElement );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if ( newViewAttributes ) {
|
|
26
|
+
setViewAttributes( writer, newViewAttributes, viewElement );
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Helper function for the downcast converter. Sets attributes on the given view element.
|
|
32
|
+
*
|
|
33
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer.
|
|
34
|
+
* @param {Object} viewAttributes The GHS attribute value.
|
|
35
|
+
* @param {module:engine/view/element~Element} viewElement The view element to update.
|
|
36
|
+
*/
|
|
19
37
|
export function setViewAttributes( writer, viewAttributes, viewElement ) {
|
|
20
38
|
if ( viewAttributes.attributes ) {
|
|
21
39
|
for ( const [ key, value ] of Object.entries( viewAttributes.attributes ) ) {
|
|
@@ -32,6 +50,31 @@ export function setViewAttributes( writer, viewAttributes, viewElement ) {
|
|
|
32
50
|
}
|
|
33
51
|
}
|
|
34
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Helper function for the downcast converter. Removes attributes on the given view element.
|
|
55
|
+
*
|
|
56
|
+
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer.
|
|
57
|
+
* @param {Object} viewAttributes The GHS attribute value.
|
|
58
|
+
* @param {module:engine/view/element~Element} viewElement The view element to update.
|
|
59
|
+
*/
|
|
60
|
+
export function removeViewAttributes( writer, viewAttributes, viewElement ) {
|
|
61
|
+
if ( viewAttributes.attributes ) {
|
|
62
|
+
for ( const [ key ] of Object.entries( viewAttributes.attributes ) ) {
|
|
63
|
+
writer.removeAttribute( key, viewElement );
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ( viewAttributes.styles ) {
|
|
68
|
+
for ( const style of Object.keys( viewAttributes.styles ) ) {
|
|
69
|
+
writer.removeStyle( style, viewElement );
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if ( viewAttributes.classes ) {
|
|
74
|
+
writer.removeClass( viewAttributes.classes, viewElement );
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
35
78
|
/**
|
|
36
79
|
* Merges view element attribute objects.
|
|
37
80
|
*
|
|
@@ -45,7 +88,7 @@ export function mergeViewElementAttributes( target, source ) {
|
|
|
45
88
|
for ( const key in source ) {
|
|
46
89
|
// Merge classes.
|
|
47
90
|
if ( Array.isArray( source[ key ] ) ) {
|
|
48
|
-
result[ key ] = Array.from( new Set( [ ...target[ key ], ...source[ key ] ] ) );
|
|
91
|
+
result[ key ] = Array.from( new Set( [ ...( target[ key ] || [] ), ...source[ key ] ] ) );
|
|
49
92
|
}
|
|
50
93
|
|
|
51
94
|
// Merge attributes or styles.
|
package/src/converters.js
CHANGED
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { toWidget } from 'ckeditor5/src/widget';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
setViewAttributes,
|
|
13
|
+
mergeViewElementAttributes,
|
|
14
|
+
updateViewAttributes
|
|
15
|
+
} from './conversionutils';
|
|
12
16
|
|
|
13
17
|
/**
|
|
14
18
|
* View-to-model conversion helper for object elements.
|
|
@@ -28,7 +32,7 @@ export function viewToModelObjectConverter( { model: modelName } ) {
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
|
-
* Conversion helper converting object element to HTML object widget.
|
|
35
|
+
* Conversion helper converting an object element to an HTML object widget.
|
|
32
36
|
*
|
|
33
37
|
* @param {module:core/editor/editor~Editor} editor
|
|
34
38
|
* @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition
|
|
@@ -37,27 +41,27 @@ export function viewToModelObjectConverter( { model: modelName } ) {
|
|
|
37
41
|
export function toObjectWidgetConverter( editor, { view: viewName, isInline } ) {
|
|
38
42
|
const t = editor.t;
|
|
39
43
|
|
|
40
|
-
return ( modelElement, { writer
|
|
44
|
+
return ( modelElement, { writer } ) => {
|
|
41
45
|
const widgetLabel = t( 'HTML object' );
|
|
42
46
|
|
|
43
|
-
// Widget cannot be a raw element because the widget system would not be able
|
|
44
|
-
// to add its UI to it. Thus, we need separate view container.
|
|
45
|
-
const viewContainer = writer.createContainerElement( isInline ? 'span' : 'div', {
|
|
46
|
-
class: 'html-object-embed',
|
|
47
|
-
'data-html-object-embed-label': widgetLabel
|
|
48
|
-
}, {
|
|
49
|
-
isAllowedInsideAttributeElement: isInline
|
|
50
|
-
} );
|
|
51
|
-
|
|
52
47
|
const viewElement = createObjectView( viewName, modelElement, writer );
|
|
48
|
+
const viewAttributes = modelElement.getAttribute( 'htmlAttributes' );
|
|
49
|
+
|
|
53
50
|
writer.addClass( 'html-object-embed__content', viewElement );
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
if ( viewAttributes && consumable.consume( modelElement, `attribute:htmlAttributes:${ modelElement.name }` ) ) {
|
|
52
|
+
if ( viewAttributes ) {
|
|
57
53
|
setViewAttributes( writer, viewAttributes, viewElement );
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
|
|
56
|
+
// Widget cannot be a raw element because the widget system would not be able
|
|
57
|
+
// to add its UI to it. Thus, we need separate view container.
|
|
58
|
+
const viewContainer = writer.createContainerElement( isInline ? 'span' : 'div',
|
|
59
|
+
{
|
|
60
|
+
class: 'html-object-embed',
|
|
61
|
+
'data-html-object-embed-label': widgetLabel
|
|
62
|
+
},
|
|
63
|
+
viewElement
|
|
64
|
+
);
|
|
61
65
|
|
|
62
66
|
return toWidget( viewContainer, writer, { widgetLabel } );
|
|
63
67
|
};
|
|
@@ -87,7 +91,19 @@ export function createObjectView( viewName, modelElement, writer ) {
|
|
|
87
91
|
export function viewToAttributeInlineConverter( { view: viewName, model: attributeKey }, dataFilter ) {
|
|
88
92
|
return dispatcher => {
|
|
89
93
|
dispatcher.on( `element:${ viewName }`, ( evt, data, conversionApi ) => {
|
|
90
|
-
|
|
94
|
+
let viewAttributes = dataFilter.processViewAttributes( data.viewItem, conversionApi );
|
|
95
|
+
|
|
96
|
+
// Do not apply the attribute if the element itself is already consumed and there are no view attributes to store.
|
|
97
|
+
if ( !viewAttributes && !conversionApi.consumable.test( data.viewItem, { name: true } ) ) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Otherwise, we might need to convert it to an empty object just to preserve element itself,
|
|
102
|
+
// for example `<cite>` => <$text htmlCite="{}">.
|
|
103
|
+
viewAttributes = viewAttributes || {};
|
|
104
|
+
|
|
105
|
+
// Consume the element itself if it wasn't consumed by any other converter.
|
|
106
|
+
conversionApi.consumable.consume( data.viewItem, { name: true } );
|
|
91
107
|
|
|
92
108
|
// Since we are converting to attribute we need a range on which we will set the attribute.
|
|
93
109
|
// If the range is not created yet, we will create it.
|
|
@@ -101,7 +117,7 @@ export function viewToAttributeInlineConverter( { view: viewName, model: attribu
|
|
|
101
117
|
// Node's children are converted recursively, so node can already include model attribute.
|
|
102
118
|
// We want to extend it, not replace.
|
|
103
119
|
const nodeAttributes = node.getAttribute( attributeKey );
|
|
104
|
-
const attributesToAdd = mergeViewElementAttributes( viewAttributes
|
|
120
|
+
const attributesToAdd = mergeViewElementAttributes( viewAttributes, nodeAttributes || {} );
|
|
105
121
|
|
|
106
122
|
conversionApi.writer.setAttribute( attributeKey, attributesToAdd, node );
|
|
107
123
|
}
|
|
@@ -143,11 +159,15 @@ export function attributeToViewInlineConverter( { priority, view: viewName } ) {
|
|
|
143
159
|
export function viewToModelBlockAttributeConverter( { view: viewName }, dataFilter ) {
|
|
144
160
|
return dispatcher => {
|
|
145
161
|
dispatcher.on( `element:${ viewName }`, ( evt, data, conversionApi ) => {
|
|
146
|
-
|
|
162
|
+
// Converting an attribute of an element that has not been converted to anything does not make sense
|
|
163
|
+
// because there will be nowhere to set that attribute on. At this stage, the element should've already
|
|
164
|
+
// been converted. A collapsed range can show up in to-do lists (<input>) or complex widgets (e.g. table).
|
|
165
|
+
// (https://github.com/ckeditor/ckeditor5/issues/11000).
|
|
166
|
+
if ( !data.modelRange || data.modelRange.isCollapsed ) {
|
|
147
167
|
return;
|
|
148
168
|
}
|
|
149
169
|
|
|
150
|
-
const viewAttributes = dataFilter.
|
|
170
|
+
const viewAttributes = dataFilter.processViewAttributes( data.viewItem, conversionApi );
|
|
151
171
|
|
|
152
172
|
if ( viewAttributes ) {
|
|
153
173
|
conversionApi.writer.setAttribute( 'htmlAttributes', viewAttributes, data.modelRange );
|
|
@@ -166,16 +186,15 @@ export function viewToModelBlockAttributeConverter( { view: viewName }, dataFilt
|
|
|
166
186
|
export function modelToViewBlockAttributeConverter( { model: modelName } ) {
|
|
167
187
|
return dispatcher => {
|
|
168
188
|
dispatcher.on( `attribute:htmlAttributes:${ modelName }`, ( evt, data, conversionApi ) => {
|
|
169
|
-
const viewAttributes = data.attributeNewValue;
|
|
170
|
-
|
|
171
189
|
if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
|
|
172
190
|
return;
|
|
173
191
|
}
|
|
174
192
|
|
|
193
|
+
const { attributeOldValue, attributeNewValue } = data;
|
|
175
194
|
const viewWriter = conversionApi.writer;
|
|
176
195
|
const viewElement = conversionApi.mapper.toViewElement( data.item );
|
|
177
196
|
|
|
178
|
-
|
|
197
|
+
updateViewAttributes( viewWriter, attributeOldValue, attributeNewValue, viewElement );
|
|
179
198
|
} );
|
|
180
199
|
};
|
|
181
200
|
}
|
package/src/datafilter.js
CHANGED
|
@@ -53,6 +53,9 @@ import '../theme/datafilter.css';
|
|
|
53
53
|
* }
|
|
54
54
|
* } );
|
|
55
55
|
*
|
|
56
|
+
* To apply the information about allowed and disallowed attributes in custom integration plugin,
|
|
57
|
+
* use the {@link module:html-support/datafilter~DataFilter#processViewAttributes `processViewAttributes()`} method.
|
|
58
|
+
*
|
|
56
59
|
* @extends module:core/plugin~Plugin
|
|
57
60
|
*/
|
|
58
61
|
export default class DataFilter extends Plugin {
|
|
@@ -106,8 +109,18 @@ export default class DataFilter extends Plugin {
|
|
|
106
109
|
*/
|
|
107
110
|
this._dataInitialized = false;
|
|
108
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Cached map of coupled attributes. Keys are the feature attributes names
|
|
114
|
+
* and values are arrays with coupled GHS attributes names.
|
|
115
|
+
*
|
|
116
|
+
* @private
|
|
117
|
+
* @member {Map.<String,Array>}
|
|
118
|
+
*/
|
|
119
|
+
this._coupledAttributes = null;
|
|
120
|
+
|
|
109
121
|
this._registerElementsAfterInit();
|
|
110
122
|
this._registerElementHandlers();
|
|
123
|
+
this._registerModelPostFixer();
|
|
111
124
|
}
|
|
112
125
|
|
|
113
126
|
/**
|
|
@@ -167,6 +180,9 @@ export default class DataFilter extends Plugin {
|
|
|
167
180
|
if ( this._dataInitialized ) {
|
|
168
181
|
this._fireRegisterEvent( definition );
|
|
169
182
|
}
|
|
183
|
+
|
|
184
|
+
// Reset cached map to recalculate it on the next usage.
|
|
185
|
+
this._coupledAttributes = null;
|
|
170
186
|
}
|
|
171
187
|
}
|
|
172
188
|
|
|
@@ -208,17 +224,30 @@ export default class DataFilter extends Plugin {
|
|
|
208
224
|
}
|
|
209
225
|
|
|
210
226
|
/**
|
|
211
|
-
*
|
|
227
|
+
* Processes all allowed and disallowed attributes on the view element by consuming them and returning the allowed ones.
|
|
212
228
|
*
|
|
213
|
-
* @
|
|
229
|
+
* This method applies the configuration set up by {@link #allowAttributes `allowAttributes()`}
|
|
230
|
+
* and {@link #disallowAttributes `disallowAttributes()`} over the given view element by consuming relevant attributes.
|
|
231
|
+
* It returns the allowed attributes that were found on the given view element for further processing by integration code.
|
|
232
|
+
*
|
|
233
|
+
* dispatcher.on( 'element:myElement', ( evt, data, conversionApi ) => {
|
|
234
|
+
* // Get rid of disallowed and extract all allowed attributes from a viewElement.
|
|
235
|
+
* const viewAttributes = dataFilter.processViewAttributes( data.viewItem, conversionApi );
|
|
236
|
+
* // Do something with them, i.e. store inside a model as a dictionary.
|
|
237
|
+
* if ( viewAttributes ) {
|
|
238
|
+
* conversionApi.writer.setAttribute( 'htmlAttributesOfMyElement', viewAttributes, data.modelRange );
|
|
239
|
+
* }
|
|
240
|
+
* } );
|
|
241
|
+
*
|
|
242
|
+
* @see module:engine/conversion/viewconsumable~ViewConsumable#consume
|
|
214
243
|
* @param {module:engine/view/element~Element} viewElement
|
|
215
|
-
* @param {module:engine/conversion/
|
|
244
|
+
* @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi
|
|
216
245
|
* @returns {Object} [result]
|
|
217
246
|
* @returns {Object} result.attributes Set with matched attribute names.
|
|
218
247
|
* @returns {Object} result.styles Set with matched style names.
|
|
219
248
|
* @returns {Array.<String>} result.classes Set with matched class names.
|
|
220
249
|
*/
|
|
221
|
-
|
|
250
|
+
processViewAttributes( viewElement, conversionApi ) {
|
|
222
251
|
// Make sure that the disabled attributes are handled before the allowed attributes are called.
|
|
223
252
|
// For example, for block images the <figure> converter triggers conversion for <img> first and then for other elements, i.e. <a>.
|
|
224
253
|
consumeAttributes( viewElement, conversionApi, this._disallowedAttributes );
|
|
@@ -240,10 +269,14 @@ export default class DataFilter extends Plugin {
|
|
|
240
269
|
this._fireRegisterEvent( definition );
|
|
241
270
|
}
|
|
242
271
|
}, {
|
|
243
|
-
// With
|
|
244
|
-
// running data conversion.
|
|
245
|
-
//
|
|
246
|
-
|
|
272
|
+
// With highest priority listener we are able to register elements right before
|
|
273
|
+
// running data conversion. Also:
|
|
274
|
+
// * Make sure that priority is higher than the one used by `RealTimeCollaborationClient`,
|
|
275
|
+
// as RTC is stopping event propagation.
|
|
276
|
+
// * Make sure no other features hook into this event before GHS because otherwise the
|
|
277
|
+
// downcast conversion (for these features) could run before GHS registered its converters
|
|
278
|
+
// (https://github.com/ckeditor/ckeditor5/issues/11356).
|
|
279
|
+
priority: priorities.get( 'highest' ) + 1
|
|
247
280
|
} );
|
|
248
281
|
}
|
|
249
282
|
|
|
@@ -284,6 +317,91 @@ export default class DataFilter extends Plugin {
|
|
|
284
317
|
}, { priority: 'lowest' } );
|
|
285
318
|
}
|
|
286
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Registers a model post-fixer that is removing coupled GHS attributes of inline elements. Those attributes
|
|
322
|
+
* are removed if a coupled feature attribute is removed.
|
|
323
|
+
*
|
|
324
|
+
* For example, consider following HTML:
|
|
325
|
+
*
|
|
326
|
+
* <a href="foo.html" id="myId">bar</a>
|
|
327
|
+
*
|
|
328
|
+
* Which would be upcasted to following text node in the model:
|
|
329
|
+
*
|
|
330
|
+
* <$text linkHref="foo.html" htmlA="{ attributes: { id: 'myId' } }">bar</$text>
|
|
331
|
+
*
|
|
332
|
+
* When the user removes the link from that text (using UI), only `linkHref` attribute would be removed:
|
|
333
|
+
*
|
|
334
|
+
* <$text htmlA="{ attributes: { id: 'myId' } }">bar</$text>
|
|
335
|
+
*
|
|
336
|
+
* The `htmlA` attribute would stay in the model and would cause GHS to generate an `<a>` element.
|
|
337
|
+
* This is incorrect from UX point of view, as the user wanted to remove the whole link (not only `href`).
|
|
338
|
+
*
|
|
339
|
+
* @private
|
|
340
|
+
*/
|
|
341
|
+
_registerModelPostFixer() {
|
|
342
|
+
const model = this.editor.model;
|
|
343
|
+
|
|
344
|
+
model.document.registerPostFixer( writer => {
|
|
345
|
+
const changes = model.document.differ.getChanges();
|
|
346
|
+
let changed = false;
|
|
347
|
+
|
|
348
|
+
const coupledAttributes = this._getCoupledAttributesMap();
|
|
349
|
+
|
|
350
|
+
for ( const change of changes ) {
|
|
351
|
+
// Handle only attribute removals.
|
|
352
|
+
if ( change.type != 'attribute' || change.attributeNewValue !== null ) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Find a list of coupled GHS attributes.
|
|
357
|
+
const attributeKeys = coupledAttributes.get( change.attributeKey );
|
|
358
|
+
|
|
359
|
+
if ( !attributeKeys ) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Remove the coupled GHS attributes on the same range as the feature attribute was removed.
|
|
364
|
+
for ( const { item } of change.range.getWalker( { shallow: true } ) ) {
|
|
365
|
+
for ( const attributeKey of attributeKeys ) {
|
|
366
|
+
if ( item.hasAttribute( attributeKey ) ) {
|
|
367
|
+
writer.removeAttribute( attributeKey, item );
|
|
368
|
+
changed = true;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return changed;
|
|
375
|
+
} );
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Collects the map of coupled attributes. The returned map is keyed by the feature attribute name
|
|
380
|
+
* and coupled GHS attribute names are stored in the value array .
|
|
381
|
+
*
|
|
382
|
+
* @private
|
|
383
|
+
* @returns {Map.<String,Array>}
|
|
384
|
+
*/
|
|
385
|
+
_getCoupledAttributesMap() {
|
|
386
|
+
if ( this._coupledAttributes ) {
|
|
387
|
+
return this._coupledAttributes;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
this._coupledAttributes = new Map();
|
|
391
|
+
|
|
392
|
+
for ( const definition of this._allowedElements ) {
|
|
393
|
+
if ( definition.coupledAttribute && definition.model ) {
|
|
394
|
+
const attributeNames = this._coupledAttributes.get( definition.coupledAttribute );
|
|
395
|
+
|
|
396
|
+
if ( attributeNames ) {
|
|
397
|
+
attributeNames.push( definition.model );
|
|
398
|
+
} else {
|
|
399
|
+
this._coupledAttributes.set( definition.coupledAttribute, [ definition.model ] );
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
287
405
|
/**
|
|
288
406
|
* Fires `register` event for the given element definition.
|
|
289
407
|
*
|
|
@@ -308,6 +426,7 @@ export default class DataFilter extends Plugin {
|
|
|
308
426
|
|
|
309
427
|
schema.register( modelName, definition.modelSchema );
|
|
310
428
|
|
|
429
|
+
/* istanbul ignore next: paranoid check */
|
|
311
430
|
if ( !viewName ) {
|
|
312
431
|
return;
|
|
313
432
|
}
|
|
@@ -331,8 +450,13 @@ export default class DataFilter extends Plugin {
|
|
|
331
450
|
} );
|
|
332
451
|
conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, this ) );
|
|
333
452
|
|
|
334
|
-
conversion.for( 'editingDowncast' ).
|
|
335
|
-
model:
|
|
453
|
+
conversion.for( 'editingDowncast' ).elementToStructure( {
|
|
454
|
+
model: {
|
|
455
|
+
name: modelName,
|
|
456
|
+
attributes: [
|
|
457
|
+
'htmlAttributes'
|
|
458
|
+
]
|
|
459
|
+
},
|
|
336
460
|
view: toObjectWidgetConverter( editor, definition )
|
|
337
461
|
} );
|
|
338
462
|
|
|
@@ -452,7 +576,7 @@ export default class DataFilter extends Plugin {
|
|
|
452
576
|
//
|
|
453
577
|
// @private
|
|
454
578
|
// @param {module:engine/view/element~Element} viewElement
|
|
455
|
-
// @param {module:engine/conversion/
|
|
579
|
+
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi
|
|
456
580
|
// @param {module:engine/view/matcher~Matcher Matcher} matcher
|
|
457
581
|
// @returns {Object} [result]
|
|
458
582
|
// @returns {Object} result.attributes
|
|
@@ -486,7 +610,7 @@ function consumeAttributes( viewElement, conversionApi, matcher ) {
|
|
|
486
610
|
//
|
|
487
611
|
// @private
|
|
488
612
|
// @param {module:engine/view/element~Element} viewElement
|
|
489
|
-
// @param {module:engine/conversion/
|
|
613
|
+
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi
|
|
490
614
|
// @param {module:engine/view/matcher~Matcher Matcher} matcher
|
|
491
615
|
// @returns {Array.<Object>} Array with match information about found attributes.
|
|
492
616
|
function consumeAttributeMatches( viewElement, { consumable }, matcher ) {
|
|
@@ -499,9 +623,8 @@ function consumeAttributeMatches( viewElement, { consumable }, matcher ) {
|
|
|
499
623
|
// We only want to consume attributes, so element can be still processed by other converters.
|
|
500
624
|
delete match.match.name;
|
|
501
625
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
626
|
+
consumable.consume( viewElement, match.match );
|
|
627
|
+
consumedMatches.push( match );
|
|
505
628
|
}
|
|
506
629
|
|
|
507
630
|
return consumedMatches;
|
|
@@ -511,7 +634,7 @@ function consumeAttributeMatches( viewElement, { consumable }, matcher ) {
|
|
|
511
634
|
//
|
|
512
635
|
// @private
|
|
513
636
|
// @param {module:engine/view/element~Element} viewElement
|
|
514
|
-
// @param {module:engine/conversion/
|
|
637
|
+
// @param {module:engine/conversion/viewconsumable~ViewConsumable} consumable
|
|
515
638
|
// @param {Object} match
|
|
516
639
|
function removeConsumedAttributes( consumable, viewElement, match ) {
|
|
517
640
|
for ( const key of [ 'attributes', 'classes', 'styles' ] ) {
|
|
@@ -521,7 +644,8 @@ function removeConsumedAttributes( consumable, viewElement, match ) {
|
|
|
521
644
|
continue;
|
|
522
645
|
}
|
|
523
646
|
|
|
524
|
-
|
|
647
|
+
// Iterating over a copy of an array so removing items doesn't influence iteration.
|
|
648
|
+
for ( const value of Array.from( attributes ) ) {
|
|
525
649
|
if ( !consumable.test( viewElement, ( { [ key ]: [ value ] } ) ) ) {
|
|
526
650
|
removeItemFromArray( attributes, value );
|
|
527
651
|
}
|
package/src/dataschema.js
CHANGED
|
@@ -251,5 +251,8 @@ function testViewName( pattern, viewName ) {
|
|
|
251
251
|
* @property {Number} [priority] Element priority. Decides in what order elements are wrapped by
|
|
252
252
|
* {@link module:engine/view/downcastwriter~DowncastWriter}.
|
|
253
253
|
* Set by {@link module:html-support/dataschema~DataSchema#registerInlineElement} method.
|
|
254
|
+
* @property {String} [coupledAttribute] The name of the model attribute that generates the same view element. GHS inline attribute
|
|
255
|
+
* will be removed from the model tree as soon as the coupled attribute is removed. See
|
|
256
|
+
* {@link module:html-support/datafilter~DataFilter#_registerModelPostFixer GHS post-fixer} for more details.
|
|
254
257
|
* @extends module:html-support/dataschema~DataSchemaDefinition
|
|
255
258
|
*/
|