From 3f113b303fd1addf79a05a87e0c3d58eb4b7ade8 Mon Sep 17 00:00:00 2001 From: Cory Forsyth Date: Mon, 10 Aug 2015 12:50:54 -0400 Subject: [PATCH] Handle newline at start or end of section * simplify section#splitMarker * tests for hitting enter in middle and end of section * add test for hitting enter in first section * fix bug with PostNodeBuilder not assigning a builder to the blank marker fixes #39 --- src/js/editor/editor.js | 26 ++---- src/js/models/marker.js | 24 ++---- src/js/models/markup-section.js | 54 +++++++++++- src/js/models/post-node-builder.js | 5 +- src/js/renderers/editor-dom.js | 16 ++-- src/js/utils/array-utils.js | 11 ++- src/js/utils/linked-list.js | 3 + tests/acceptance/editor-sections-test.js | 55 ++++++++++++- tests/acceptance/editor-selections-test.js | 31 ++++++- tests/helpers/dom.js | 13 ++- tests/unit/models/marker-test.js | 96 ++++++++++++---------- tests/unit/models/markup-section-test.js | 72 ++++++++-------- 12 files changed, 273 insertions(+), 133 deletions(-) diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js index ada2da6e5..b3f3eaed1 100644 --- a/src/js/editor/editor.js +++ b/src/js/editor/editor.js @@ -442,33 +442,19 @@ class Editor { const markerRenderNode = leftRenderNode; const marker = markerRenderNode.postNode; const section = marker.section; - const newMarkers = marker.split(leftOffset); - // FIXME rightMarker is not guaranteed to be there - let [leftMarker, rightMarker] = newMarkers; + let [beforeSection, afterSection] = section.splitAtMarker(marker, leftOffset); - section.markers.insertAfter(leftMarker, marker); - markerRenderNode.scheduleForRemoval(); + section.renderNode.scheduleForRemoval(); - const newSection = this.builder.createMarkupSection('p'); - newSection.markers.append(rightMarker); - - let nodeForMove = markerRenderNode.next; - while (nodeForMove) { - nodeForMove.scheduleForRemoval(); - let movedMarker = nodeForMove.postNode.clone(); - newSection.markers.append(movedMarker); - - nodeForMove = nodeForMove.next; - } - - const post = this.post; - post.sections.insertAfter(newSection, section); + this.post.sections.insertAfter(beforeSection, section); + this.post.sections.insertAfter(afterSection, beforeSection); + this.post.sections.remove(section); this.rerender(); this.trigger('update'); - this.cursor.moveToSection(newSection); + this.cursor.moveToSection(afterSection); } hasSelection() { diff --git a/src/js/models/marker.js b/src/js/models/marker.js index 8ce20b41e..fd620bd8d 100644 --- a/src/js/models/marker.js +++ b/src/js/models/marker.js @@ -26,6 +26,10 @@ const Marker = class Marker extends LinkedItem { return new this.constructor(this.value, clonedMarkups); } + empty() { + return this.length === 0; + } + get length() { return this.value.length; } @@ -88,21 +92,11 @@ const Marker = class Marker extends LinkedItem { split(offset=0, endOffset=this.length) { let markers = []; - if (offset !== 0) { - markers.push( - new Marker(this.value.substring(0, offset)) - ); - } - - markers.push( - new Marker(this.value.substring(offset, endOffset)) - ); - - if (endOffset < this.length) { - markers.push( - new Marker(this.value.substring(endOffset)) - ); - } + markers = [ + this.builder.createMarker(this.value.substring(0, offset)), + this.builder.createMarker(this.value.substring(offset, endOffset)), + this.builder.createMarker(this.value.substring(endOffset)) + ]; this.markups.forEach(mu => markers.forEach(m => m.addMarkup(mu))); return markers; diff --git a/src/js/models/markup-section.js b/src/js/models/markup-section.js index 1a65588bc..f5e945ab1 100644 --- a/src/js/models/markup-section.js +++ b/src/js/models/markup-section.js @@ -2,6 +2,11 @@ import { normalizeTagName } from '../utils/dom-utils'; +import { + forEach, + filter +} from '../utils/array-utils'; + export const DEFAULT_TAG_NAME = normalizeTagName('p'); export const VALID_MARKUP_SECTION_TAGNAMES = [ 'p', 'h3', 'h2', 'h1', 'blockquote', 'ul', 'ol' @@ -53,16 +58,57 @@ export default class Section extends LinkedItem { } /** - * Splits the marker at the offset (until the endOffset, if given) - * into 1, 2, or 3 markers and replaces the existing marker - * with the new ones + * Splits the marker at the offset, filters empty markers from the result, + * and replaces this marker with the new non-empty ones + * @param {Marker} marker the marker to split + * @return {Array} the new markers that replaced `marker` */ splitMarker(marker, offset, endOffset=marker.length) { - const newMarkers = marker.split(offset, endOffset); + const newMarkers = filter(marker.split(offset, endOffset), m => !m.empty()); this.markers.splice(marker, 1, newMarkers); return newMarkers; } + splitAtMarker(marker, offset=0) { + let [beforeSection, afterSection] = [ + this.builder.createMarkupSection(this.tagName, []), + this.builder.createMarkupSection(this.tagName, []) + ]; + + let currentSection = beforeSection; + forEach(this.markers, m => { + if (m === marker) { + const [beforeMarker, ...afterMarkers] = marker.split(offset); + beforeSection.markers.append(beforeMarker); + forEach(afterMarkers, _m => afterSection.markers.append(_m)); + currentSection = afterSection; + } else { + currentSection.markers.append(m.clone()); + } + }); + + beforeSection.coalesceMarkers(); + afterSection.coalesceMarkers(); + + return [beforeSection, afterSection]; + } + + /** + * Remove extranous empty markers, adding one at the end if there + * are no longer any markers + * + * Mutates this section's markers + */ + coalesceMarkers() { + forEach( + filter(this.markers, m => m.empty()), + m => this.markers.remove(m) + ); + if (this.markers.empty()) { + this.markers.append(this.builder.createBlankMarker()); + } + } + /** * @return {Array} 2 new sections */ diff --git a/src/js/models/post-node-builder.js b/src/js/models/post-node-builder.js index 26d5a0b10..a2738625f 100644 --- a/src/js/models/post-node-builder.js +++ b/src/js/models/post-node-builder.js @@ -23,6 +23,7 @@ export default class PostNodeBuilder { if (isGenerated) { section.isGenerated = true; } + section.builder = this; return section; } @@ -45,7 +46,9 @@ export default class PostNodeBuilder { } createBlankMarker() { - return new Marker(''); + const marker = new Marker(''); + marker.builder = this; + return marker; } createMarkup(tagName, attributes) { diff --git a/src/js/renderers/editor-dom.js b/src/js/renderers/editor-dom.js index a8f06a3e5..6e3d6a549 100644 --- a/src/js/renderers/editor-dom.js +++ b/src/js/renderers/editor-dom.js @@ -8,7 +8,7 @@ import { IMAGE_SECTION_TYPE } from "../models/image"; import { CARD_TYPE } from "../models/card"; import { clearChildNodes } from '../utils/dom-utils'; -export const UNPRINTABLE_CHARACTER = "\u200C"; +export const UNPRINTABLE_CHARACTER = "\u2006"; function createElementFromMarkup(doc, markup) { var element = doc.createElement(markup.tagName); @@ -55,7 +55,6 @@ function getNextMarkerElement(renderNode) { } function renderMarker(marker, element, previousRenderNode) { - const openTypes = marker.openedMarkups; let text = marker.value; if (isEmptyText(text)) { // This is necessary to allow the cursor to move into this area @@ -66,6 +65,7 @@ function renderMarker(marker, element, previousRenderNode) { let currentElement = textNode; let markup; + const openTypes = marker.openedMarkups; for (let j=openTypes.length-1;j>=0;j--) { markup = openTypes[j]; let openedElement = createElementFromMarkup(document, markup); @@ -116,13 +116,11 @@ class Visitor { if (renderNode.prev) { let previousElement = renderNode.prev.element; - let nextElement = previousElement.nextSibling; - if (nextElement) { - nextElement.parentNode.insertBefore(element, nextElement); - } - } - if (!element.parentNode) { - renderNode.parent.element.appendChild(element); + let parentNode = previousElement.parentNode; + parentNode.insertBefore(element, previousElement.nextSibling); + } else { + let parentElement = renderNode.parent.element; + parentElement.insertBefore(element, parentElement.firstChild); } } else { renderNode.parent.element.replaceChild(element, originalElement); diff --git a/src/js/utils/array-utils.js b/src/js/utils/array-utils.js index 78f4be88d..a64090516 100644 --- a/src/js/utils/array-utils.js +++ b/src/js/utils/array-utils.js @@ -49,9 +49,18 @@ function difference(enumerable, otherEnumerable) { return diff; } +function filter(enumerable, conditionFn) { + const filtered = []; + forEach(enumerable, i => { + if (conditionFn(i)) { filtered.push(i); } + }); + return filtered; +} + export { detect, forEach, any, - difference + difference, + filter }; diff --git a/src/js/utils/linked-list.js b/src/js/utils/linked-list.js index 19463bc2e..20ccbb7cc 100644 --- a/src/js/utils/linked-list.js +++ b/src/js/utils/linked-list.js @@ -9,6 +9,9 @@ export default class LinkedList { this.freeItem = freeItem; } } + empty() { + return this.length === 0; + } prepend(item) { this.insertBefore(item, this.head); } diff --git a/tests/acceptance/editor-sections-test.js b/tests/acceptance/editor-sections-test.js index b7d2c4584..5c23364f1 100644 --- a/tests/acceptance/editor-sections-test.js +++ b/tests/acceptance/editor-sections-test.js @@ -101,18 +101,69 @@ module('Acceptance: Editor sections', { } }); -Helpers.skipInPhantom('typing inserts section', (assert) => { +test('typing enter inserts new section', (assert) => { editor = new Editor(editorElement, {mobiledoc: mobileDocWith1Section}); assert.equal($('#editor p').length, 1, 'has 1 paragraph to start'); Helpers.dom.moveCursorTo(editorElement.childNodes[0].childNodes[0], 5); - Helpers.dom.triggerKeyEvent(document, 'keydown', Helpers.dom.KEY_CODES.ENTER); + Helpers.dom.triggerEnter(editor); assert.equal($('#editor p').length, 2, 'has 2 paragraphs after typing return'); assert.hasElement(`#editor p:contains(only)`, 'has correct first pargraph text'); assert.hasElement('#editor p:contains(section)', 'has correct second paragraph text'); }); +test('hitting enter in first section splits it correctly', (assert) => { + editor = new Editor(editorElement, {mobiledoc: mobileDocWith2Sections}); + assert.equal($('#editor p').length, 2, 'precond - has 2 paragraphs'); + + Helpers.dom.moveCursorTo(editorElement.childNodes[0].childNodes[0], 3); + Helpers.dom.triggerEnter(editor); + + assert.equal($('#editor p').length, 3, 'has 3 paragraphs after typing return'); + + assert.equal($('#editor p:eq(0)').text(), 'fir', 'first para has correct text'); + assert.equal($('#editor p:eq(1)').text(), 'st section', 'second para has correct text'); + assert.equal($('#editor p:eq(2)').text(), 'second section', 'third para still has correct text'); + + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: editorElement.childNodes[1].childNodes[0], + offset: 0}); +}); + +test('hitting enter at start of a section creates empty section where cursor was', (assert) => { + editor = new Editor(editorElement, {mobiledoc: mobileDocWith1Section}); + assert.equal($('#editor p').length, 1, 'has 1 paragraph to start'); + + Helpers.dom.moveCursorTo(editorElement.childNodes[0].childNodes[0], 0); + Helpers.dom.triggerEnter(editor); + + assert.equal($('#editor p').length, 2, 'has 2 paragraphs after typing return'); + + let firstP = $('#editor p:eq(0)'); + assert.equal(firstP.text(), UNPRINTABLE_CHARACTER, 'first para has unprintable char'); + assert.hasElement('#editor p:eq(1):contains(only section)', 'has correct second paragraph text'); + + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: editorElement.childNodes[1].childNodes[0], + offset: 0}); +}); + +test('hitting enter at end of a section creates new empty section', (assert) => { + editor = new Editor(editorElement, {mobiledoc: mobileDocWith1Section}); + assert.equal($('#editor p').length, 1, 'has 1 section to start'); + + Helpers.dom.moveCursorTo(editorElement.childNodes[0].childNodes[0], 'only section'.length); + Helpers.dom.triggerEnter(editor); + + assert.equal($('#editor p').length, 2, 'has 2 sections after typing return'); + assert.hasElement('#editor p:eq(0):contains(only section)', 'has same first section text'); + + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: editorElement.childNodes[1].childNodes[0], + offset: 0}); +}); + test('deleting across 0 sections merges them', (assert) => { editor = new Editor(editorElement, {mobiledoc: mobileDocWith2Sections}); assert.equal($('#editor p').length, 2, 'precond - has 2 sections to start'); diff --git a/tests/acceptance/editor-selections-test.js b/tests/acceptance/editor-selections-test.js index 87a33f7b0..58dee7c29 100644 --- a/tests/acceptance/editor-selections-test.js +++ b/tests/acceptance/editor-selections-test.js @@ -176,6 +176,35 @@ test('select text and apply markup multiple times', (assert) => { }); }); -// test selecting text across markers deletes intermediary markers +test('selecting text across markers deletes intermediary markers', (assert) => { + const done = assert.async(); + editor = new Editor(editorElement, {mobiledoc: mobileDocWith2Sections}); + + Helpers.dom.selectText('rst sec', editorElement); + Helpers.dom.triggerEvent(document, 'mouseup'); + + setTimeout(() => { + Helpers.toolbar.clickButton(assert, 'bold'); + + const textNode1 = editorElement.childNodes[0].childNodes[0], + textNode2 = editorElement.childNodes[0].childNodes[2]; + Helpers.dom.selectText('i', textNode1, + 'tio', textNode2); + Helpers.dom.triggerEvent(document, 'mouseup'); + + setTimeout(() => { + Helpers.dom.triggerDelete(editor); + + assert.hasElement('p:contains(fn)', 'has remaining first section'); + assert.deepEqual(Helpers.dom.getCursorPosition(), + {node: editorElement.childNodes[0].childNodes[0], + offset: 1}); + + done(); + }); + }); +}); + // test selecting text that includes entire sections deletes the sections +// test selecting text across two types of sections and deleting // test selecting text and hitting enter or keydown diff --git a/tests/helpers/dom.js b/tests/helpers/dom.js index 91f970da8..c2d70e8ae 100644 --- a/tests/helpers/dom.js +++ b/tests/helpers/dom.js @@ -146,6 +146,16 @@ function triggerDelete(editor) { } } +function triggerEnter(editor) { + if (isPhantom()) { + // simulate event when testing with phantom + let event = { preventDefault() {} }; + editor.handleNewline(event); + } else { + triggerKeyEvent(document, 'keydown', KEY_CODES.ENTER); + } +} + const DOMHelper = { moveCursorTo, selectText, @@ -156,7 +166,8 @@ const DOMHelper = { KEY_CODES, getCursorPosition, getSelectedText, - triggerDelete + triggerDelete, + triggerEnter }; export { triggerEvent }; diff --git a/tests/unit/models/marker-test.js b/tests/unit/models/marker-test.js index f192dc9a6..f12cb1713 100644 --- a/tests/unit/models/marker-test.js +++ b/tests/unit/models/marker-test.js @@ -1,16 +1,19 @@ const {module, test} = QUnit; -import Marker from 'content-kit-editor/models/marker'; -import Markup from 'content-kit-editor/models/markup'; - -module('Unit: Marker'); - -test('Marker exists', (assert) => { - assert.ok(Marker); +import PostNodeBuilder from 'content-kit-editor/models/post-node-builder'; + +let builder; +module('Unit: Marker', { + beforeEach() { + builder = new PostNodeBuilder(); + }, + afterEach() { + builder = null; + } }); test('a marker can truncated from an offset', (assert) => { - const m1 = new Marker('hi there!'); + const m1 = builder.createMarker('hi there!'); const offset = 5; m1.truncateFrom(offset); @@ -19,7 +22,7 @@ test('a marker can truncated from an offset', (assert) => { }); test('a marker can truncated to an offset', (assert) => { - const m1 = new Marker('hi there!'); + const m1 = builder.createMarker('hi there!'); const offset = 5; m1.truncateTo(offset); @@ -28,23 +31,23 @@ test('a marker can truncated to an offset', (assert) => { }); test('a marker can have a markup applied to it', (assert) => { - const m1 = new Marker('hi there!'); - m1.addMarkup(new Markup('b')); + const m1 = builder.createMarker('hi there!'); + m1.addMarkup(builder.createMarkup('b')); assert.ok(m1.hasMarkup('b')); }); test('a marker can have the same markup tagName applied twice', (assert) => { - const m1 = new Marker('hi there!'); - m1.addMarkup(new Markup('b')); - m1.addMarkup(new Markup('b')); + const m1 = builder.createMarker('hi there!'); + m1.addMarkup(builder.createMarkup('b')); + m1.addMarkup(builder.createMarkup('b')); assert.equal(m1.markups.length, 2, 'markup only applied once'); }); test('a marker can have a complex markup applied to it', (assert) => { - const m1 = new Marker('hi there!'); - const markup = new Markup('a', {href:'blah'}); + const m1 = builder.createMarker('hi there!'); + const markup = builder.createMarkup('a', {href:'blah'}); m1.addMarkup(markup); assert.ok(m1.hasMarkup('a')); @@ -52,9 +55,9 @@ test('a marker can have a complex markup applied to it', (assert) => { }); test('a marker can have the same complex markup tagName applied twice, even with different attributes', (assert) => { - const m1 = new Marker('hi there!'); - const markup1 = new Markup('a', {href:'blah'}); - const markup2 = new Markup('a', {href:'blah2'}); + const m1 = builder.createMarker('hi there!'); + const markup1 = builder.createMarkup('a', {href:'blah'}); + const markup2 = builder.createMarkup('a', {href:'blah2'}); m1.addMarkup(markup1); m1.addMarkup(markup2); @@ -64,10 +67,10 @@ test('a marker can have the same complex markup tagName applied twice, even with }); test('a marker can be joined to another', (assert) => { - const m1 = new Marker('hi'); - m1.addMarkup(new Markup('b')); - const m2 = new Marker(' there!'); - m2.addMarkup(new Markup('i')); + const m1 = builder.createMarker('hi'); + m1.addMarkup(builder.createMarkup('b')); + const m2 = builder.createMarker(' there!'); + m2.addMarkup(builder.createMarkup('i')); const m3 = m1.join(m2); assert.equal(m3.value, 'hi there!'); @@ -75,35 +78,40 @@ test('a marker can be joined to another', (assert) => { assert.ok(m3.hasMarkup('i')); }); -test('#split splits a marker in 2 when no endOffset is passed', (assert) => { - const m1 = new Marker('hi there!'); - m1.addMarkup(new Markup('b')); +test('#split splits a marker in 3 with blank markers when no endOffset is passed', (assert) => { + const m1 = builder.createMarker('hi there!'); + m1.addMarkup(builder.createMarkup('b')); + + const [beforeMarker, ...afterMarkers] = m1.split(5); - const [_m1, m2] = m1.split(5); - assert.ok(_m1.hasMarkup('b') && m2.hasMarkup('b'), - 'both markers get the markup'); + assert.ok(beforeMarker.hasMarkup('b')); + afterMarkers.forEach(m => assert.ok(m.hasMarkup('b'))); - assert.equal(_m1.value, 'hi th'); - assert.equal(m2.value, 'ere!'); + assert.equal(beforeMarker.value, 'hi th'); + assert.equal(afterMarkers[0].value, 'ere!'); + assert.ok(afterMarkers[1].empty(), 'final split marker is empty'); }); test('#split splits a marker in 3 when endOffset is passed', (assert) => { - const m = new Marker('hi there!'); - m.addMarkup(new Markup('b')); + const m = builder.createMarker('hi there!'); + m.addMarkup(builder.createMarkup('b')); - const newMarkers = m.split(2, 4); + const [beforeMarker, ...afterMarkers] = m.split(2, 4); - assert.equal(newMarkers.length, 3, 'creates 3 new markers'); - newMarkers.forEach(m => assert.ok(m.hasMarkup('b'), 'marker has markup')); + assert.equal(1 + afterMarkers.length, 3, 'creates 3 new markers'); + assert.ok(beforeMarker.hasMarkup('b'), 'beforeMarker has markup'); + afterMarkers.forEach(m => assert.ok(m.hasMarkup('b'), 'afterMarker has markup')); - assert.equal(newMarkers[0].value, 'hi'); - assert.equal(newMarkers[1].value, ' t'); - assert.equal(newMarkers[2].value, 'here!'); + assert.equal(beforeMarker.value, 'hi'); + assert.equal(afterMarkers[0].value, ' t'); + assert.equal(afterMarkers[1].value, 'here!'); }); -test('#split does not create an empty marker if the offset is 0', (assert) => { - const m = new Marker('hi there!'); - const newMarkers = m.split(0); - assert.equal(newMarkers.length, 1); - assert.equal(newMarkers[0].value, 'hi there!'); +test('#split creates an initial empty marker if the offset is 0', (assert) => { + const m = builder.createMarker('hi there!'); + const [beforeMarker, ...afterMarkers] = m.split(0); + assert.equal(afterMarkers.length, 2, '2 after markers'); + assert.ok(beforeMarker.empty(), 'beforeMarker is empty'); + assert.equal(afterMarkers[0].value, 'hi there!'); + assert.ok(afterMarkers[1].empty(), 'final afterMarker is empty'); }); diff --git a/tests/unit/models/markup-section-test.js b/tests/unit/models/markup-section-test.js index 4143d273d..a7570cdd7 100644 --- a/tests/unit/models/markup-section-test.js +++ b/tests/unit/models/markup-section-test.js @@ -1,26 +1,28 @@ const {module, test} = QUnit; -import Section from 'content-kit-editor/models/markup-section'; -import Marker from 'content-kit-editor/models/marker'; -import Markup from 'content-kit-editor/models/markup'; - -module('Unit: Markup Section'); - -test('Section exists', (assert) => { - assert.ok(Section); +import PostNodeBuilder from 'content-kit-editor/models/post-node-builder'; + +let builder; +module('Unit: Markup Section', { + beforeEach() { + builder = new PostNodeBuilder(); + }, + afterEach() { + builder = null; + } }); test('a section can append a marker', (assert) => { - const s1 = new Section(); - const m1 = new Marker('hello'); + const s1 = builder.createMarkupSection('P'); + const m1 = builder.createMarker('hello'); s1.markers.append(m1); assert.equal(s1.markers.length, 1); }); test('#markerContaining finds the marker at the given offset when 1 marker', (assert) => { - const m = new Marker('hi there!'); - const s = new Section('h2',[m]); + const m = builder.createMarker('hi there!'); + const s = builder.createMarkupSection('h2',[m]); for (let i=0; i { - const m1 = new Marker('hi '); - const m2 = new Marker('there!'); - const s = new Section('h2',[m1,m2]); + const m1 = builder.createMarker('hi '); + const m2 = builder.createMarker('there!'); + const s = builder.createMarkupSection('h2',[m1,m2]); assert.equal(s.markerContaining(0), m1, 'first marker is always found at offset 0'); @@ -53,11 +55,11 @@ test('#markerContaining finds the marker at the given offset when 2 markers', (a }); test('#markerContaining finds the marker at the given offset when multiple markers', (assert) => { - const m1 = new Marker('hi '); - const m2 = new Marker('there!'); - const m3 = new Marker(' and more'); + const m1 = builder.createMarker('hi '); + const m2 = builder.createMarker('there!'); + const m3 = builder.createMarker(' and more'); const markerLength = [m1,m2,m3].reduce((prev, cur) => prev + cur.length, 0); - const s = new Section('h2',[m1,m2,m3]); + const s = builder.createMarkupSection('h2',[m1,m2,m3]); assert.equal(s.markerContaining(0), m1, 'first marker is always found at offset 0'); @@ -87,8 +89,8 @@ test('#markerContaining finds the marker at the given offset when multiple marke }); test('a section can be split, splitting its markers', (assert) => { - const m = new Marker('hi there!', [new Markup('b')]); - const s = new Section('p', [m]); + const m = builder.createMarker('hi there!', [builder.createMarkup('b')]); + const s = builder.createMarkupSection('p', [m]); const [s1, s2] = s.split(5); assert.equal(s1.markers.length, 1, 's1 has marker'); @@ -102,9 +104,9 @@ test('a section can be split, splitting its markers', (assert) => { }); test('a section can be split, splitting its markers when multiple markers', (assert) => { - const m1 = new Marker('hi '); - const m2 = new Marker('there!'); - const s = new Section('h2', [m1,m2]); + const m1 = builder.createMarker('hi '); + const m2 = builder.createMarker('there!'); + const s = builder.createMarkupSection('h2', [m1,m2]); const [s1, s2] = s.split(5); assert.equal(s1.markers.length, 2, 's1 has 2 markers'); @@ -116,21 +118,21 @@ test('a section can be split, splitting its markers when multiple markers', (ass }); test('#splitMarker splits the marker at the offset', (assert) => { - const m1 = new Marker('hi '); - const m2 = new Marker('there!'); - const s = new Section('h2', [m1,m2]); + const m1 = builder.createMarker('hi '); + const m2 = builder.createMarker('there!'); + const s = builder.createMarkupSection('h2', [m1,m2]); s.splitMarker(m2, 3); assert.equal(s.markers.length, 3, 'adds a 3rd marker'); - assert.equal(s.markers.head.value, 'hi ', 'original marker unchanged'); + assert.equal(s.markers.objectAt(0).value, 'hi ', 'original marker unchanged'); assert.equal(s.markers.objectAt(1).value, 'the', 'first half of split'); - assert.equal(s.markers.tail.value, 're!', 'second half of split'); + assert.equal(s.markers.objectAt(2).value, 're!', 'second half of split'); }); test('#splitMarker splits the marker at the end offset if provided', (assert) => { - const m1 = new Marker('hi '); - const m2 = new Marker('there!'); - const s = new Section('h2', [m1,m2]); + const m1 = builder.createMarker('hi '); + const m2 = builder.createMarker('there!'); + const s = builder.createMarkupSection('h2', [m1,m2]); s.splitMarker(m2, 1, 3); assert.equal(s.markers.length, 4, 'adds a marker for the split and has one on each side'); @@ -141,9 +143,9 @@ test('#splitMarker splits the marker at the end offset if provided', (assert) => }); test('#splitMarker does not create an empty marker if offset=0', (assert) => { - const m1 = new Marker('hi '); - const m2 = new Marker('there!'); - const s = new Section('h2', [m1,m2]); + const m1 = builder.createMarker('hi '); + const m2 = builder.createMarker('there!'); + const s = builder.createMarkupSection('h2', [m1,m2]); s.splitMarker(m2, 0); assert.equal(s.markers.length, 2, 'still 2 markers');