Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Changed model utilities setData method to use document.enqueueChanges. #533

Merged
merged 3 commits into from
Jul 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions tests/_utils-tests/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ describe( 'model test utils', () => {
const parseSpy = sandbox.spy( setData, '_parse' );
const options = {};
const data = '<b>btext</b>text';
document.schema.registerItem( 'b', '$inline' );

setData( document, data, options );

Expand All @@ -71,6 +72,7 @@ describe( 'model test utils', () => {
const parseSpy = sandbox.spy( setData, '_parse' );
const options = {};
const data = '<selection><b>btext</b></selection>';
document.schema.registerItem( 'b', '$inline' );

setData( document, data, options );

Expand All @@ -80,11 +82,63 @@ describe( 'model test utils', () => {
expect( args[ 0 ] ).to.equal( data );
} );

it( 'should insert text', () => {
test( 'this is test text', '<selection />this is test text' );
} );

it( 'should insert text with selection around', () => {
test( '<selection>this is test text</selection>' );
} );

it( 'should insert text with selection inside #1', () => {
test( 'this <selection>is test</selection> text' );
} );

it( 'should insert text with selection inside #2', () => {
test( '<selection>this is test</selection> text' );
} );

it( 'should insert text with selection inside #2', () => {
test( 'this is <selection>test text</selection>' );
} );

it( 'should insert element', () => {
document.schema.registerItem( 'b', '$inline' );
test( '<b>foo bar</b>', '<selection /><b>foo bar</b>' );
} );

it( 'should insert element with selection inside #1', () => {
document.schema.registerItem( 'b', '$inline' );
test( '<b><selection>foo </selection>bar</b>' );
} );

it( 'should insert element with selection inside #2', () => {
document.schema.registerItem( 'b', '$inline' );
test( '<selection><b>foo </selection>bar</b>' );
} );

it( 'should insert element with selection inside #3', () => {
document.schema.registerItem( 'b', '$inline' );
test( '<b><selection>foo bar</b></selection>' );
} );

it( 'should insert backward selection', () => {
document.schema.registerItem( 'b', '$inline' );
test( '<b><selection backward>foo bar</b></selection>' );
} );

it( 'should throw an error when passing invalid document', () => {
expect( () => {
setData( { invalid: 'document' } );
} ).to.throw( TypeError, 'Document needs to be an instance of engine.model.Document.' );
} );

function test( data, expected ) {
expected = expected || data;

setData( document, data );
expect( getData( document ) ).to.equal( expected );
}
} );

describe( 'stringify', () => {
Expand Down
78 changes: 56 additions & 22 deletions tests/_utils/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,67 @@ getData._stringify = stringify;

/**
* Sets the contents of the {@link engine.model.Document Document} provided as HTML-like string.
* It uses {@link engine.model.Document#enqueueChanges enqueueChanges} method.
*
* NOTE:
* Remember to register elements in {@link engine.model.Document#schema document's schema} before inserting them.
*
* @param {engine.model.Document} document
* @param {String} data HTML-like string to write into Document.
* @param {Object} options
* @param {String} [options.rootName] Root name where parsed data will be stored. If not provided, default `main` name will be
* used.
* @param {String} [options.rootName='main'] Root name where parsed data will be stored. If not provided, default `main`
* name will be used.
* @param {String} [options.batchType='transparent'] Batch type used for inserting elements. See {@link engine.model.Batch#type}.
*/
export function setData( document, data, options = {} ) {
if ( !( document instanceof Document ) ) {
throw new TypeError( 'Document needs to be an instance of engine.model.Document.' );
}

setData._parse( data, {
document: document,
rootName: options.rootName
let model, selection;
const result = setData._parse( data );

if ( result.model && result.selection ) {
model = result.model;
selection = result.selection;
} else {
model = result;
}

// Save to model.
const modelRoot = document.getRoot( options.rootName || 'main' );

document.enqueueChanges( () => {
document.batch( options.batchType || 'transparent' )
.remove( Range.createFromElement( modelRoot ) )
.insert( Position.createAt( modelRoot, 0 ), model );

if ( selection ) {
const ranges = [];

for ( let range of selection.getRanges() ) {
let start, end;

// Each range returned from `parse()` method has its root placed in DocumentFragment.
// Here we convert each range to have its root re-calculated properly and be placed inside
// model document root.
if ( range.start.parent instanceof DocumentFragment ) {
start = Position.createFromParentAndOffset( modelRoot, range.start.offset );
} else {
start = Position.createFromParentAndOffset( range.start.parent, range.start.offset );
}

if ( range.end.parent instanceof DocumentFragment ) {
end = Position.createFromParentAndOffset( modelRoot, range.end.offset );
} else {
end = Position.createFromParentAndOffset( range.end.parent, range.end.offset );
}

ranges.push( new Range( start, end ) );
}

document.selection.setRanges( ranges, selection.isBackward );
}
} );
}

Expand Down Expand Up @@ -132,27 +178,15 @@ export function stringify( node, selectionOrPositionOrRange = null ) {
*
* @param {String} data HTML-like string to be parsed.
* @param {Object} options
* @param {engine.model.Document} [options.document] Document from which root element and selection will be used. If
* not provided new {engine.model.Document document} instance will be created.
* @param {String} [options.rootName='main'] When `document` option is provided this root name will be used to create
* {engine.model.RootElement RootElement} instance.
* @returns {engine.model.RootElement|Object} Returns parsed RootElement or object with two fields `model`
* and `selection` when selection ranges were included in data to parse.
* @returns {engine.model.Element|engine.model.Text|engine.model.DocumentFragment|Object} Returns parsed model node or
* object with two fields `model` and `selection` when selection ranges were included in data to parse.
*/
export function parse( data, options = {} ) {
export function parse( data ) {
let root, selection;
let withSelection = false;
const rootName = options.rootName || 'main';

if ( options.document ) {
const document = options.document;
root = document.getRoot( rootName );
root.removeChildren( 0, root.getMaxOffset() );
selection = document.selection;
} else {
root = new DocumentFragment();
selection = new Selection();
}
root = new DocumentFragment();
selection = new Selection();

const path = [];
let selectionStart, selectionEnd, selectionAttributes, textAttributes;
Expand Down
2 changes: 2 additions & 0 deletions tests/conversion/model-selection-to-view-converters.js
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ describe( 'table cell selection converter', () => {
} );

it( 'should add a class to the selected table cell', () => {
modelDoc.schema.registerItem( 'table', '$block' );
test(
// table tr#0, table tr#1
[ [ 0, 0, 0 ], [ 0, 0, 1 ] ],
Expand All @@ -444,6 +445,7 @@ describe( 'table cell selection converter', () => {
} );

it( 'should not be used if selection contains more than just a table cell', () => {
modelDoc.schema.registerItem( 'table', '$block' );
test(
// table tr td#1, table tr#2
[ [ 0, 0, 0, 1 ], [ 0, 0, 2 ] ],
Expand Down
6 changes: 6 additions & 0 deletions tests/datacontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ describe( 'DataController', () => {

describe( 'get', () => {
it( 'should get paragraph with text', () => {
modelDocument.schema.registerItem( 'paragraph', '$block' );
setData( modelDocument, '<paragraph>foo</paragraph>' );

BuildModelConverterFor( data.modelToView ).fromElement( 'paragraph' ).toElement( 'p' );
Expand All @@ -144,6 +145,7 @@ describe( 'DataController', () => {
} );

it( 'should get empty paragraph', () => {
modelDocument.schema.registerItem( 'paragraph', '$block' );
setData( modelDocument, '<paragraph></paragraph>' );

BuildModelConverterFor( data.modelToView ).fromElement( 'paragraph' ).toElement( 'p' );
Expand All @@ -152,6 +154,7 @@ describe( 'DataController', () => {
} );

it( 'should get two paragraphs', () => {
modelDocument.schema.registerItem( 'paragraph', '$block' );
setData( modelDocument, '<paragraph>foo</paragraph><paragraph>bar</paragraph>' );

BuildModelConverterFor( data.modelToView ).fromElement( 'paragraph' ).toElement( 'p' );
Expand All @@ -166,6 +169,7 @@ describe( 'DataController', () => {
} );

it( 'should get paragraphs without bold', () => {
modelDocument.schema.registerItem( 'paragraph', '$block' );
setData( modelDocument, '<paragraph>foo<$text bold=true>bar</$text></paragraph>' );

BuildModelConverterFor( data.modelToView ).fromElement( 'paragraph' ).toElement( 'p' );
Expand All @@ -174,6 +178,7 @@ describe( 'DataController', () => {
} );

it( 'should get paragraphs with bold', () => {
modelDocument.schema.registerItem( 'paragraph', '$block' );
setData( modelDocument, '<paragraph>foo<$text bold=true>bar</$text></paragraph>' );

BuildModelConverterFor( data.modelToView ).fromElement( 'paragraph' ).toElement( 'p' );
Expand All @@ -183,6 +188,7 @@ describe( 'DataController', () => {
} );

it( 'should get root name as a parameter', () => {
modelDocument.schema.registerItem( 'paragraph', '$block' );
setData( modelDocument, '<paragraph>foo</paragraph>', { rootName: 'main' } );
setData( modelDocument, 'Bar', { rootName: 'title' } );

Expand Down
18 changes: 5 additions & 13 deletions tests/manual/editingcontroller.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,27 @@

import EditingController from '/ckeditor5/engine/editingcontroller.js';
import Document from '/ckeditor5/engine/model/document.js';
import ModelPosition from '/ckeditor5/engine/model/position.js';
import ModelRange from '/ckeditor5/engine/model/range.js';
import ModelDocumentFragment from '/ckeditor5/engine/model/documentfragment.js';

import { parse } from '/tests/engine/_utils/model.js';
import { setData } from '/tests/engine/_utils/model.js';

import BuildModelConverterFor from '/ckeditor5/engine/conversion/model-converter-builder.js';

const model = new Document();
window.model = model;
const modelRoot = model.createRoot();
model.createRoot();

const editing = new EditingController( model );
editing.createRoot( document.getElementById( 'editor' ) );

model.schema.registerItem( 'paragraph', '$block' );
BuildModelConverterFor( editing.modelToView ).fromElement( 'paragraph' ).toElement( 'p' );

const modelData = new ModelDocumentFragment( parse(
setData( model,
'<paragraph>foo</paragraph>' +
'<paragraph></paragraph>' +
'<paragraph>bar</paragraph>'
)._children );
'<paragraph>bar</paragraph>' );

model.enqueueChanges( () => {
model.batch().insert( ModelPosition.createAt( modelRoot, 0 ), modelData );
model.selection.addRange( ModelRange.createFromParentsAndOffsets(
modelRoot.getChild( 0 ), 0, modelRoot.getChild( 0 ), 0 ) );
} );
editing.view.focus();

// enter
editing.view.on( 'keydown', ( evt, data ) => {
Expand Down
1 change: 1 addition & 0 deletions tests/model/composer/composer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe( 'Composer', () => {

beforeEach( () => {
document = new Document();
document.schema.registerItem( 'p', '$block' );
document.createRoot();

composer = new Composer();
Expand Down
1 change: 1 addition & 0 deletions tests/model/composer/modifyselection.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe( 'Delete utils', () => {

beforeEach( () => {
document = new Document();
document.schema.registerItem( 'p', '$block' );
document.createRoot();
} );

Expand Down