Skip to content

Commit

Permalink
fixes for block embed
Browse files Browse the repository at this point in the history
Many off by one issues occur since Block Embeds have length 2,
represented by an object and newline. Change to specifying with just
length 1 and how inline embeds are represented.
  • Loading branch information
jhchen committed Jun 3, 2016
1 parent 0cacdbe commit 3525e2d
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 32 deletions.
30 changes: 14 additions & 16 deletions blots/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,29 @@ class BlockEmbed extends Embed {
}

delta() {
return new Delta().insert(this.value(), this.formats()).insert('\n', this.attributes.values());
return new Delta().insert(this.value(), extend(this.formats(), this.attributes.values()));
}

formatAt(index, length, format, value) {
if (index + length === this.length()) {
let attribute = Parchment.query(format, Parchment.Scope.ATTRIBUTE);
if (attribute != null) {
this.attributes.attribute(attribute, value);
}
if (length <= 1) return;
format(name, value) {
let attribute = Parchment.query(name, Parchment.Scope.BLOCK_ATTRIBUTE);
if (attribute != null) {
this.attributes.attribute(attribute, value);
}
this.format(format, value);
}

formatAt(index, length, name, value) {
this.format(name, value);
}

insertAt(index, value, def) {
if (typeof value === 'string' && value.startsWith('\n')) {
let block = Parchment.create('block');
if (typeof value === 'string' && value.endsWith('\n')) {
let block = Parchment.create(Block.blotName);
this.parent.insertBefore(block, index === 0 ? this : this.next);
block.insertAt(0, value.slice(1));
block.insertAt(0, value.slice(0, -1));
} else {
super.insertAt(index, value, def);
}
}

length() {
return super.length() + NEWLINE_LENGTH;
}
}
BlockEmbed.scope = Parchment.Scope.BLOCK_BLOT;
// It is important for cursor behavior BlockEmbeds use tags that are block level elements
Expand Down
3 changes: 2 additions & 1 deletion blots/scroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class Scroll extends Parchment.Scroll {
let [first, firstOffset] = this.line(index);
let [last, lastOffset] = this.line(index + length);
super.deleteAt(index, length);
if (last != null && first !== last && firstOffset > 0) {
if (last != null && first !== last && firstOffset > 0 &&
!(first instanceof BlockEmbed) && !(last instanceof BlockEmbed)) {
let lastChild = first.children.tail;
last.moveChildren(first);
last.remove();
Expand Down
6 changes: 2 additions & 4 deletions core/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,8 @@ class Editor {
let key = Object.keys(op.insert)[0]; // There should only be one key
if (key != null) {
this.scroll.insertAt(index, key, op.insert[key]);
// Block embeds themselves already represent newline
if (Parchment.query(key, Parchment.Scope.BLOCK) != null) {
consumeNextNewline = true;
}
} else {
return index;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions test/unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import './helpers/unit.js';

import './unit/blots/scroll.js';
import './unit/blots/block.js';
import './unit/blots/block-embed.js';
import './unit/blots/inline.js';

import './unit/core/editor';
Expand Down
117 changes: 117 additions & 0 deletions test/unit/blots/block-embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import Parchment from 'parchment';
import Scroll from '../../../blots/scroll';


describe('Block Embed', function() {
it('insert', function() {
let scroll = this.initialize(Scroll, '<p>0123</p>');
scroll.insertAt(2, 'video', '#');
expect(scroll.domNode).toEqualHTML(`
<p>01</p>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
<p>23</p>
`);
});

it('split newline', function() {
let scroll = this.initialize(Scroll, '<p>0123</p>');
scroll.insertAt(4, 'video', '#');
expect(scroll.domNode).toEqualHTML(`
<p>0123</p>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
<p><br></p>
`);
});

it('insert end of document', function() {
let scroll = this.initialize(Scroll, '<p>0123</p>');
scroll.insertAt(5, 'video', '#');
expect(scroll.domNode).toEqualHTML(`
<p>0123</p>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
`);
});

it('insert text before', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(0, 'Test');
expect(scroll.domNode).toEqualHTML(`
<p>Test</p>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
`);
});

it('insert text after', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(1, 'Test');
expect(scroll.domNode).toEqualHTML(`
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
<p>Test</p>
`);
});

it('insert inline embed before', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(0, 'image', '/assets/favicon.png');
expect(scroll.domNode).toEqualHTML(`
<p><img src="/assets/favicon.png"></p>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
`);
});

it('insert inline embed after', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(1, 'image', '/assets/favicon.png');
expect(scroll.domNode).toEqualHTML(`
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
<p><img src="/assets/favicon.png"></p>
`);
});

it('insert block embed before', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(0, 'video', '#1');
expect(scroll.domNode).toEqualHTML(`
<iframe src="#1" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
`);
});

it('insert block embed after', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(1, 'video', '#1');
expect(scroll.domNode).toEqualHTML(`
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
<iframe src="#1" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
`);
});

it('insert newline before', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(0, '\n');
scroll.optimize();
expect(scroll.domNode).toEqualHTML(`
<p><br></p>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
`);
});

it('insert newline after', function() {
let scroll = this.initialize(Scroll, '<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.insertAt(1, '\n');
scroll.optimize();
expect(scroll.domNode).toEqualHTML(`
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
<p><br></p>
`);
});

it('delete preceding newline', function() {
let scroll = this.initialize(Scroll, '<p>0123</p><iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
scroll.deleteAt(4, 1);
expect(scroll.domNode).toEqualHTML(`
<p>0123</p>
<iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>
`);
});
});
14 changes: 3 additions & 11 deletions test/unit/core/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,19 +317,17 @@ describe('Editor', function() {
expect(this.container).toEqualHTML('<p><em><img src="/assets/favicon.png"></em>');
});

it('insert block embed', function() {
it('improper block embed insert', function() {
let editor = this.initialize(Editor, '<p>0123</p>');
editor.applyDelta(new Delta().retain(2).insert('\n').insert({ video: '#' }).insert('\n'));
editor.applyDelta(new Delta().retain(2).insert({ video: '#' }));
expect(this.container).toEqualHTML('<p>01</p><iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe><p>23</p>');
});

it('append formatted block embed', function() {
let editor = this.initialize(Editor, '<p>0123</p><p><br></p>');
editor.applyDelta(new Delta()
.retain(5)
.insert({ video: '#' })
.retain(1, { align: 'right' }) // Valid delta where newline does not follow block embed
.insert('\n')
.insert({ video: '#' }, { align: 'right' })
);
expect(this.container).toEqualHTML('<p>0123</p><iframe src="#" class="ql-video ql-align-right" frameborder="0" allowfullscreen="true"></iframe><p><br></p>');
});
Expand Down Expand Up @@ -378,12 +376,6 @@ describe('Editor', function() {
);
expect(this.container).toEqualHTML('<p>0123</p><h1>56</h1><h2>89</h2>');
});

it('append block embed', function() {
let editor = this.initialize(Editor, '<p>0123</p>');
editor.applyDelta(new Delta().retain(5).insert({ video: '#' }).insert('\n'));
expect(this.container).toEqualHTML('<p>0123</p><iframe src="#" class="ql-video" frameborder="0" allowfullscreen="true"></iframe>');
});
});

describe('getFormat()', function() {
Expand Down

0 comments on commit 3525e2d

Please sign in to comment.