@ckeditor/ckeditor5-engine 46.1.1 → 47.0.0-alpha.1

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
@@ -5800,7 +5800,9 @@ ViewDocumentSelection.prototype.is = function(type) {
5800
5800
  }
5801
5801
  }
5802
5802
 
5803
- const contextsSymbol = Symbol('bubbling contexts');
5803
+ const bubblingEmitterSymbol = Symbol('bubblingEmitter');
5804
+ const callbackMapSymbol = Symbol('bubblingCallbacks');
5805
+ const contextsSymbol = Symbol('bubblingContexts');
5804
5806
  /**
5805
5807
  * Bubbling emitter mixin for the view document as described in the {@link ~BubblingEmitter} interface.
5806
5808
  *
@@ -5821,40 +5823,27 @@ const contextsSymbol = Symbol('bubbling contexts');
5821
5823
  fire(eventOrInfo, ...eventArgs) {
5822
5824
  try {
5823
5825
  const eventInfo = eventOrInfo instanceof EventInfo ? eventOrInfo : new EventInfo(this, eventOrInfo);
5824
- const eventContexts = getBubblingContexts(this);
5825
- if (!eventContexts.size) {
5826
- return;
5827
- }
5826
+ const bubblingEmitter = getBubblingEmitter(this);
5827
+ const customContexts = getCustomContexts(this);
5828
5828
  updateEventInfo(eventInfo, 'capturing', this);
5829
5829
  // The capture phase of the event.
5830
- if (fireListenerFor(eventContexts, '$capture', eventInfo, ...eventArgs)) {
5830
+ if (fireListenerFor(bubblingEmitter, '$capture', eventInfo, ...eventArgs)) {
5831
5831
  return eventInfo.return;
5832
5832
  }
5833
5833
  const startRange = eventInfo.startRange || this.selection.getFirstRange();
5834
5834
  const selectedElement = startRange ? startRange.getContainedElement() : null;
5835
- const isCustomContext = selectedElement ? Boolean(getCustomContext(eventContexts, selectedElement)) : false;
5835
+ const isCustomContext = selectedElement ? hasMatchingCustomContext(customContexts, selectedElement) : false;
5836
5836
  let node = selectedElement || getDeeperRangeParent(startRange);
5837
5837
  updateEventInfo(eventInfo, 'atTarget', node);
5838
5838
  // For the not yet bubbling event trigger for $text node if selection can be there and it's not a custom context selected.
5839
5839
  if (!isCustomContext) {
5840
- if (fireListenerFor(eventContexts, '$text', eventInfo, ...eventArgs)) {
5840
+ if (fireListenerFor(bubblingEmitter, '$text', eventInfo, ...eventArgs)) {
5841
5841
  return eventInfo.return;
5842
5842
  }
5843
5843
  updateEventInfo(eventInfo, 'bubbling', node);
5844
5844
  }
5845
5845
  while(node){
5846
- // Root node handling.
5847
- if (node.is('rootElement')) {
5848
- if (fireListenerFor(eventContexts, '$root', eventInfo, ...eventArgs)) {
5849
- return eventInfo.return;
5850
- }
5851
- } else if (node.is('element')) {
5852
- if (fireListenerFor(eventContexts, node.name, eventInfo, ...eventArgs)) {
5853
- return eventInfo.return;
5854
- }
5855
- }
5856
- // Check custom contexts (i.e., a widget).
5857
- if (fireListenerFor(eventContexts, node, eventInfo, ...eventArgs)) {
5846
+ if (node.is('element') && fireListenerFor(bubblingEmitter, node, eventInfo, ...eventArgs)) {
5858
5847
  return eventInfo.return;
5859
5848
  }
5860
5849
  node = node.parent;
@@ -5862,7 +5851,7 @@ const contextsSymbol = Symbol('bubbling contexts');
5862
5851
  }
5863
5852
  updateEventInfo(eventInfo, 'bubbling', this);
5864
5853
  // Document context.
5865
- fireListenerFor(eventContexts, '$document', eventInfo, ...eventArgs);
5854
+ fireListenerFor(bubblingEmitter, '$document', eventInfo, ...eventArgs);
5866
5855
  return eventInfo.return;
5867
5856
  } catch (err) {
5868
5857
  // @if CK_DEBUG // throw err;
@@ -5871,20 +5860,27 @@ const contextsSymbol = Symbol('bubbling contexts');
5871
5860
  }
5872
5861
  _addEventListener(event, callback, options) {
5873
5862
  const contexts = toArray(options.context || '$document');
5874
- const eventContexts = getBubblingContexts(this);
5863
+ const bubblingEmitter = getBubblingEmitter(this);
5864
+ const callbacksMap = getCallbackMap(this);
5875
5865
  for (const context of contexts){
5876
- let emitter = eventContexts.get(context);
5877
- if (!emitter) {
5878
- emitter = new (EmitterMixin())();
5879
- eventContexts.set(context, emitter);
5866
+ if (typeof context == 'function') {
5867
+ getCustomContexts(this).add(context);
5880
5868
  }
5881
- this.listenTo(emitter, event, callback, options);
5882
5869
  }
5870
+ // Wrap callback with current target match.
5871
+ const wrappedCallback = wrapCallback(this, contexts, callback);
5872
+ // Store for later removing of listeners.
5873
+ callbacksMap.set(callback, wrappedCallback);
5874
+ // Listen for the event.
5875
+ this.listenTo(bubblingEmitter, event, wrappedCallback, options);
5883
5876
  }
5884
5877
  _removeEventListener(event, callback) {
5885
- const eventContexts = getBubblingContexts(this);
5886
- for (const emitter of eventContexts.values()){
5887
- this.stopListening(emitter, event, callback);
5878
+ const bubblingEmitter = getBubblingEmitter(this);
5879
+ const callbacksMap = getCallbackMap(this);
5880
+ const wrappedCallback = callbacksMap.get(callback);
5881
+ if (wrappedCallback) {
5882
+ callbacksMap.delete(callback);
5883
+ this.stopListening(bubblingEmitter, event, wrappedCallback);
5888
5884
  }
5889
5885
  }
5890
5886
  }
@@ -5908,32 +5904,85 @@ const contextsSymbol = Symbol('bubbling contexts');
5908
5904
  * @param eventInfo The `EventInfo` object.
5909
5905
  * @param eventArgs Additional arguments to be passed to the callbacks.
5910
5906
  * @returns True if event stop was called.
5911
- */ function fireListenerFor(eventContexts, context, eventInfo, ...eventArgs) {
5912
- const emitter = typeof context == 'string' ? eventContexts.get(context) : getCustomContext(eventContexts, context);
5913
- if (!emitter) {
5914
- return false;
5907
+ */ function fireListenerFor(emitter, currentTarget, eventInfo, ...eventArgs) {
5908
+ emitter.fire(eventInfo, {
5909
+ currentTarget,
5910
+ eventArgs
5911
+ });
5912
+ // Similar to DOM Event#stopImmediatePropagation() this does not fire events
5913
+ // for other contexts on the same node.
5914
+ if (eventInfo.stop.called) {
5915
+ return true;
5915
5916
  }
5916
- emitter.fire(eventInfo, ...eventArgs);
5917
- return eventInfo.stop.called;
5917
+ return false;
5918
5918
  }
5919
5919
  /**
5920
- * Returns an emitter for a specified view node.
5921
- */ function getCustomContext(eventContexts, node) {
5922
- for (const [context, emitter] of eventContexts){
5923
- if (typeof context == 'function' && context(node)) {
5924
- return emitter;
5920
+ * Returns an event callback wrapped with context check condition.
5921
+ */ function wrapCallback(emitter, contexts, callback) {
5922
+ return function(event, data) {
5923
+ const { currentTarget, eventArgs } = data;
5924
+ // Quick path for string based context ($capture, $text, $document).
5925
+ if (typeof currentTarget == 'string') {
5926
+ if (contexts.includes(currentTarget)) {
5927
+ callback.call(emitter, event, ...eventArgs);
5928
+ }
5929
+ return;
5930
+ }
5931
+ // The current target is a view element.
5932
+ // Special case for the root element as it could be handled ac $root context.
5933
+ // Note that it could also be matched later by custom context.
5934
+ if (currentTarget.is('rootElement') && contexts.includes('$root')) {
5935
+ callback.call(emitter, event, ...eventArgs);
5936
+ return;
5925
5937
  }
5938
+ // Check if it is a context for this element name.
5939
+ if (contexts.includes(currentTarget.name)) {
5940
+ callback.call(emitter, event, ...eventArgs);
5941
+ return;
5942
+ }
5943
+ // Check if dynamic context matches.
5944
+ for (const context of contexts){
5945
+ if (typeof context == 'function' && context(currentTarget)) {
5946
+ callback.call(emitter, event, ...eventArgs);
5947
+ return;
5948
+ }
5949
+ }
5950
+ };
5951
+ }
5952
+ /**
5953
+ * Returns bubbling emitter for the source (emitter).
5954
+ */ function getBubblingEmitter(source) {
5955
+ if (!source[bubblingEmitterSymbol]) {
5956
+ source[bubblingEmitterSymbol] = new (EmitterMixin())();
5926
5957
  }
5927
- return null;
5958
+ return source[bubblingEmitterSymbol];
5959
+ }
5960
+ /**
5961
+ * Returns map of callbacks (original to wrapped one).
5962
+ */ function getCallbackMap(source) {
5963
+ if (!source[callbackMapSymbol]) {
5964
+ source[callbackMapSymbol] = new Map();
5965
+ }
5966
+ return source[callbackMapSymbol];
5928
5967
  }
5929
5968
  /**
5930
- * Returns bubbling contexts map for the source (emitter).
5931
- */ function getBubblingContexts(source) {
5969
+ * Returns the set of registered custom contexts.
5970
+ */ function getCustomContexts(source) {
5932
5971
  if (!source[contextsSymbol]) {
5933
- source[contextsSymbol] = new Map();
5972
+ source[contextsSymbol] = new Set();
5934
5973
  }
5935
5974
  return source[contextsSymbol];
5936
5975
  }
5976
+ /**
5977
+ * Returns true if any of custom context match the given element.
5978
+ */ function hasMatchingCustomContext(customContexts, element) {
5979
+ for (const context of customContexts){
5980
+ if (context(element)) {
5981
+ return true;
5982
+ }
5983
+ }
5984
+ return false;
5985
+ }
5937
5986
  /**
5938
5987
  * Returns the deeper parent element for the range.
5939
5988
  */ function getDeeperRangeParent(range) {
@@ -21842,7 +21891,9 @@ function getFromAttributeCreator(config) {
21842
21891
  * Helper function for `highlight`. Prepares the actual descriptor object using value passed to the converter.
21843
21892
  */ function prepareDescriptor(highlightDescriptor, data, conversionApi) {
21844
21893
  // If passed descriptor is a creator function, call it. If not, just use passed value.
21845
- const descriptor = typeof highlightDescriptor == 'function' ? highlightDescriptor(data, conversionApi) : highlightDescriptor;
21894
+ const descriptor = typeof highlightDescriptor == 'function' ? highlightDescriptor(data, conversionApi) : {
21895
+ ...highlightDescriptor
21896
+ };
21846
21897
  if (!descriptor) {
21847
21898
  return null;
21848
21899
  }
@@ -27173,7 +27224,7 @@ function* _getUpcastDefinition(model, view, upcastAlso) {
27173
27224
  *
27174
27225
  * @error split-operation-split-in-root
27175
27226
  */ throw new CKEditorError('split-operation-split-in-root', this);
27176
- } else if (this.howMany != element.maxOffset - this.splitPosition.offset) {
27227
+ } else if (this.howMany !== Number.NEGATIVE_INFINITY && this.howMany != element.maxOffset - this.splitPosition.offset) {
27177
27228
  /**
27178
27229
  * Split operation specifies wrong number of nodes to move.
27179
27230
  *
@@ -27186,6 +27237,7 @@ function* _getUpcastDefinition(model, view, upcastAlso) {
27186
27237
  * @error split-operation-graveyard-position-invalid
27187
27238
  */ throw new CKEditorError('split-operation-graveyard-position-invalid', this);
27188
27239
  }
27240
+ this.howMany = this.splitPosition.parent.maxOffset - this.splitPosition.offset;
27189
27241
  }
27190
27242
  /**
27191
27243
  * @inheritDoc
@@ -27343,13 +27395,14 @@ function* _getUpcastDefinition(model, view, upcastAlso) {
27343
27395
  *
27344
27396
  * @error merge-operation-target-position-invalid
27345
27397
  */ throw new CKEditorError('merge-operation-target-position-invalid', this);
27346
- } else if (this.howMany != sourceElement.maxOffset) {
27398
+ } else if (this.howMany !== Number.NEGATIVE_INFINITY && this.howMany != sourceElement.maxOffset) {
27347
27399
  /**
27348
27400
  * Merge operation specifies wrong number of nodes to move.
27349
27401
  *
27350
27402
  * @error merge-operation-how-many-invalid
27351
27403
  */ throw new CKEditorError('merge-operation-how-many-invalid', this);
27352
27404
  }
27405
+ this.howMany = this.sourcePosition.parent.maxOffset;
27353
27406
  }
27354
27407
  /**
27355
27408
  * @inheritDoc
@@ -27746,13 +27799,14 @@ class MarkerOperation extends Operation {
27746
27799
  *
27747
27800
  * @error rename-operation-wrong-position
27748
27801
  */ throw new CKEditorError('rename-operation-wrong-position', this);
27749
- } else if (element.name !== this.oldName) {
27802
+ } else if (this.oldName !== '' && element.name !== this.oldName) {
27750
27803
  /**
27751
27804
  * Element to change has different name than operation's old name.
27752
27805
  *
27753
27806
  * @error rename-operation-wrong-name
27754
27807
  */ throw new CKEditorError('rename-operation-wrong-name', this);
27755
27808
  }
27809
+ this.oldName = element.name;
27756
27810
  }
27757
27811
  /**
27758
27812
  * @inheritDoc