@ckeditor/ckeditor5-engine 45.1.0 → 45.2.0-alpha.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/dist/index.js CHANGED
@@ -9012,15 +9012,25 @@ const validNodesToInsert = [
9012
9012
  this._removeFakeSelection();
9013
9013
  return;
9014
9014
  }
9015
- const domRoot = this.domConverter.mapViewToDom(this.selection.editableElement);
9016
- // Do nothing if there is no focus, or there is no DOM element corresponding to selection's editable element.
9017
- if (!this.isFocused || !domRoot) {
9015
+ const domEditable = this.domConverter.mapViewToDom(this.selection.editableElement);
9016
+ // Do not update DOM selection if there is no focus, or there is no DOM element corresponding to selection's editable element.
9017
+ if (!this.isFocused || !domEditable) {
9018
9018
  // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
9019
9019
  // @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'Renderer',
9020
9020
  // @if CK_DEBUG_TYPING // 'Skip updating DOM selection:',
9021
- // @if CK_DEBUG_TYPING // `isFocused: ${ this.isFocused }, hasDomRoot: ${ !!domRoot }`
9021
+ // @if CK_DEBUG_TYPING // `isFocused: ${ this.isFocused }, hasDomEditable: ${ !!domEditable }`
9022
9022
  // @if CK_DEBUG_TYPING // ) );
9023
9023
  // @if CK_DEBUG_TYPING // }
9024
+ // But if there was a fake selection, and it is not fake anymore - remove it as it can map to no longer existing widget.
9025
+ // See https://github.com/ckeditor/ckeditor5/issues/18123.
9026
+ if (!this.selection.isFake && this._fakeSelectionContainer && this._fakeSelectionContainer.isConnected) {
9027
+ // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
9028
+ // @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'Renderer',
9029
+ // @if CK_DEBUG_TYPING // 'Remove fake selection (not focused editable)'
9030
+ // @if CK_DEBUG_TYPING // ) );
9031
+ // @if CK_DEBUG_TYPING // }
9032
+ this._removeFakeSelection();
9033
+ }
9024
9034
  return;
9025
9035
  }
9026
9036
  // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
@@ -9030,31 +9040,31 @@ const validNodesToInsert = [
9030
9040
  // @if CK_DEBUG_TYPING // }
9031
9041
  // Render fake selection - create the fake selection container (if needed) and move DOM selection to it.
9032
9042
  if (this.selection.isFake) {
9033
- this._updateFakeSelection(domRoot);
9043
+ this._updateFakeSelection(domEditable);
9034
9044
  } else if (this._fakeSelectionContainer && this._fakeSelectionContainer.isConnected) {
9035
9045
  this._removeFakeSelection();
9036
- this._updateDomSelection(domRoot);
9046
+ this._updateDomSelection(domEditable);
9037
9047
  } else if (!(this.isComposing && env.isAndroid)) {
9038
- this._updateDomSelection(domRoot);
9048
+ this._updateDomSelection(domEditable);
9039
9049
  }
9040
9050
  }
9041
9051
  /**
9042
9052
  * Updates the fake selection.
9043
9053
  *
9044
- * @param domRoot A valid DOM root where the fake selection container should be added.
9045
- */ _updateFakeSelection(domRoot) {
9046
- const domDocument = domRoot.ownerDocument;
9054
+ * @param domEditable A valid DOM editable where the fake selection container should be added.
9055
+ */ _updateFakeSelection(domEditable) {
9056
+ const domDocument = domEditable.ownerDocument;
9047
9057
  if (!this._fakeSelectionContainer) {
9048
9058
  this._fakeSelectionContainer = createFakeSelectionContainer(domDocument);
9049
9059
  }
9050
9060
  const container = this._fakeSelectionContainer;
9051
9061
  // Bind fake selection container with the current selection *position*.
9052
9062
  this.domConverter.bindFakeSelection(container, this.selection);
9053
- if (!this._fakeSelectionNeedsUpdate(domRoot)) {
9063
+ if (!this._fakeSelectionNeedsUpdate(domEditable)) {
9054
9064
  return;
9055
9065
  }
9056
- if (!container.parentElement || container.parentElement != domRoot) {
9057
- domRoot.appendChild(container);
9066
+ if (!container.parentElement || container.parentElement != domEditable) {
9067
+ domEditable.appendChild(container);
9058
9068
  }
9059
9069
  container.textContent = this.selection.fakeSelectionLabel || '\u00A0';
9060
9070
  // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
@@ -9071,9 +9081,9 @@ const validNodesToInsert = [
9071
9081
  /**
9072
9082
  * Updates the DOM selection.
9073
9083
  *
9074
- * @param domRoot A valid DOM root where the DOM selection should be rendered.
9075
- */ _updateDomSelection(domRoot) {
9076
- const domSelection = domRoot.ownerDocument.defaultView.getSelection();
9084
+ * @param domEditable A valid DOM editable where the DOM selection should be rendered.
9085
+ */ _updateDomSelection(domEditable) {
9086
+ const domSelection = domEditable.ownerDocument.defaultView.getSelection();
9077
9087
  // Let's check whether DOM selection needs updating at all.
9078
9088
  if (!this._domSelectionNeedsUpdate(domSelection)) {
9079
9089
  // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
@@ -9128,13 +9138,13 @@ const validNodesToInsert = [
9128
9138
  /**
9129
9139
  * Checks whether the fake selection needs to be updated.
9130
9140
  *
9131
- * @param domRoot A valid DOM root where a new fake selection container should be added.
9132
- */ _fakeSelectionNeedsUpdate(domRoot) {
9141
+ * @param domEditable A valid DOM editable where a new fake selection container should be added.
9142
+ */ _fakeSelectionNeedsUpdate(domEditable) {
9133
9143
  const container = this._fakeSelectionContainer;
9134
- const domSelection = domRoot.ownerDocument.getSelection();
9144
+ const domSelection = domEditable.ownerDocument.getSelection();
9135
9145
  // Fake selection needs to be updated if there's no fake selection container, or the container currently sits
9136
9146
  // in a different root.
9137
- if (!container || container.parentElement !== domRoot) {
9147
+ if (!container || container.parentElement !== domEditable) {
9138
9148
  return true;
9139
9149
  }
9140
9150
  // Make sure that the selection actually is within the fake selection.
@@ -10265,7 +10275,7 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
10265
10275
  try {
10266
10276
  range.setStart(selection.anchorNode, selection.anchorOffset);
10267
10277
  range.setEnd(selection.focusNode, selection.focusOffset);
10268
- } catch (e) {
10278
+ } catch {
10269
10279
  // Safari sometimes gives us a selection that makes Range.set{Start,End} throw.
10270
10280
  // See https://github.com/ckeditor/ckeditor5/issues/12375.
10271
10281
  return false;
@@ -10853,7 +10863,7 @@ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
10853
10863
  const container = domSelection.getRangeAt(0).startContainer;
10854
10864
  try {
10855
10865
  Object.prototype.toString.call(container);
10856
- } catch (error) {
10866
+ } catch {
10857
10867
  return true;
10858
10868
  }
10859
10869
  return false;
@@ -11784,8 +11794,9 @@ function sameNodes(child1, child2) {
11784
11794
  this.view.hasDomSelection = true;
11785
11795
  // Mark the latest focus change as complete (we got new selection after the focus so the selection is in the focused element).
11786
11796
  this.focusObserver.flush();
11787
- // Ignore selection change as the editable is not focused.
11788
- if (!this.view.document.isFocused) {
11797
+ // Ignore selection change as the editable is not focused. Note that in read-only mode, we have to update
11798
+ // the model selection as there won't be any focus change to flush the pending selection changes.
11799
+ if (!this.view.document.isFocused && !this.view.document.isReadOnly) {
11789
11800
  // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
11790
11801
  // @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'SelectionObserver',
11791
11802
  // @if CK_DEBUG_TYPING // 'Ignore selection change while editable is not focused'
@@ -12068,7 +12079,8 @@ function getFiles(nativeDataTransfer) {
12068
12079
  if (viewStart && startsWithFiller(domRange.startContainer) && domRange.startOffset < INLINE_FILLER_LENGTH) {
12069
12080
  // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
12070
12081
  // @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'InputObserver',
12071
- // @if CK_DEBUG_TYPING // 'Target range starts in an inline filler - adjusting it',
12082
+ // @if CK_DEBUG_TYPING // '%cTarget range starts in an inline filler - adjusting it',
12083
+ // @if CK_DEBUG_TYPING // 'font-style: italic'
12072
12084
  // @if CK_DEBUG_TYPING // ) );
12073
12085
  // @if CK_DEBUG_TYPING // }
12074
12086
  domEvent.preventDefault();
@@ -12088,6 +12100,16 @@ function getFiles(nativeDataTransfer) {
12088
12100
  singleCharacters: true
12089
12101
  });
12090
12102
  }
12103
+ // Check if there is no an inline filler just after the target range.
12104
+ if (isFollowedByInlineFiller(domRange.endContainer, domRange.endOffset)) {
12105
+ // @if CK_DEBUG_TYPING // if ( ( window as any ).logCKETyping ) {
12106
+ // @if CK_DEBUG_TYPING // console.info( ..._buildLogMessage( this, 'InputObserver',
12107
+ // @if CK_DEBUG_TYPING // '%cTarget range ends just before an inline filler - prevent default behavior',
12108
+ // @if CK_DEBUG_TYPING // 'font-style: italic'
12109
+ // @if CK_DEBUG_TYPING // ) );
12110
+ // @if CK_DEBUG_TYPING // }
12111
+ domEvent.preventDefault();
12112
+ }
12091
12113
  if (viewStart) {
12092
12114
  return view.createRange(viewStart, viewEnd);
12093
12115
  } else if (viewEnd) {
@@ -12139,6 +12161,8 @@ function getFiles(nativeDataTransfer) {
12139
12161
  // it to paragraphs as it is our default action for enter handling.
12140
12162
  const parts = data.split(/\n{1,2}/g);
12141
12163
  let partTargetRanges = targetRanges;
12164
+ // Handle all parts on our side as we rely on paragraph inserting and synchronously updated view selection.
12165
+ domEvent.preventDefault();
12142
12166
  for(let i = 0; i < parts.length; i++){
12143
12167
  const dataPart = parts[i];
12144
12168
  if (dataPart != '') {
@@ -12183,6 +12207,28 @@ function getFiles(nativeDataTransfer) {
12183
12207
  // @if CK_DEBUG_TYPING // }
12184
12208
  }
12185
12209
  }
12210
+ /**
12211
+ * Returns `true` if there is an inline filler just after the position in DOM.
12212
+ * It walks up the DOM tree if the offset is at the end of the node.
12213
+ */ function isFollowedByInlineFiller(node, offset) {
12214
+ while(node.parentNode){
12215
+ if (isText(node)) {
12216
+ if (offset != node.data.length) {
12217
+ return false;
12218
+ }
12219
+ } else {
12220
+ if (offset != node.childNodes.length) {
12221
+ return false;
12222
+ }
12223
+ }
12224
+ offset = indexOf(node) + 1;
12225
+ node = node.parentNode;
12226
+ if (offset < node.childNodes.length && startsWithFiller(node.childNodes[offset])) {
12227
+ return true;
12228
+ }
12229
+ }
12230
+ return false;
12231
+ }
12186
12232
 
12187
12233
  /**
12188
12234
  * Arrow keys observer introduces the {@link module:engine/view/document~Document#event:arrowKey `Document#arrowKey`} event.
@@ -39192,7 +39238,7 @@ function convertToModelText() {
39192
39238
  function parseAttributeValue(attribute) {
39193
39239
  try {
39194
39240
  return JSON.parse(attribute);
39195
- } catch (e) {
39241
+ } catch {
39196
39242
  return attribute;
39197
39243
  }
39198
39244
  }