@ckeditor/ckeditor5-engine 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/package.json +22 -22
- package/src/controller/datacontroller.js +58 -66
- package/src/controller/editingcontroller.js +82 -5
- package/src/conversion/conversion.js +14 -13
- package/src/conversion/downcastdispatcher.js +297 -366
- package/src/conversion/downcasthelpers.js +859 -80
- package/src/conversion/mapper.js +104 -59
- package/src/conversion/modelconsumable.js +84 -34
- package/src/conversion/upcastdispatcher.js +33 -6
- package/src/conversion/upcasthelpers.js +21 -2
- package/src/dataprocessor/htmldataprocessor.js +1 -1
- package/src/dev-utils/model.js +13 -11
- package/src/index.js +12 -0
- package/src/model/batch.js +12 -12
- package/src/model/differ.js +114 -68
- package/src/model/document.js +32 -31
- package/src/model/history.js +160 -22
- package/src/model/markercollection.js +28 -4
- package/src/model/model.js +122 -4
- package/src/model/schema.js +79 -10
- package/src/model/treewalker.js +2 -3
- package/src/model/utils/deletecontent.js +15 -2
- package/src/model/utils/findoptimalinsertionrange.js +68 -0
- package/src/model/utils/insertobject.js +173 -0
- package/src/model/utils/modifyselection.js +14 -7
- package/src/model/writer.js +16 -26
- package/src/view/attributeelement.js +0 -10
- package/src/view/document.js +2 -1
- package/src/view/domconverter.js +47 -11
- package/src/view/downcastwriter.js +90 -49
- package/src/view/element.js +0 -27
- package/src/view/emptyelement.js +0 -3
- package/src/view/matcher.js +2 -2
- package/src/view/observer/clickobserver.js +0 -1
- package/src/view/observer/inputobserver.js +1 -1
- package/src/view/observer/tabobserver.js +68 -0
- package/src/view/placeholder.js +1 -1
- package/src/view/rawelement.js +0 -3
- package/src/view/uielement.js +0 -3
- package/src/view/view.js +4 -0
- package/theme/placeholder.css +9 -0
package/src/view/domconverter.js
CHANGED
|
@@ -35,6 +35,7 @@ const NBSP_FILLER_REF = NBSP_FILLER( document ); // eslint-disable-line new-cap
|
|
|
35
35
|
const MARKED_NBSP_FILLER_REF = MARKED_NBSP_FILLER( document ); // eslint-disable-line new-cap
|
|
36
36
|
const UNSAFE_ATTRIBUTE_NAME_PREFIX = 'data-ck-unsafe-attribute-';
|
|
37
37
|
const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
|
|
38
|
+
const UNSAFE_ELEMENTS = [ 'script', 'style' ];
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* `DomConverter` is a set of tools to do transformations between DOM nodes and view nodes. It also handles
|
|
@@ -323,7 +324,7 @@ export default class DomConverter {
|
|
|
323
324
|
|
|
324
325
|
// There are certain nodes, that should be renamed to <span> in editing pipeline.
|
|
325
326
|
if ( this._shouldRenameElement( elementName ) ) {
|
|
326
|
-
|
|
327
|
+
_logUnsafeElement( elementName );
|
|
327
328
|
|
|
328
329
|
currentNode.replaceWith( this._createReplacementDomElement( elementName, currentNode ) );
|
|
329
330
|
}
|
|
@@ -384,7 +385,7 @@ export default class DomConverter {
|
|
|
384
385
|
} else {
|
|
385
386
|
// Create DOM element.
|
|
386
387
|
if ( this._shouldRenameElement( viewNode.name ) ) {
|
|
387
|
-
|
|
388
|
+
_logUnsafeElement( viewNode.name );
|
|
388
389
|
|
|
389
390
|
domElement = this._createReplacementDomElement( viewNode.name );
|
|
390
391
|
} else if ( viewNode.hasAttribute( 'xmlns' ) ) {
|
|
@@ -428,7 +429,7 @@ export default class DomConverter {
|
|
|
428
429
|
* @param {String} key The name of the attribute.
|
|
429
430
|
* @param {String} value The value of the attribute.
|
|
430
431
|
* @param {module:engine/view/element~Element} [relatedViewElement] The view element related to the `domElement` (if there is any).
|
|
431
|
-
* It helps decide whether the attribute set is unsafe. For instance, view elements created via
|
|
432
|
+
* It helps decide whether the attribute set is unsafe. For instance, view elements created via the
|
|
432
433
|
* {@link module:engine/view/downcastwriter~DowncastWriter} methods can allow certain attributes that would normally be filtered out.
|
|
433
434
|
*/
|
|
434
435
|
setDomElementAttribute( domElement, key, value, relatedViewElement = null ) {
|
|
@@ -492,7 +493,22 @@ export default class DomConverter {
|
|
|
492
493
|
yield this._getBlockFiller( domDocument );
|
|
493
494
|
}
|
|
494
495
|
|
|
495
|
-
|
|
496
|
+
const transparentRendering = childView.is( 'element' ) && childView.getCustomProperty( 'dataPipeline:transparentRendering' );
|
|
497
|
+
|
|
498
|
+
if ( transparentRendering && this.renderingMode == 'data' ) {
|
|
499
|
+
yield* this.viewChildrenToDom( childView, domDocument, options );
|
|
500
|
+
} else {
|
|
501
|
+
if ( transparentRendering ) {
|
|
502
|
+
/**
|
|
503
|
+
* The `dataPipeline:transparentRendering` flag is supported only in the data pipeline.
|
|
504
|
+
*
|
|
505
|
+
* @error domconverter-transparent-rendering-unsupported-in-editing-pipeline
|
|
506
|
+
*/
|
|
507
|
+
logWarning( 'domconverter-transparent-rendering-unsupported-in-editing-pipeline', { viewElement: childView } );
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
yield this.viewToDom( childView, domDocument, options );
|
|
511
|
+
}
|
|
496
512
|
|
|
497
513
|
offset++;
|
|
498
514
|
}
|
|
@@ -1546,7 +1562,9 @@ export default class DomConverter {
|
|
|
1546
1562
|
* @returns {Boolean}
|
|
1547
1563
|
*/
|
|
1548
1564
|
_shouldRenameElement( elementName ) {
|
|
1549
|
-
|
|
1565
|
+
const name = elementName.toLowerCase();
|
|
1566
|
+
|
|
1567
|
+
return this.renderingMode === 'editing' && UNSAFE_ELEMENTS.includes( name );
|
|
1550
1568
|
}
|
|
1551
1569
|
|
|
1552
1570
|
/**
|
|
@@ -1626,6 +1644,20 @@ function hasBlockParent( domNode, blockElements ) {
|
|
|
1626
1644
|
return parent && parent.tagName && blockElements.includes( parent.tagName.toLowerCase() );
|
|
1627
1645
|
}
|
|
1628
1646
|
|
|
1647
|
+
// Log to console the information about element that was replaced.
|
|
1648
|
+
// Check UNSAFE_ELEMENTS for all recognized unsafe elements.
|
|
1649
|
+
//
|
|
1650
|
+
// @param {String} elementName The name of the view element
|
|
1651
|
+
function _logUnsafeElement( elementName ) {
|
|
1652
|
+
if ( elementName === 'script' ) {
|
|
1653
|
+
logWarning( 'domconverter-unsafe-script-element-detected' );
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
if ( elementName === 'style' ) {
|
|
1657
|
+
logWarning( 'domconverter-unsafe-style-element-detected' );
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1629
1661
|
/**
|
|
1630
1662
|
* Enum representing the type of the block filler.
|
|
1631
1663
|
*
|
|
@@ -1640,13 +1672,17 @@ function hasBlockParent( domNode, blockElements ) {
|
|
|
1640
1672
|
*/
|
|
1641
1673
|
|
|
1642
1674
|
/**
|
|
1643
|
-
*
|
|
1644
|
-
*
|
|
1645
|
-
*
|
|
1675
|
+
* While rendering the editor content, the {@link module:engine/view/domconverter~DomConverter} detected a `<script>` element that may
|
|
1676
|
+
* disrupt the editing experience. To avoid this, the `<script>` element was replaced with `<span data-ck-unsafe-element="script"></span>`.
|
|
1677
|
+
*
|
|
1678
|
+
* @error domconverter-unsafe-script-element-detected
|
|
1679
|
+
*/
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* While rendering the editor content, the {@link module:engine/view/domconverter~DomConverter} detected a `<style>` element that may affect
|
|
1683
|
+
* the editing experience. To avoid this, the `<style>` element was replaced with `<span data-ck-unsafe-element="style"></span>`.
|
|
1646
1684
|
*
|
|
1647
|
-
* @error domconverter-unsafe-element-detected
|
|
1648
|
-
* @param {module:engine/model/element~Element|HTMLElement} unsafeElement The editing view or DOM element
|
|
1649
|
-
* that was renamed.
|
|
1685
|
+
* @error domconverter-unsafe-style-element-detected
|
|
1650
1686
|
*/
|
|
1651
1687
|
|
|
1652
1688
|
/**
|
|
@@ -58,6 +58,14 @@ export default class DowncastWriter {
|
|
|
58
58
|
* @type {Map.<String,Set>}
|
|
59
59
|
*/
|
|
60
60
|
this._cloneGroups = new Map();
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The slot factory used by the `elementToStructure` downcast helper.
|
|
64
|
+
*
|
|
65
|
+
* @private
|
|
66
|
+
* @type {Function|null}
|
|
67
|
+
*/
|
|
68
|
+
this._slotFactory = null;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/**
|
|
@@ -177,10 +185,6 @@ export default class DowncastWriter {
|
|
|
177
185
|
* // Set `id` of a marker element so it is not joined or merged with "normal" elements.
|
|
178
186
|
* writer.createAttributeElement( 'span', { class: 'my-marker' }, { id: 'marker:my' } );
|
|
179
187
|
*
|
|
180
|
-
* **Note:** By default an `AttributeElement` is split by a
|
|
181
|
-
* {@link module:engine/view/containerelement~ContainerElement `ContainerElement`} but this behavior can be modified
|
|
182
|
-
* with `isAllowedInsideAttributeElement` option set while {@link #createContainerElement creating the element}.
|
|
183
|
-
*
|
|
184
188
|
* @param {String} name Name of the element.
|
|
185
189
|
* @param {Object} [attributes] Element's attributes.
|
|
186
190
|
* @param {Object} [options] Element's options.
|
|
@@ -222,23 +226,36 @@ export default class DowncastWriter {
|
|
|
222
226
|
* // Create element with custom classes.
|
|
223
227
|
* writer.createContainerElement( 'p', { class: 'foo bar baz' } );
|
|
224
228
|
*
|
|
229
|
+
* // Create element with children.
|
|
230
|
+
* writer.createContainerElement( 'figure', { class: 'image' }, [
|
|
231
|
+
* writer.createEmptyElement( 'img' ),
|
|
232
|
+
* writer.createContainerElement( 'figcaption' )
|
|
233
|
+
* ] );
|
|
234
|
+
*
|
|
235
|
+
* // Create element with specific options.
|
|
236
|
+
* writer.createContainerElement( 'span', { class: 'placeholder' }, { renderUnsafeAttributes: [ 'foo' ] } );
|
|
237
|
+
*
|
|
225
238
|
* @param {String} name Name of the element.
|
|
226
239
|
* @param {Object} [attributes] Elements attributes.
|
|
240
|
+
* @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>|Object} [childrenOrOptions]
|
|
241
|
+
* A node or a list of nodes to be inserted into the created element. If no children were specified, element's `options`
|
|
242
|
+
* can be passed in this argument.
|
|
227
243
|
* @param {Object} [options] Element's options.
|
|
228
|
-
* @param {Boolean} [options.isAllowedInsideAttributeElement=false] Whether an element is
|
|
229
|
-
* {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped
|
|
230
|
-
* with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.
|
|
231
244
|
* @param {Array.<String>} [options.renderUnsafeAttributes] A list of attribute names that should be rendered in the editing
|
|
232
245
|
* pipeline even though they would normally be filtered out by unsafe attribute detection mechanisms.
|
|
233
246
|
* @returns {module:engine/view/containerelement~ContainerElement} Created element.
|
|
234
247
|
*/
|
|
235
|
-
createContainerElement( name, attributes, options = {} ) {
|
|
236
|
-
|
|
248
|
+
createContainerElement( name, attributes, childrenOrOptions = {}, options = {} ) {
|
|
249
|
+
let children = null;
|
|
237
250
|
|
|
238
|
-
if (
|
|
239
|
-
|
|
251
|
+
if ( isPlainObject( childrenOrOptions ) ) {
|
|
252
|
+
options = childrenOrOptions;
|
|
253
|
+
} else {
|
|
254
|
+
children = childrenOrOptions;
|
|
240
255
|
}
|
|
241
256
|
|
|
257
|
+
const containerElement = new ContainerElement( this.document, name, attributes, children );
|
|
258
|
+
|
|
242
259
|
if ( options.renderUnsafeAttributes ) {
|
|
243
260
|
containerElement._unsafeAttributesToRender.push( ...options.renderUnsafeAttributes );
|
|
244
261
|
}
|
|
@@ -282,9 +299,6 @@ export default class DowncastWriter {
|
|
|
282
299
|
* @param {String} name Name of the element.
|
|
283
300
|
* @param {Object} [attributes] Elements attributes.
|
|
284
301
|
* @param {Object} [options] Element's options.
|
|
285
|
-
* @param {Boolean} [options.isAllowedInsideAttributeElement=true] Whether an element is
|
|
286
|
-
* {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped
|
|
287
|
-
* with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.
|
|
288
302
|
* @param {Array.<String>} [options.renderUnsafeAttributes] A list of attribute names that should be rendered in the editing
|
|
289
303
|
* pipeline even though they would normally be filtered out by unsafe attribute detection mechanisms.
|
|
290
304
|
* @returns {module:engine/view/emptyelement~EmptyElement} Created element.
|
|
@@ -292,10 +306,6 @@ export default class DowncastWriter {
|
|
|
292
306
|
createEmptyElement( name, attributes, options = {} ) {
|
|
293
307
|
const emptyElement = new EmptyElement( this.document, name, attributes );
|
|
294
308
|
|
|
295
|
-
if ( options.isAllowedInsideAttributeElement !== undefined ) {
|
|
296
|
-
emptyElement._isAllowedInsideAttributeElement = options.isAllowedInsideAttributeElement;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
309
|
if ( options.renderUnsafeAttributes ) {
|
|
300
310
|
emptyElement._unsafeAttributesToRender.push( ...options.renderUnsafeAttributes );
|
|
301
311
|
}
|
|
@@ -326,23 +336,15 @@ export default class DowncastWriter {
|
|
|
326
336
|
* @param {String} name The name of the element.
|
|
327
337
|
* @param {Object} [attributes] Element attributes.
|
|
328
338
|
* @param {Function} [renderFunction] A custom render function.
|
|
329
|
-
* @param {Object} [options] Element's options.
|
|
330
|
-
* @param {Boolean} [options.isAllowedInsideAttributeElement=true] Whether an element is
|
|
331
|
-
* {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped
|
|
332
|
-
* with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.
|
|
333
339
|
* @returns {module:engine/view/uielement~UIElement} The created element.
|
|
334
340
|
*/
|
|
335
|
-
createUIElement( name, attributes, renderFunction
|
|
341
|
+
createUIElement( name, attributes, renderFunction ) {
|
|
336
342
|
const uiElement = new UIElement( this.document, name, attributes );
|
|
337
343
|
|
|
338
344
|
if ( renderFunction ) {
|
|
339
345
|
uiElement.render = renderFunction;
|
|
340
346
|
}
|
|
341
347
|
|
|
342
|
-
if ( options.isAllowedInsideAttributeElement !== undefined ) {
|
|
343
|
-
uiElement._isAllowedInsideAttributeElement = options.isAllowedInsideAttributeElement;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
348
|
return uiElement;
|
|
347
349
|
}
|
|
348
350
|
|
|
@@ -369,9 +371,6 @@ export default class DowncastWriter {
|
|
|
369
371
|
* @param {Object} [attributes] Element attributes.
|
|
370
372
|
* @param {Function} [renderFunction] A custom render function.
|
|
371
373
|
* @param {Object} [options] Element's options.
|
|
372
|
-
* @param {Boolean} [options.isAllowedInsideAttributeElement=true] Whether an element is
|
|
373
|
-
* {@link module:engine/view/element~Element#isAllowedInsideAttributeElement allowed inside an AttributeElement} and can be wrapped
|
|
374
|
-
* with {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.
|
|
375
374
|
* @param {Array.<String>} [options.renderUnsafeAttributes] A list of attribute names that should be rendered in the editing
|
|
376
375
|
* pipeline even though they would normally be filtered out by unsafe attribute detection mechanisms.
|
|
377
376
|
* @returns {module:engine/view/rawelement~RawElement} The created element.
|
|
@@ -381,10 +380,6 @@ export default class DowncastWriter {
|
|
|
381
380
|
|
|
382
381
|
rawElement.render = renderFunction || ( () => {} );
|
|
383
382
|
|
|
384
|
-
if ( options.isAllowedInsideAttributeElement !== undefined ) {
|
|
385
|
-
rawElement._isAllowedInsideAttributeElement = options.isAllowedInsideAttributeElement;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
383
|
if ( options.renderUnsafeAttributes ) {
|
|
389
384
|
rawElement._unsafeAttributesToRender.push( ...options.renderUnsafeAttributes );
|
|
390
385
|
}
|
|
@@ -762,7 +757,7 @@ export default class DowncastWriter {
|
|
|
762
757
|
|
|
763
758
|
// Break attributes on nodes that do exist in the model tree so they can have attributes, other elements
|
|
764
759
|
// can't have an attribute in model and won't get wrapped with an AttributeElement while down-casted.
|
|
765
|
-
const breakAttributes = !
|
|
760
|
+
const breakAttributes = !node.is( 'uiElement' );
|
|
766
761
|
|
|
767
762
|
if ( !lastGroup || lastGroup.breakAttributes != breakAttributes ) {
|
|
768
763
|
groups.push( {
|
|
@@ -951,16 +946,6 @@ export default class DowncastWriter {
|
|
|
951
946
|
* Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-nonselection-collapsed-range` when passed range
|
|
952
947
|
* is collapsed and different than view selection.
|
|
953
948
|
*
|
|
954
|
-
* **Note:** Attribute elements by default can wrap {@link module:engine/view/text~Text},
|
|
955
|
-
* {@link module:engine/view/emptyelement~EmptyElement}, {@link module:engine/view/uielement~UIElement},
|
|
956
|
-
* {@link module:engine/view/rawelement~RawElement} and other attribute elements with higher priority. Other elements while placed
|
|
957
|
-
* inside an attribute element will split it (or nest it in case of an `AttributeElement`). This behavior can be modified by changing
|
|
958
|
-
* the `isAllowedInsideAttributeElement` option while using
|
|
959
|
-
* {@link module:engine/view/downcastwriter~DowncastWriter#createContainerElement},
|
|
960
|
-
* {@link module:engine/view/downcastwriter~DowncastWriter#createEmptyElement},
|
|
961
|
-
* {@link module:engine/view/downcastwriter~DowncastWriter#createUIElement} or
|
|
962
|
-
* {@link module:engine/view/downcastwriter~DowncastWriter#createRawElement}.
|
|
963
|
-
*
|
|
964
949
|
* @param {module:engine/view/range~Range} range Range to wrap.
|
|
965
950
|
* @param {module:engine/view/attributeelement~AttributeElement} attribute Attribute element to use as wrapper.
|
|
966
951
|
* @returns {module:engine/view/range~Range} range Range after wrapping, spanning over wrapping attribute element.
|
|
@@ -1167,7 +1152,7 @@ export default class DowncastWriter {
|
|
|
1167
1152
|
}
|
|
1168
1153
|
|
|
1169
1154
|
/**
|
|
1170
|
-
Creates new {@link module:engine/view/selection~Selection} instance.
|
|
1155
|
+
* Creates new {@link module:engine/view/selection~Selection} instance.
|
|
1171
1156
|
*
|
|
1172
1157
|
* // Creates empty selection without ranges.
|
|
1173
1158
|
* const selection = writer.createSelection();
|
|
@@ -1230,6 +1215,63 @@ export default class DowncastWriter {
|
|
|
1230
1215
|
return new Selection( selectable, placeOrOffset, options );
|
|
1231
1216
|
}
|
|
1232
1217
|
|
|
1218
|
+
/**
|
|
1219
|
+
* Creates placeholders for child elements of the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure
|
|
1220
|
+
* `elementToStructure()`} conversion helper.
|
|
1221
|
+
*
|
|
1222
|
+
* const viewSlot = conversionApi.writer.createSlot();
|
|
1223
|
+
* const viewPosition = conversionApi.writer.createPositionAt( viewElement, 0 );
|
|
1224
|
+
*
|
|
1225
|
+
* conversionApi.writer.insert( viewPosition, viewSlot );
|
|
1226
|
+
*
|
|
1227
|
+
* It could be filtered down to a specific subset of children (only `<foo>` model elements in this case):
|
|
1228
|
+
*
|
|
1229
|
+
* const viewSlot = conversionApi.writer.createSlot( node => node.is( 'element', 'foo' ) );
|
|
1230
|
+
* const viewPosition = conversionApi.writer.createPositionAt( viewElement, 0 );
|
|
1231
|
+
*
|
|
1232
|
+
* conversionApi.writer.insert( viewPosition, viewSlot );
|
|
1233
|
+
*
|
|
1234
|
+
* While providing a filtered slot, make sure to provide slots for all child nodes. A single node can not be downcasted into
|
|
1235
|
+
* multiple slots.
|
|
1236
|
+
*
|
|
1237
|
+
* **Note**: You should not change the order of nodes. View elements should be in the same order as model nodes.
|
|
1238
|
+
*
|
|
1239
|
+
* @param {'children'|module:engine/conversion/downcasthelpers~SlotFilter} [modeOrFilter='children'] The filter for child nodes.
|
|
1240
|
+
* @returns {module:engine/view/element~Element} The slot element to be placed in to the view structure while processing
|
|
1241
|
+
* {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure `elementToStructure()`}.
|
|
1242
|
+
*/
|
|
1243
|
+
createSlot( modeOrFilter ) {
|
|
1244
|
+
if ( !this._slotFactory ) {
|
|
1245
|
+
/**
|
|
1246
|
+
* The `createSlot()` method is only allowed inside the `elementToStructure` downcast helper callback.
|
|
1247
|
+
*
|
|
1248
|
+
* @error view-writer-invalid-create-slot-context
|
|
1249
|
+
*/
|
|
1250
|
+
throw new CKEditorError( 'view-writer-invalid-create-slot-context', this.document );
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return this._slotFactory( this, modeOrFilter );
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* Registers a slot factory.
|
|
1258
|
+
*
|
|
1259
|
+
* @protected
|
|
1260
|
+
* @param {Function} slotFactory The slot factory.
|
|
1261
|
+
*/
|
|
1262
|
+
_registerSlotFactory( slotFactory ) {
|
|
1263
|
+
this._slotFactory = slotFactory;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Clears the registered slot factory.
|
|
1268
|
+
*
|
|
1269
|
+
* @protected
|
|
1270
|
+
*/
|
|
1271
|
+
_clearSlotFactory() {
|
|
1272
|
+
this._slotFactory = null;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1233
1275
|
/**
|
|
1234
1276
|
* Inserts a node or nodes at the specified position. Takes care of breaking attributes before insertion
|
|
1235
1277
|
* and merging them afterwards if requested by the breakAttributes param.
|
|
@@ -1314,7 +1356,6 @@ export default class DowncastWriter {
|
|
|
1314
1356
|
const child = parent.getChild( i );
|
|
1315
1357
|
const isText = child.is( '$text' );
|
|
1316
1358
|
const isAttribute = child.is( 'attributeElement' );
|
|
1317
|
-
const isAllowedInsideAttributeElement = child.isAllowedInsideAttributeElement;
|
|
1318
1359
|
|
|
1319
1360
|
//
|
|
1320
1361
|
// (In all examples, assume that `wrapElement` is `<span class="foo">` element.)
|
|
@@ -1333,7 +1374,7 @@ export default class DowncastWriter {
|
|
|
1333
1374
|
//
|
|
1334
1375
|
// <p>abc</p> --> <p><span class="foo">abc</span></p>
|
|
1335
1376
|
// <p><strong>abc</strong></p> --> <p><span class="foo"><strong>abc</strong></span></p>
|
|
1336
|
-
else if ( isText ||
|
|
1377
|
+
else if ( isText || !isAttribute || shouldABeOutsideB( wrapElement, child ) ) {
|
|
1337
1378
|
// Clone attribute.
|
|
1338
1379
|
const newAttribute = wrapElement._clone();
|
|
1339
1380
|
|
|
@@ -1351,7 +1392,7 @@ export default class DowncastWriter {
|
|
|
1351
1392
|
//
|
|
1352
1393
|
// <p><a href="foo.html">abc</a></p> --> <p><a href="foo.html"><span class="foo">abc</span></a></p>
|
|
1353
1394
|
//
|
|
1354
|
-
else if ( isAttribute ) {
|
|
1395
|
+
else /* if ( isAttribute ) */ {
|
|
1355
1396
|
this._wrapChildren( child, 0, child.childCount, wrapElement );
|
|
1356
1397
|
}
|
|
1357
1398
|
|
package/src/view/element.js
CHANGED
|
@@ -130,15 +130,6 @@ export default class Element extends Node {
|
|
|
130
130
|
*/
|
|
131
131
|
this._customProperties = new Map();
|
|
132
132
|
|
|
133
|
-
/**
|
|
134
|
-
* Whether an element is allowed inside an AttributeElement and can be wrapped with
|
|
135
|
-
* {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.
|
|
136
|
-
*
|
|
137
|
-
* @protected
|
|
138
|
-
* @member {Boolean}
|
|
139
|
-
*/
|
|
140
|
-
this._isAllowedInsideAttributeElement = false;
|
|
141
|
-
|
|
142
133
|
/**
|
|
143
134
|
* A list of attribute names that should be rendered in the editing pipeline even though filtering mechanisms
|
|
144
135
|
* implemented in the {@link module:engine/view/domconverter~DomConverter} (for instance,
|
|
@@ -175,17 +166,6 @@ export default class Element extends Node {
|
|
|
175
166
|
return this._children.length === 0;
|
|
176
167
|
}
|
|
177
168
|
|
|
178
|
-
/**
|
|
179
|
-
* Whether the element is allowed inside an AttributeElement and can be wrapped with
|
|
180
|
-
* {@link module:engine/view/attributeelement~AttributeElement} by {@link module:engine/view/downcastwriter~DowncastWriter}.
|
|
181
|
-
*
|
|
182
|
-
* @readonly
|
|
183
|
-
* @type {Boolean}
|
|
184
|
-
*/
|
|
185
|
-
get isAllowedInsideAttributeElement() {
|
|
186
|
-
return this._isAllowedInsideAttributeElement;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
169
|
/**
|
|
190
170
|
* Checks whether this object is of the given.
|
|
191
171
|
*
|
|
@@ -350,11 +330,6 @@ export default class Element extends Node {
|
|
|
350
330
|
return false;
|
|
351
331
|
}
|
|
352
332
|
|
|
353
|
-
// Check isAllowedInsideAttributeElement property.
|
|
354
|
-
if ( this.isAllowedInsideAttributeElement != otherElement.isAllowedInsideAttributeElement ) {
|
|
355
|
-
return false;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
333
|
// Check number of attributes, classes and styles.
|
|
359
334
|
if ( this._attrs.size !== otherElement._attrs.size || this._classes.size !== otherElement._classes.size ||
|
|
360
335
|
this._styles.size !== otherElement._styles.size ) {
|
|
@@ -633,8 +608,6 @@ export default class Element extends Node {
|
|
|
633
608
|
// is changed by e.g. toWidget() function from ckeditor5-widget. Perhaps this should be one of custom props.
|
|
634
609
|
cloned.getFillerOffset = this.getFillerOffset;
|
|
635
610
|
|
|
636
|
-
cloned._isAllowedInsideAttributeElement = this.isAllowedInsideAttributeElement;
|
|
637
|
-
|
|
638
611
|
return cloned;
|
|
639
612
|
}
|
|
640
613
|
|
package/src/view/emptyelement.js
CHANGED
|
@@ -37,9 +37,6 @@ export default class EmptyElement extends Element {
|
|
|
37
37
|
constructor( document, name, attrs, children ) {
|
|
38
38
|
super( document, name, attrs, children );
|
|
39
39
|
|
|
40
|
-
// Override the default of the base class.
|
|
41
|
-
this._isAllowedInsideAttributeElement = true;
|
|
42
|
-
|
|
43
40
|
/**
|
|
44
41
|
* Returns `null` because filler is not needed for EmptyElements.
|
|
45
42
|
*
|
package/src/view/matcher.js
CHANGED
|
@@ -739,7 +739,7 @@ function matchStyles( patterns, element ) {
|
|
|
739
739
|
* styles: /^border.*$/
|
|
740
740
|
* }
|
|
741
741
|
*
|
|
742
|
-
* Refer to the {@glink
|
|
742
|
+
* Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
|
|
743
743
|
* and {@link module:engine/view/matcher~MatcherPattern} documentation.
|
|
744
744
|
*
|
|
745
745
|
* @param {Object} pattern Pattern with missing properties.
|
|
@@ -769,7 +769,7 @@ function matchStyles( patterns, element ) {
|
|
|
769
769
|
* classes: 'foobar'
|
|
770
770
|
* }
|
|
771
771
|
*
|
|
772
|
-
* Refer to the {@glink
|
|
772
|
+
* Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
|
|
773
773
|
* and the {@link module:engine/view/matcher~MatcherPattern} documentation.
|
|
774
774
|
*
|
|
775
775
|
* @param {Object} pattern Pattern with missing properties.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module engine/view/observer/tabobserver
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import Observer from './observer';
|
|
11
|
+
import BubblingEventInfo from './bubblingeventinfo';
|
|
12
|
+
|
|
13
|
+
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Tab observer introduces the {@link module:engine/view/document~Document#event:tab `Document#tab`} event.
|
|
17
|
+
*
|
|
18
|
+
* Note that because {@link module:engine/view/observer/tabobserver~TabObserver} is attached by the
|
|
19
|
+
* {@link module:engine/view/view~View}, this event is available by default.
|
|
20
|
+
*
|
|
21
|
+
* @extends module:engine/view/observer/observer~Observer
|
|
22
|
+
*/
|
|
23
|
+
export default class TabObserver extends Observer {
|
|
24
|
+
/**
|
|
25
|
+
* @inheritDoc
|
|
26
|
+
*/
|
|
27
|
+
constructor( view ) {
|
|
28
|
+
super( view );
|
|
29
|
+
|
|
30
|
+
const doc = this.document;
|
|
31
|
+
|
|
32
|
+
doc.on( 'keydown', ( evt, data ) => {
|
|
33
|
+
if (
|
|
34
|
+
!this.isEnabled ||
|
|
35
|
+
data.keyCode != keyCodes.tab ||
|
|
36
|
+
data.ctrlKey
|
|
37
|
+
) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const event = new BubblingEventInfo( doc, 'tab', doc.selection.getFirstRange() );
|
|
42
|
+
|
|
43
|
+
doc.fire( event, data );
|
|
44
|
+
|
|
45
|
+
if ( event.stop.called ) {
|
|
46
|
+
evt.stop();
|
|
47
|
+
}
|
|
48
|
+
} );
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @inheritDoc
|
|
53
|
+
*/
|
|
54
|
+
observe() {}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Event fired when the user presses a tab key.
|
|
59
|
+
*
|
|
60
|
+
* Introduced by {@link module:engine/view/observer/tabobserver~TabObserver}.
|
|
61
|
+
*
|
|
62
|
+
* Note that because {@link module:engine/view/observer/tabobserver~TabObserver} is attached by the
|
|
63
|
+
* {@link module:engine/view/view~View}, this event is available by default.
|
|
64
|
+
*
|
|
65
|
+
* @event module:engine/view/document~Document#event:tab
|
|
66
|
+
*
|
|
67
|
+
* @param {module:engine/view/observer/domeventdata~DomEventData} data
|
|
68
|
+
*/
|
package/src/view/placeholder.js
CHANGED
|
@@ -276,7 +276,7 @@ function getChildPlaceholderHostSubstitute( parent ) {
|
|
|
276
276
|
if ( parent.childCount ) {
|
|
277
277
|
const firstChild = parent.getChild( 0 );
|
|
278
278
|
|
|
279
|
-
if ( firstChild.is( 'element' ) && !firstChild.is( 'uiElement' ) ) {
|
|
279
|
+
if ( firstChild.is( 'element' ) && !firstChild.is( 'uiElement' ) && !firstChild.is( 'attributeElement' ) ) {
|
|
280
280
|
return firstChild;
|
|
281
281
|
}
|
|
282
282
|
}
|
package/src/view/rawelement.js
CHANGED
|
@@ -47,9 +47,6 @@ export default class RawElement extends Element {
|
|
|
47
47
|
constructor( document, name, attrs, children ) {
|
|
48
48
|
super( document, name, attrs, children );
|
|
49
49
|
|
|
50
|
-
// Override the default of the base class.
|
|
51
|
-
this._isAllowedInsideAttributeElement = true;
|
|
52
|
-
|
|
53
50
|
/**
|
|
54
51
|
* Returns `null` because filler is not needed for raw elements.
|
|
55
52
|
*
|
package/src/view/uielement.js
CHANGED
|
@@ -50,9 +50,6 @@ export default class UIElement extends Element {
|
|
|
50
50
|
constructor( document, name, attributes, children ) {
|
|
51
51
|
super( document, name, attributes, children );
|
|
52
52
|
|
|
53
|
-
// Override the default of the base class.
|
|
54
|
-
this._isAllowedInsideAttributeElement = true;
|
|
55
|
-
|
|
56
53
|
/**
|
|
57
54
|
* Returns `null` because filler is not needed for UIElements.
|
|
58
55
|
*
|
package/src/view/view.js
CHANGED
|
@@ -23,6 +23,7 @@ import FocusObserver from './observer/focusobserver';
|
|
|
23
23
|
import CompositionObserver from './observer/compositionobserver';
|
|
24
24
|
import InputObserver from './observer/inputobserver';
|
|
25
25
|
import ArrowKeysObserver from './observer/arrowkeysobserver';
|
|
26
|
+
import TabObserver from './observer/tabobserver';
|
|
26
27
|
|
|
27
28
|
import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
|
|
28
29
|
import mix from '@ckeditor/ckeditor5-utils/src/mix';
|
|
@@ -54,6 +55,8 @@ import env from '@ckeditor/ckeditor5-utils/src/env';
|
|
|
54
55
|
* * {@link module:engine/view/observer/keyobserver~KeyObserver},
|
|
55
56
|
* * {@link module:engine/view/observer/fakeselectionobserver~FakeSelectionObserver}.
|
|
56
57
|
* * {@link module:engine/view/observer/compositionobserver~CompositionObserver}.
|
|
58
|
+
* * {@link module:engine/view/observer/arrowkeysobserver~ArrowKeysObserver}.
|
|
59
|
+
* * {@link module:engine/view/observer/tabobserver~TabObserver}.
|
|
57
60
|
*
|
|
58
61
|
* This class also {@link module:engine/view/view~View#attachDomRoot binds the DOM and the view elements}.
|
|
59
62
|
*
|
|
@@ -186,6 +189,7 @@ export default class View {
|
|
|
186
189
|
this.addObserver( FakeSelectionObserver );
|
|
187
190
|
this.addObserver( CompositionObserver );
|
|
188
191
|
this.addObserver( ArrowKeysObserver );
|
|
192
|
+
this.addObserver( TabObserver );
|
|
189
193
|
|
|
190
194
|
if ( env.isAndroid ) {
|
|
191
195
|
this.addObserver( InputObserver );
|
package/theme/placeholder.css
CHANGED
|
@@ -25,3 +25,12 @@
|
|
|
25
25
|
display: none;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* Rules for the `ck-placeholder` are loaded before the rules for `ck-reset_all` in the base CKEditor 5 DLL build.
|
|
31
|
+
* This fix overwrites the incorrectly set `position: static` from `ck-reset_all`.
|
|
32
|
+
* See https://github.com/ckeditor/ckeditor5/issues/11418.
|
|
33
|
+
*/
|
|
34
|
+
.ck.ck-reset_all .ck-placeholder {
|
|
35
|
+
position: relative;
|
|
36
|
+
}
|