@ckeditor/ckeditor5-typing
Advanced tools
Comparing version 10.0.1 to 11.0.0
Changelog | ||
========= | ||
## [11.0.0](https://github.com/ckeditor/ckeditor5-typing/compare/v10.0.1...v11.0.0) (2018-07-18) | ||
### Bug fixes | ||
* Handle <kbd>Backspace</kbd> on Android. Closes ckeditor/ckeditor5/issues/1106. Closes https://github.com/ckeditor/ckeditor5/issues/1130. ([9161275](https://github.com/ckeditor/ckeditor5-typing/commit/9161275)) | ||
* Remove selection contents on `keydown` before the composition starts. Closes [#83](https://github.com/ckeditor/ckeditor5-typing/issues/83). Closes [#150](https://github.com/ckeditor/ckeditor5-typing/issues/150). ([ab1b46d](https://github.com/ckeditor/ckeditor5-typing/commit/ab1b46d)) | ||
### BREAKING CHANGES | ||
* `@ckeditor/ckeditor5-typing/src/changebuffer.js` was moved to `@ckeditor/ckeditor5-typing/src/utils/changebuffer.js`. | ||
## [10.0.1](https://github.com/ckeditor/ckeditor5-typing/compare/v10.0.0...v10.0.1) (2018-06-21) | ||
@@ -5,0 +17,0 @@ |
{ | ||
"name": "@ckeditor/ckeditor5-typing", | ||
"version": "10.0.1", | ||
"version": "11.0.0", | ||
"description": "Typing feature for CKEditor 5.", | ||
@@ -12,18 +12,18 @@ "keywords": [ | ||
"dependencies": { | ||
"@ckeditor/ckeditor5-core": "^10.1.0", | ||
"@ckeditor/ckeditor5-engine": "^10.1.0", | ||
"@ckeditor/ckeditor5-utils": "^10.1.0" | ||
"@ckeditor/ckeditor5-core": "^11.0.0", | ||
"@ckeditor/ckeditor5-engine": "^10.2.0", | ||
"@ckeditor/ckeditor5-utils": "^10.2.0" | ||
}, | ||
"devDependencies": { | ||
"@ckeditor/ckeditor5-basic-styles": "^10.0.1", | ||
"@ckeditor/ckeditor5-block-quote": "^10.0.1", | ||
"@ckeditor/ckeditor5-editor-classic": "^10.0.1", | ||
"@ckeditor/ckeditor5-enter": "^10.1.0", | ||
"@ckeditor/ckeditor5-essentials": "^10.1.0", | ||
"@ckeditor/ckeditor5-heading": "^10.0.1", | ||
"@ckeditor/ckeditor5-image": "^10.1.0", | ||
"@ckeditor/ckeditor5-link": "^10.0.2", | ||
"@ckeditor/ckeditor5-list": "^11.0.0", | ||
"@ckeditor/ckeditor5-paragraph": "^10.0.1", | ||
"@ckeditor/ckeditor5-undo": "^10.0.1", | ||
"@ckeditor/ckeditor5-basic-styles": "^10.0.2", | ||
"@ckeditor/ckeditor5-block-quote": "^10.0.2", | ||
"@ckeditor/ckeditor5-editor-classic": "^11.0.0", | ||
"@ckeditor/ckeditor5-enter": "^10.1.1", | ||
"@ckeditor/ckeditor5-essentials": "^10.1.1", | ||
"@ckeditor/ckeditor5-heading": "^10.0.2", | ||
"@ckeditor/ckeditor5-image": "^10.2.0", | ||
"@ckeditor/ckeditor5-link": "^10.0.3", | ||
"@ckeditor/ckeditor5-list": "^11.0.1", | ||
"@ckeditor/ckeditor5-paragraph": "^10.0.2", | ||
"@ckeditor/ckeditor5-undo": "^10.0.2", | ||
"eslint": "^4.15.0", | ||
@@ -35,3 +35,3 @@ "eslint-config-ckeditor5": "^1.0.7", | ||
"engines": { | ||
"node": ">=6.0.0", | ||
"node": ">=6.9.0", | ||
"npm": ">=3.0.0" | ||
@@ -38,0 +38,0 @@ }, |
@@ -14,2 +14,4 @@ /** | ||
import injectAndroidBackspaceMutationsHandling from './utils/injectandroidbackspacemutationshandling'; | ||
/** | ||
@@ -43,3 +45,5 @@ * The delete and backspace feature. Handles the <kbd>Delete</kbd> and <kbd>Backspace</kbd> keys in the editor. | ||
} ); | ||
injectAndroidBackspaceMutationsHandling( editor ); | ||
} | ||
} |
@@ -14,3 +14,3 @@ /** | ||
import Range from '@ckeditor/ckeditor5-engine/src/model/range'; | ||
import ChangeBuffer from './changebuffer'; | ||
import ChangeBuffer from './utils/changebuffer'; | ||
import count from '@ckeditor/ckeditor5-utils/src/count'; | ||
@@ -17,0 +17,0 @@ |
477
src/input.js
@@ -11,11 +11,7 @@ /** | ||
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; | ||
import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range'; | ||
import ViewPosition from '@ckeditor/ckeditor5-engine/src/view/position'; | ||
import ViewText from '@ckeditor/ckeditor5-engine/src/view/text'; | ||
import diff from '@ckeditor/ckeditor5-utils/src/diff'; | ||
import diffToChanges from '@ckeditor/ckeditor5-utils/src/difftochanges'; | ||
import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard'; | ||
import DomConverter from '@ckeditor/ckeditor5-engine/src/view/domconverter'; | ||
import InputCommand from './inputcommand'; | ||
import injectUnsafeKeystrokesHandling from './utils/injectunsafekeystrokeshandling'; | ||
import injectTypingMutationsHandling from './utils/injecttypingmutationshandling'; | ||
/** | ||
@@ -39,472 +35,11 @@ * Handles text input coming from the keyboard or other input methods. | ||
const editor = this.editor; | ||
const editingView = editor.editing.view; | ||
const inputCommand = new InputCommand( editor, editor.config.get( 'typing.undoStep' ) || 20 ); | ||
// TODO The above default configuration value should be defined using editor.config.define() once it's fixed. | ||
const inputCommand = new InputCommand( editor, editor.config.get( 'typing.undoStep' ) || 20 ); | ||
editor.commands.add( 'input', inputCommand ); | ||
this.listenTo( editingView.document, 'keydown', ( evt, data ) => { | ||
this._handleKeydown( data, inputCommand ); | ||
}, { priority: 'lowest' } ); | ||
this.listenTo( editingView.document, 'mutations', ( evt, mutations, viewSelection ) => { | ||
this._handleMutations( mutations, viewSelection ); | ||
} ); | ||
injectUnsafeKeystrokesHandling( editor ); | ||
injectTypingMutationsHandling( editor ); | ||
} | ||
/** | ||
* Handles the keydown event. We need to guess whether such keystroke is going to result | ||
* in typing. If so, then before character insertion happens, any selected content needs | ||
* to be deleted. Otherwise the default browser deletion mechanism would be | ||
* triggered, resulting in: | ||
* | ||
* * Hundreds of mutations which could not be handled. | ||
* * But most importantly, loss of control over how the content is being deleted. | ||
* | ||
* The method is used in a low-priority listener, hence allowing other listeners (e.g. delete or enter features) | ||
* to handle the event. | ||
* | ||
* @private | ||
* @param {module:engine/view/observer/keyobserver~KeyEventData} evtData | ||
* @param {module:typing/inputcommand~InputCommand} inputCommand | ||
*/ | ||
_handleKeydown( evtData, inputCommand ) { | ||
const model = this.editor.model; | ||
const doc = model.document; | ||
const buffer = inputCommand.buffer; | ||
// By relying on the state of the input command we allow disabling the entire input easily | ||
// by just disabling the input command. We could’ve used here the delete command but that | ||
// would mean requiring the delete feature which would block loading one without the other. | ||
// We could also check the editor.isReadOnly property, but that wouldn't allow to block | ||
// the input without blocking other features. | ||
if ( !inputCommand.isEnabled ) { | ||
return; | ||
} | ||
if ( isSafeKeystroke( evtData ) || doc.selection.isCollapsed ) { | ||
return; | ||
} | ||
buffer.lock(); | ||
model.enqueueChange( buffer.batch, () => { | ||
this.editor.model.deleteContent( doc.selection ); | ||
} ); | ||
buffer.unlock(); | ||
} | ||
/** | ||
* Handles DOM mutations. | ||
* | ||
* @private | ||
* @param {Array.<module:engine/view/observer/mutationobserver~MutatedText| | ||
* module:engine/view/observer/mutationobserver~MutatedChildren>} mutations | ||
* @param {module:engine/view/selection~Selection|null} viewSelection | ||
*/ | ||
_handleMutations( mutations, viewSelection ) { | ||
new MutationHandler( this.editor ).handle( mutations, viewSelection ); | ||
} | ||
} | ||
/** | ||
* Helper class for translating DOM mutations into model changes. | ||
* | ||
* @private | ||
*/ | ||
class MutationHandler { | ||
/** | ||
* Creates an instance of the mutation handler. | ||
* | ||
* @param {module:core/editor/editor~Editor} editor | ||
*/ | ||
constructor( editor ) { | ||
/** | ||
* Editor instance for which mutations are handled. | ||
* | ||
* @readonly | ||
* @member {module:core/editor/editor~Editor} #editor | ||
*/ | ||
this.editor = editor; | ||
/** | ||
* The editing controller. | ||
* | ||
* @readonly | ||
* @member {module:engine/controller/editingcontroller~EditingController} #editing | ||
*/ | ||
this.editing = this.editor.editing; | ||
} | ||
/** | ||
* Handles given mutations. | ||
* | ||
* @param {Array.<module:engine/view/observer/mutationobserver~MutatedText| | ||
* module:engine/view/observer/mutationobserver~MutatedChildren>} mutations | ||
* @param {module:engine/view/selection~Selection|null} viewSelection | ||
*/ | ||
handle( mutations, viewSelection ) { | ||
if ( containerChildrenMutated( mutations ) ) { | ||
this._handleContainerChildrenMutations( mutations, viewSelection ); | ||
} else { | ||
for ( const mutation of mutations ) { | ||
// Fortunately it will never be both. | ||
this._handleTextMutation( mutation, viewSelection ); | ||
this._handleTextNodeInsertion( mutation ); | ||
} | ||
} | ||
} | ||
/** | ||
* Handles situations when container's children mutated during input. This can happen when | ||
* the browser is trying to "fix" DOM in certain situations. For example, when the user starts to type | ||
* in `<p><a href=""><i>Link{}</i></a></p>`, the browser might change the order of elements | ||
* to `<p><i><a href="">Link</a>x{}</i></p>`. A similar situation happens when the spell checker | ||
* replaces a word wrapped with `<strong>` with a word wrapped with a `<b>` element. | ||
* | ||
* To handle such situations, the common DOM ancestor of all mutations is converted to the model representation | ||
* and then compared with the current model to calculate the proper text change. | ||
* | ||
* Note: Single text node insertion is handled in {@link #_handleTextNodeInsertion} and text node mutation is handled | ||
* in {@link #_handleTextMutation}). | ||
* | ||
* @private | ||
* @param {Array.<module:engine/view/observer/mutationobserver~MutatedText| | ||
* module:engine/view/observer/mutationobserver~MutatedChildren>} mutations | ||
* @param {module:engine/view/selection~Selection|null} viewSelection | ||
*/ | ||
_handleContainerChildrenMutations( mutations, viewSelection ) { | ||
// Get common ancestor of all mutations. | ||
const mutationsCommonAncestor = getMutationsContainer( mutations ); | ||
// Quit if there is no common ancestor. | ||
if ( !mutationsCommonAncestor ) { | ||
return; | ||
} | ||
const domConverter = this.editor.editing.view.domConverter; | ||
// Get common ancestor in DOM. | ||
const domMutationCommonAncestor = domConverter.mapViewToDom( mutationsCommonAncestor ); | ||
// Create fresh DomConverter so it will not use existing mapping and convert current DOM to model. | ||
// This wouldn't be needed if DomConverter would allow to create fresh view without checking any mappings. | ||
const freshDomConverter = new DomConverter(); | ||
const modelFromCurrentDom = this.editor.data.toModel( | ||
freshDomConverter.domToView( domMutationCommonAncestor ) | ||
).getChild( 0 ); | ||
// Current model. | ||
const currentModel = this.editor.editing.mapper.toModelElement( mutationsCommonAncestor ); | ||
// If common ancestor is not mapped, do not do anything. It probably is a parent of another view element. | ||
// That means that we would need to diff model elements (see `if` below). Better return early instead of | ||
// trying to get a reasonable model ancestor. It will fell into the `if` below anyway. | ||
// This situation happens for example for lists. If `<ul>` is a common ancestor, `currentModel` is `undefined` | ||
// because `<ul>` is not mapped (`<li>`s are). | ||
// See https://github.com/ckeditor/ckeditor5/issues/718. | ||
if ( !currentModel ) { | ||
return; | ||
} | ||
// Get children from both ancestors. | ||
const modelFromDomChildren = Array.from( modelFromCurrentDom.getChildren() ); | ||
const currentModelChildren = Array.from( currentModel.getChildren() ); | ||
// Remove the last `<softBreak>` from the end of `modelFromDomChildren` if there is no `<softBreak>` in current model. | ||
// If the described scenario happened, it means that this is a bogus `<br />` added by a browser. | ||
const lastDomChild = modelFromDomChildren[ modelFromDomChildren.length - 1 ]; | ||
const lastCurrentChild = currentModelChildren[ currentModelChildren.length - 1 ]; | ||
if ( lastDomChild && lastDomChild.is( 'softBreak' ) && lastCurrentChild && !lastCurrentChild.is( 'softBreak' ) ) { | ||
modelFromDomChildren.pop(); | ||
} | ||
// Skip situations when common ancestor has any container elements. | ||
if ( !isSafeForTextMutation( modelFromDomChildren ) || !isSafeForTextMutation( currentModelChildren ) ) { | ||
return; | ||
} | ||
// Replace inserted by the browser with normal space. See comment in `_handleTextMutation`. | ||
// Replace non-texts with any character. This is potentially dangerous but passes in manual tests. The thing is | ||
// that we need to take care of proper indexes so we cannot simply remove non-text elements from the content. | ||
// By inserting a character we keep all the real texts on their indexes. | ||
const newText = modelFromDomChildren.map( item => item.is( 'text' ) ? item.data : '@' ).join( '' ).replace( /\u00A0/g, ' ' ); | ||
const oldText = currentModelChildren.map( item => item.is( 'text' ) ? item.data : '@' ).join( '' ); | ||
// Do nothing if mutations created same text. | ||
if ( oldText === newText ) { | ||
return; | ||
} | ||
const diffResult = diff( oldText, newText ); | ||
const { firstChangeAt, insertions, deletions } = calculateChanges( diffResult ); | ||
// Try setting new model selection according to passed view selection. | ||
let modelSelectionRange = null; | ||
if ( viewSelection ) { | ||
modelSelectionRange = this.editing.mapper.toModelRange( viewSelection.getFirstRange() ); | ||
} | ||
const insertText = newText.substr( firstChangeAt, insertions ); | ||
const removeRange = ModelRange.createFromParentsAndOffsets( | ||
currentModel, | ||
firstChangeAt, | ||
currentModel, | ||
firstChangeAt + deletions | ||
); | ||
this.editor.execute( 'input', { | ||
text: insertText, | ||
range: removeRange, | ||
resultRange: modelSelectionRange | ||
} ); | ||
} | ||
/** | ||
* @private | ||
*/ | ||
_handleTextMutation( mutation, viewSelection ) { | ||
if ( mutation.type != 'text' ) { | ||
return; | ||
} | ||
// Replace inserted by the browser with normal space. | ||
// We want only normal spaces in the model and in the view. Renderer and DOM Converter will be then responsible | ||
// for rendering consecutive spaces using , but the model and the view has to be clear. | ||
// Other feature may introduce inserting non-breakable space on specific key stroke (for example shift + space). | ||
// However then it will be handled outside of mutations, like enter key is. | ||
// The replacing is here because it has to be done before `diff` and `diffToChanges` functions, as they | ||
// take `newText` and compare it to (cleaned up) view. | ||
// It could also be done in mutation observer too, however if any outside plugin would like to | ||
// introduce additional events for mutations, they would get already cleaned up version (this may be good or not). | ||
const newText = mutation.newText.replace( /\u00A0/g, ' ' ); | ||
// To have correct `diffResult`, we also compare view node text data with replaced by space. | ||
const oldText = mutation.oldText.replace( /\u00A0/g, ' ' ); | ||
const diffResult = diff( oldText, newText ); | ||
const { firstChangeAt, insertions, deletions } = calculateChanges( diffResult ); | ||
// Try setting new model selection according to passed view selection. | ||
let modelSelectionRange = null; | ||
if ( viewSelection ) { | ||
modelSelectionRange = this.editing.mapper.toModelRange( viewSelection.getFirstRange() ); | ||
} | ||
// Get the position in view and model where the changes will happen. | ||
const viewPos = new ViewPosition( mutation.node, firstChangeAt ); | ||
const modelPos = this.editing.mapper.toModelPosition( viewPos ); | ||
const removeRange = ModelRange.createFromPositionAndShift( modelPos, deletions ); | ||
const insertText = newText.substr( firstChangeAt, insertions ); | ||
this.editor.execute( 'input', { | ||
text: insertText, | ||
range: removeRange, | ||
resultRange: modelSelectionRange | ||
} ); | ||
} | ||
/** | ||
* @private | ||
*/ | ||
_handleTextNodeInsertion( mutation ) { | ||
if ( mutation.type != 'children' ) { | ||
return; | ||
} | ||
const change = getSingleTextNodeChange( mutation ); | ||
const viewPos = new ViewPosition( mutation.node, change.index ); | ||
const modelPos = this.editing.mapper.toModelPosition( viewPos ); | ||
const insertedText = change.values[ 0 ].data; | ||
this.editor.execute( 'input', { | ||
// Replace inserted by the browser with normal space. | ||
// See comment in `_handleTextMutation`. | ||
// In this case we don't need to do this before `diff` because we diff whole nodes. | ||
// Just change in case there are some. | ||
text: insertedText.replace( /\u00A0/g, ' ' ), | ||
range: new ModelRange( modelPos ) | ||
} ); | ||
} | ||
} | ||
const safeKeycodes = [ | ||
getCode( 'arrowUp' ), | ||
getCode( 'arrowRight' ), | ||
getCode( 'arrowDown' ), | ||
getCode( 'arrowLeft' ), | ||
9, // Tab | ||
16, // Shift | ||
17, // Ctrl | ||
18, // Alt | ||
20, // CapsLock | ||
27, // Escape | ||
33, // PageUp | ||
34, // PageDown | ||
35, // Home | ||
36, // End | ||
229 // Composition start key | ||
]; | ||
// Function keys. | ||
for ( let code = 112; code <= 135; code++ ) { | ||
safeKeycodes.push( code ); | ||
} | ||
// Returns `true` if a keystroke should not cause any content change caused by "typing". | ||
// | ||
// Note: This implementation is very simple and will need to be refined with time. | ||
// | ||
// @private | ||
// @param {engine.view.observer.keyObserver.KeyEventData} keyData | ||
// @returns {Boolean} | ||
function isSafeKeystroke( keyData ) { | ||
// Keystrokes which contain Ctrl don't represent typing. | ||
if ( keyData.ctrlKey ) { | ||
return true; | ||
} | ||
return safeKeycodes.includes( keyData.keyCode ); | ||
} | ||
// Helper function that compares whether two given view nodes are same. It is used in `diff` when it's passed an array | ||
// with child nodes. | ||
function compareChildNodes( oldChild, newChild ) { | ||
if ( oldChild instanceof ViewText && newChild instanceof ViewText ) { | ||
return oldChild.data === newChild.data; | ||
} else { | ||
return oldChild === newChild; | ||
} | ||
} | ||
// Returns change made to a single text node. Returns `undefined` if more than a single text node was changed. | ||
// | ||
// @private | ||
// @param mutation | ||
function getSingleTextNodeChange( mutation ) { | ||
// One new node. | ||
if ( mutation.newChildren.length - mutation.oldChildren.length != 1 ) { | ||
return; | ||
} | ||
// Which is text. | ||
const diffResult = diff( mutation.oldChildren, mutation.newChildren, compareChildNodes ); | ||
const changes = diffToChanges( diffResult, mutation.newChildren ); | ||
// In case of [ delete, insert, insert ] the previous check will not exit. | ||
if ( changes.length > 1 ) { | ||
return; | ||
} | ||
const change = changes[ 0 ]; | ||
// Which is text. | ||
if ( !( change.values[ 0 ] instanceof ViewText ) ) { | ||
return; | ||
} | ||
return change; | ||
} | ||
// Returns first common ancestor of all mutations that is either {@link module:engine/view/containerelement~ContainerElement} | ||
// or {@link module:engine/view/rootelement~RootElement}. | ||
// | ||
// @private | ||
// @param {Array.<module:engine/view/observer/mutationobserver~MutatedText| | ||
// module:engine/view/observer/mutationobserver~MutatedChildren>} mutations | ||
// @returns {module:engine/view/containerelement~ContainerElement|engine/view/rootelement~RootElement|undefined} | ||
function getMutationsContainer( mutations ) { | ||
const lca = mutations | ||
.map( mutation => mutation.node ) | ||
.reduce( ( commonAncestor, node ) => { | ||
return commonAncestor.getCommonAncestor( node, { includeSelf: true } ); | ||
} ); | ||
if ( !lca ) { | ||
return; | ||
} | ||
// We need to look for container and root elements only, so check all LCA's | ||
// ancestors (starting from itself). | ||
return lca.getAncestors( { includeSelf: true, parentFirst: true } ) | ||
.find( element => element.is( 'containerElement' ) || element.is( 'rootElement' ) ); | ||
} | ||
// Returns true if container children have mutated or more than a single text node was changed. | ||
// | ||
// Single text node child insertion is handled in {@link module:typing/input~MutationHandler#_handleTextNodeInsertion} | ||
// while text mutation is handled in {@link module:typing/input~MutationHandler#_handleTextMutation}. | ||
// | ||
// @private | ||
// @param {Array.<module:engine/view/observer/mutationobserver~MutatedText| | ||
// module:engine/view/observer/mutationobserver~MutatedChildren>} mutations | ||
// @returns {Boolean} | ||
function containerChildrenMutated( mutations ) { | ||
if ( mutations.length == 0 ) { | ||
return false; | ||
} | ||
// Check if there is any mutation of `children` type or any mutation that changes more than one text node. | ||
for ( const mutation of mutations ) { | ||
if ( mutation.type === 'children' && !getSingleTextNodeChange( mutation ) ) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
// Returns true if provided array contains content that won't be problematic during diffing and text mutation handling. | ||
// | ||
// @param {Array.<module:engine/model/node~Node>} children | ||
// @returns {Boolean} | ||
function isSafeForTextMutation( children ) { | ||
return children.every( child => child.is( 'text' ) || child.is( 'softBreak' ) ); | ||
} | ||
// Calculates first change index and number of characters that should be inserted and deleted starting from that index. | ||
// | ||
// @private | ||
// @param diffResult | ||
// @returns {{insertions: number, deletions: number, firstChangeAt: *}} | ||
function calculateChanges( diffResult ) { | ||
// Index where the first change happens. Used to set the position from which nodes will be removed and where will be inserted. | ||
let firstChangeAt = null; | ||
// Index where the last change happens. Used to properly count how many characters have to be removed and inserted. | ||
let lastChangeAt = null; | ||
// Get `firstChangeAt` and `lastChangeAt`. | ||
for ( let i = 0; i < diffResult.length; i++ ) { | ||
const change = diffResult[ i ]; | ||
if ( change != 'equal' ) { | ||
firstChangeAt = firstChangeAt === null ? i : firstChangeAt; | ||
lastChangeAt = i; | ||
} | ||
} | ||
// How many characters, starting from `firstChangeAt`, should be removed. | ||
let deletions = 0; | ||
// How many characters, starting from `firstChangeAt`, should be inserted. | ||
let insertions = 0; | ||
for ( let i = firstChangeAt; i <= lastChangeAt; i++ ) { | ||
// If there is no change (equal) or delete, the character is existing in `oldText`. We count it for removing. | ||
if ( diffResult[ i ] != 'insert' ) { | ||
deletions++; | ||
} | ||
// If there is no change (equal) or insert, the character is existing in `newText`. We count it for inserting. | ||
if ( diffResult[ i ] != 'delete' ) { | ||
insertions++; | ||
} | ||
} | ||
return { insertions, deletions, firstChangeAt }; | ||
} |
@@ -11,3 +11,3 @@ /** | ||
import Command from '@ckeditor/ckeditor5-core/src/command'; | ||
import ChangeBuffer from './changebuffer'; | ||
import ChangeBuffer from './utils/changebuffer'; | ||
@@ -35,3 +35,3 @@ /** | ||
* @private | ||
* @member {module:typing/changebuffer~ChangeBuffer} #_buffer | ||
* @member {module:typing/utils/changebuffer~ChangeBuffer} #_buffer | ||
*/ | ||
@@ -44,3 +44,3 @@ this._buffer = new ChangeBuffer( editor.model, undoStepSize ); | ||
* | ||
* @type {module:typing/changebuffer~ChangeBuffer} | ||
* @type {module:typing/utils/changebuffer~ChangeBuffer} | ||
*/ | ||
@@ -47,0 +47,0 @@ get buffer() { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
60242
15
1217
1
+ Added@ckeditor/ckeditor5-core@11.1.0(transitive)
+ Added@ckeditor/ckeditor5-engine@12.0.0(transitive)
+ Added@ckeditor/ckeditor5-utils@11.1.0(transitive)
+ Addedlodash-es@4.17.21(transitive)
- Removed@ckeditor/ckeditor5-core@10.1.0(transitive)