@ckeditor/ckeditor5-typing
Advanced tools
Comparing version 0.7.0 to 0.8.0
{ | ||
"name": "@ckeditor/ckeditor5-typing", | ||
"version": "0.7.0", | ||
"version": "0.8.0", | ||
"description": "Typing feature for CKEditor 5.", | ||
"keywords": [], | ||
"dependencies": { | ||
"@ckeditor/ckeditor5-core": "*", | ||
"@ckeditor/ckeditor5-engine": "*", | ||
"@ckeditor/ckeditor5-utils": "*" | ||
"@ckeditor/ckeditor5-core": "^0.7.0", | ||
"@ckeditor/ckeditor5-engine": "^0.8.0", | ||
"@ckeditor/ckeditor5-utils": "^0.8.0" | ||
}, | ||
"devDependencies": { | ||
"@ckeditor/ckeditor5-dev-lint": "^2.0.0", | ||
"@ckeditor/ckeditor5-basic-styles": "*", | ||
"@ckeditor/ckeditor5-editor-classic": "*", | ||
"@ckeditor/ckeditor5-enter": "*", | ||
"@ckeditor/ckeditor5-heading": "*", | ||
"@ckeditor/ckeditor5-paragraph": "*", | ||
"@ckeditor/ckeditor5-undo": "*", | ||
"@ckeditor/ckeditor5-dev-lint": "^2.0.2", | ||
"@ckeditor/ckeditor5-basic-styles": "^0.7.1", | ||
"@ckeditor/ckeditor5-editor-classic": "^0.7.1", | ||
"@ckeditor/ckeditor5-enter": "^0.8.0", | ||
"@ckeditor/ckeditor5-heading": "^0.8.0", | ||
"@ckeditor/ckeditor5-paragraph": "^0.6.1", | ||
"@ckeditor/ckeditor5-undo": "^0.7.1", | ||
"gulp": "^3.9.0", | ||
@@ -20,0 +20,0 @@ "guppy-pre-commit": "^0.4.0" |
126
src/input.js
@@ -11,3 +11,2 @@ /** | ||
import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; | ||
import ChangeBuffer from './changebuffer'; | ||
import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range'; | ||
@@ -19,2 +18,3 @@ import ViewPosition from '@ckeditor/ckeditor5-engine/src/view/position'; | ||
import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard'; | ||
import InputCommand from './inputcommand'; | ||
@@ -24,3 +24,3 @@ /** | ||
* | ||
* @extends core.Plugin | ||
* @extends module:core/plugin~Plugin | ||
*/ | ||
@@ -34,15 +34,10 @@ export default class Input extends Plugin { | ||
const editingView = editor.editing.view; | ||
const inputCommand = new InputCommand( editor, editor.config.get( 'typing.undoStep' ) || 20 ); | ||
/** | ||
* Typing's change buffer used to group subsequent changes into batches. | ||
* | ||
* @protected | ||
* @member {typing.ChangeBuffer} #_buffer | ||
*/ | ||
this._buffer = new ChangeBuffer( editor.document, editor.config.get( 'typing.undoStep' ) || 20 ); | ||
// TODO The above default configuration value should be defined using editor.config.define() once it's fixed. | ||
editor.commands.set( 'input', inputCommand ); | ||
this.listenTo( editingView, 'keydown', ( evt, data ) => { | ||
this._handleKeydown( data ); | ||
this._handleKeydown( data, inputCommand.buffer ); | ||
}, { priority: 'lowest' } ); | ||
@@ -56,12 +51,2 @@ | ||
/** | ||
* @inheritDoc | ||
*/ | ||
destroy() { | ||
super.destroy(); | ||
this._buffer.destroy(); | ||
this._buffer = null; | ||
} | ||
/** | ||
* Handles the keydown event. We need to guess whether such keystroke is going to result | ||
@@ -80,4 +65,5 @@ * in typing. If so, then before character insertion happens, any selected content needs | ||
* @param {module:engine/view/observer/keyobserver~KeyEventData} evtData | ||
* @param {module:typing/changebuffer~ChangeBuffer} buffer | ||
*/ | ||
_handleKeydown( evtData ) { | ||
_handleKeydown( evtData, buffer ) { | ||
const doc = this.editor.document; | ||
@@ -90,3 +76,3 @@ | ||
doc.enqueueChanges( () => { | ||
this.editor.data.deleteContent( doc.selection, this._buffer.batch ); | ||
this.editor.data.deleteContent( doc.selection, buffer.batch ); | ||
} ); | ||
@@ -98,9 +84,8 @@ } | ||
* | ||
* @param {Array.<engine.view.Document~MutatatedText|engine.view.Document~MutatatedChildren>} mutations | ||
* @private | ||
* @param {Array.<module:engine/view/document~MutatatedText|module:engine/view/document~MutatatedChildren>} mutations | ||
* @param {module:engine/view/selection~Selection|null} viewSelection | ||
*/ | ||
_handleMutations( mutations, viewSelection ) { | ||
const doc = this.editor.document; | ||
const handler = new MutationHandler( this.editor.editing, this._buffer ); | ||
doc.enqueueChanges( () => handler.handle( mutations, viewSelection ) ); | ||
new MutationHandler( this.editor ).handle( mutations, viewSelection ); | ||
} | ||
@@ -118,26 +103,20 @@ } | ||
* | ||
* @param {module:engine/controller/editingcontroller~EditingController} editing | ||
* @param {module:typing/changebuffer~ChangeBuffer} buffer | ||
* @param {module:core/editor/editor~Editor} editor | ||
*/ | ||
constructor( editing, buffer ) { | ||
constructor( editor ) { | ||
/** | ||
* The editing controller. | ||
* Editor instance for which mutations are handled. | ||
* | ||
* @member {engine.controller.EditingController} #editing | ||
* @readonly | ||
* @member {module:core/editor/editor~Editor} #editor | ||
*/ | ||
this.editing = editing; | ||
this.editor = editor; | ||
/** | ||
* The change buffer. | ||
* The editing controller. | ||
* | ||
* @member {engine.controller.EditingController} #buffer | ||
* @readonly | ||
* @member {module:engine/controller/editingcontroller~EditingController} #editing | ||
*/ | ||
this.buffer = buffer; | ||
/** | ||
* The number of inserted characters which need to be fed to the {@link #buffer change buffer}. | ||
* | ||
* @member {Number} #insertedCharacterCount | ||
*/ | ||
this.insertedCharacterCount = 0; | ||
this.editing = this.editor.editing; | ||
} | ||
@@ -148,3 +127,5 @@ | ||
* | ||
* @param {Array.<engine.view.Document~MutatatedText|engine.view.Document~MutatatedChildren>} mutations | ||
* @param {Array.<module:engine/view/observer/mutationobserver~MutatedText| | ||
* module:engine/view/observer/mutationobserver~MutatatedChildren>} mutations | ||
* @param {module:engine/view/selection~Selection|null} viewSelection | ||
*/ | ||
@@ -157,4 +138,2 @@ handle( mutations, viewSelection ) { | ||
} | ||
this.buffer.input( Math.max( this.insertedCharacterCount, 0 ) ); | ||
} | ||
@@ -224,17 +203,10 @@ | ||
const modelPos = this.editing.mapper.toModelPosition( viewPos ); | ||
const removeRange = ModelRange.createFromPositionAndShift( modelPos, deletions ); | ||
const insertText = newText.substr( firstChangeAt, insertions ); | ||
// Remove appropriate number of characters from the model text node. | ||
if ( deletions > 0 ) { | ||
const removeRange = ModelRange.createFromPositionAndShift( modelPos, deletions ); | ||
this._remove( removeRange, deletions ); | ||
} | ||
// Insert appropriate characters basing on `mutation.text`. | ||
const insertedText = mutation.newText.substr( firstChangeAt, insertions ); | ||
this._insert( modelPos, insertedText ); | ||
// If there was `viewSelection` and it got correctly mapped, collapse selection at found model position. | ||
if ( modelSelectionPosition ) { | ||
this.editing.model.selection.collapse( modelSelectionPosition ); | ||
} | ||
this.editor.execute( 'input', { | ||
text: insertText, | ||
range: removeRange, | ||
resultPosition: modelSelectionPosition | ||
} ); | ||
} | ||
@@ -270,26 +242,13 @@ | ||
const modelPos = this.editing.mapper.toModelPosition( viewPos ); | ||
let insertedText = change.values[ 0 ].data; | ||
const insertedText = change.values[ 0 ].data; | ||
// 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. | ||
insertedText = insertedText.replace( /\u00A0/g, ' ' ); | ||
this._insert( modelPos, insertedText ); | ||
this.editing.model.selection.collapse( modelPos.getShiftedBy( insertedText.length ) ); | ||
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 ) | ||
} ); | ||
} | ||
_insert( position, text ) { | ||
this.buffer.batch.weakInsert( position, text ); | ||
this.insertedCharacterCount += text.length; | ||
} | ||
_remove( range, length ) { | ||
this.buffer.batch.remove( range ); | ||
this.insertedCharacterCount -= length; | ||
} | ||
} | ||
@@ -302,2 +261,3 @@ | ||
getCode( 'arrowLeft' ), | ||
9, // Tab | ||
16, // Shift | ||
@@ -304,0 +264,0 @@ 17, // Ctrl |
@@ -33,2 +33,4 @@ /** | ||
doc.schema.allow( { name: '$text', inside: 'img' } ); | ||
doc.schema.objects.add( 'img' ); | ||
} ); | ||
@@ -35,0 +37,0 @@ } ); |
@@ -7,4 +7,5 @@ /* | ||
import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor'; | ||
import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; | ||
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; | ||
import Input from '../src/input'; | ||
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph'; | ||
@@ -29,2 +30,4 @@ import Batch from '@ckeditor/ckeditor5-engine/src/model/batch'; | ||
testUtils.createSinonSandbox(); | ||
before( () => { | ||
@@ -70,18 +73,2 @@ listenter = Object.create( EmitterMixin ); | ||
it( 'has a buffer configured to default value of config.typing.undoStep', () => { | ||
expect( editor.plugins.get( Input )._buffer ).to.have.property( 'limit', 20 ); | ||
} ); | ||
it( 'has a buffer configured to config.typing.undoStep', () => { | ||
return VirtualTestEditor.create( { | ||
plugins: [ Input ], | ||
typing: { | ||
undoStep: 5 | ||
} | ||
} ) | ||
.then( editor => { | ||
expect( editor.plugins.get( Input )._buffer ).to.have.property( 'limit', 5 ); | ||
} ); | ||
} ); | ||
describe( 'mutations handling', () => { | ||
@@ -228,3 +215,3 @@ it( 'should handle text mutation', () => { | ||
it( 'should use up to one insert and remove operations', () => { | ||
it( 'should use up to one insert and remove operations (spellchecker)', () => { | ||
// This test case emulates spellchecker correction. | ||
@@ -235,4 +222,4 @@ | ||
sinon.spy( Batch.prototype, 'weakInsert' ); | ||
sinon.spy( Batch.prototype, 'remove' ); | ||
testUtils.sinon.spy( Batch.prototype, 'weakInsert' ); | ||
testUtils.sinon.spy( Batch.prototype, 'remove' ); | ||
@@ -252,2 +239,125 @@ view.fire( 'mutations', | ||
} ); | ||
it( 'should place selection after when correcting to longer word (spellchecker)', () => { | ||
// This test case emulates spellchecker correction. | ||
editor.setData( '<p>Foo hous a</p>' ); | ||
const viewSelection = new ViewSelection(); | ||
viewSelection.collapse( viewRoot.getChild( 0 ).getChild( 0 ), 9 ); | ||
view.fire( 'mutations', | ||
[ { | ||
type: 'text', | ||
oldText: 'Foo hous a', | ||
newText: 'Foo house a', | ||
node: viewRoot.getChild( 0 ).getChild( 0 ) | ||
} ], | ||
viewSelection | ||
); | ||
expect( getModelData( model ) ).to.equal( '<paragraph>Foo house[] a</paragraph>' ); | ||
expect( getViewData( view ) ).to.equal( '<p>Foo house{} a</p>' ); | ||
} ); | ||
it( 'should place selection after when correcting to shorter word (spellchecker)', () => { | ||
// This test case emulates spellchecker correction. | ||
editor.setData( '<p>Bar athat foo</p>' ); | ||
const viewSelection = new ViewSelection(); | ||
viewSelection.collapse( viewRoot.getChild( 0 ).getChild( 0 ), 8 ); | ||
view.fire( 'mutations', | ||
[ { | ||
type: 'text', | ||
oldText: 'Bar athat foo', | ||
newText: 'Bar that foo', | ||
node: viewRoot.getChild( 0 ).getChild( 0 ) | ||
} ], | ||
viewSelection | ||
); | ||
expect( getModelData( model ) ).to.equal( '<paragraph>Bar that[] foo</paragraph>' ); | ||
expect( getViewData( view ) ).to.equal( '<p>Bar that{} foo</p>' ); | ||
} ); | ||
it( 'should place selection after when merging two words (spellchecker)', () => { | ||
// This test case emulates spellchecker correction. | ||
editor.setData( '<p>Foo hous e</p>' ); | ||
const viewSelection = new ViewSelection(); | ||
viewSelection.collapse( viewRoot.getChild( 0 ).getChild( 0 ), 9 ); | ||
view.fire( 'mutations', | ||
[ { | ||
type: 'text', | ||
oldText: 'Foo hous e', | ||
newText: 'Foo house', | ||
node: viewRoot.getChild( 0 ).getChild( 0 ) | ||
} ], | ||
viewSelection | ||
); | ||
expect( getModelData( model ) ).to.equal( '<paragraph>Foo house[]</paragraph>' ); | ||
expect( getViewData( view ) ).to.equal( '<p>Foo house{}</p>' ); | ||
} ); | ||
it( 'should replace last with space', () => { | ||
model.enqueueChanges( () => { | ||
model.selection.setRanges( [ | ||
ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 6, modelRoot.getChild( 0 ), 6 ) | ||
] ); | ||
} ); | ||
view.fire( 'mutations', [ | ||
{ | ||
type: 'text', | ||
oldText: 'foobar', | ||
newText: 'foobar\u00A0', | ||
node: viewRoot.getChild( 0 ).getChild( 0 ) | ||
} | ||
] ); | ||
expect( getModelData( model ) ).to.equal( '<paragraph>foobar []</paragraph>' ); | ||
expect( getViewData( view ) ).to.equal( '<p>foobar {}</p>' ); | ||
} ); | ||
it( 'should replace first with space', () => { | ||
model.enqueueChanges( () => { | ||
model.selection.setRanges( [ | ||
ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 0, modelRoot.getChild( 0 ), 0 ) | ||
] ); | ||
} ); | ||
view.fire( 'mutations', [ | ||
{ | ||
type: 'text', | ||
oldText: 'foobar', | ||
newText: '\u00A0foobar', | ||
node: viewRoot.getChild( 0 ).getChild( 0 ) | ||
} | ||
] ); | ||
expect( getModelData( model ) ).to.equal( '<paragraph> []foobar</paragraph>' ); | ||
expect( getViewData( view ) ).to.equal( '<p> {}foobar</p>' ); | ||
} ); | ||
it( 'should replace all with spaces', () => { | ||
model.enqueueChanges( () => { | ||
model.selection.setRanges( [ | ||
ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 6, modelRoot.getChild( 0 ), 6 ) | ||
] ); | ||
} ); | ||
view.fire( 'mutations', [ | ||
{ | ||
type: 'text', | ||
oldText: 'foobar', | ||
newText: 'foobar\u00A0\u00A0\u00A0baz', | ||
node: viewRoot.getChild( 0 ).getChild( 0 ) | ||
} | ||
] ); | ||
expect( getModelData( model ) ).to.equal( '<paragraph>foobar baz[]</paragraph>' ); | ||
expect( getViewData( view ) ).to.equal( '<p>foobar baz{}</p>' ); | ||
} ); | ||
} ); | ||
@@ -316,2 +426,14 @@ | ||
// #69 | ||
it( 'should do nothing on tab key', () => { | ||
model.enqueueChanges( () => { | ||
model.selection.setRanges( [ | ||
ModelRange.createFromParentsAndOffsets( modelRoot.getChild( 0 ), 2, modelRoot.getChild( 0 ), 4 ) ] ); | ||
} ); | ||
view.fire( 'keydown', { keyCode: 9 } ); // Tab | ||
expect( getModelData( model ) ).to.equal( '<paragraph>fo[ob]ar</paragraph>' ); | ||
} ); | ||
it( 'should do nothing if selection is collapsed', () => { | ||
@@ -323,17 +445,3 @@ view.fire( 'keydown', { ctrlKey: true, keyCode: getCode( 'c' ) } ); | ||
} ); | ||
describe( 'destroy', () => { | ||
it( 'should destroy change buffer', () => { | ||
const typing = new Input( new VirtualTestEditor() ); | ||
typing.init(); | ||
const destroy = typing._buffer.destroy = sinon.spy(); | ||
typing.destroy(); | ||
expect( destroy.calledOnce ).to.be.true; | ||
expect( typing._buffer ).to.be.null; | ||
} ); | ||
} ); | ||
} ); | ||
Sorry, the diff of this file is not supported yet
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
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
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 3 instances in 1 package
94665
58
2044
0
+ Added@ckeditor/ckeditor5-core@0.7.0(transitive)
+ Added@ckeditor/ckeditor5-engine@0.8.0(transitive)
+ Added@ckeditor/ckeditor5-utils@0.8.0(transitive)
- Removed@ckeditor/ckeditor5-core@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-engine@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-ui@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-utils@43.3.1(transitive)
- Removed@ckeditor/ckeditor5-watchdog@43.3.1(transitive)
- Removedcolor-convert@2.0.1(transitive)
- Removedcolor-name@1.1.4(transitive)
- Removedcolor-parse@1.4.2(transitive)
- Removedlodash-es@4.17.21(transitive)
- Removedvanilla-colorful@0.7.2(transitive)