diff --git a/src/js/commands/image.js b/src/js/commands/image.js index 27bb55db4..659e7594c 100644 --- a/src/js/commands/image.js +++ b/src/js/commands/image.js @@ -13,7 +13,7 @@ export default class ImageCommand extends Command { let sections = this.editor.activeSections; let lastSection = sections[sections.length - 1]; let section = builder.createCardSection('image'); - post.insertSectionAfter(section, lastSection); + post.sections.insertAfter(section, lastSection); sections.forEach(section => section.renderNode.scheduleForRemoval()); this.editor.rerender(); diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js index 7cfd9ee77..277de63ce 100644 --- a/src/js/editor/editor.js +++ b/src/js/editor/editor.js @@ -385,7 +385,7 @@ class Editor { } } else { let currentSection = currentMarker.section; - let previousMarker = currentMarker.previousSibling; + let previousMarker = currentMarker.prev; if (previousMarker) { // (B) let markerLength = previousMarker.length; previousMarker.deleteValueAtOffset(markerLength - 1); @@ -394,17 +394,17 @@ class Editor { // * none -- do nothing // * markup section -- join to it // * non-markup section (card) -- select it? delete it? - let previousSection = this.post.getPreviousSection(currentSection); + let previousSection = currentSection.prev; if (previousSection) { let isMarkupSection = previousSection.type === MARKUP_SECTION_TYPE; if (isMarkupSection) { - let previousSectionMarkerLength = previousSection.markers.length; + let lastPreviousMarker = previousSection.markers.tail; previousSection.join(currentSection); previousSection.renderNode.markDirty(); currentSection.renderNode.scheduleForRemoval(); - nextCursorMarker = previousSection.markers[previousSectionMarkerLength]; + nextCursorMarker = lastPreviousMarker.next; nextCursorOffset = 0; /* } else { @@ -449,23 +449,23 @@ class Editor { // FIXME rightMarker is not guaranteed to be there let [leftMarker, rightMarker] = newMarkers; - section.insertMarkerAfter(leftMarker, marker); + section.markers.insertAfter(leftMarker, marker); markerRenderNode.scheduleForRemoval(); const newSection = this.builder.createMarkupSection('p'); - newSection.appendMarker(rightMarker); + newSection.markers.append(rightMarker); let nodeForMove = markerRenderNode.next; while (nodeForMove) { nodeForMove.scheduleForRemoval(); let movedMarker = nodeForMove.postNode.clone(); - newSection.appendMarker(movedMarker); + newSection.markers.append(movedMarker); - nodeForMove = nodeForMove.nextSibling; + nodeForMove = nodeForMove.next; } const post = this.post; - post.insertSectionAfter(newSection, section); + post.sections.insertAfter(newSection, section); this.rerender(); this.trigger('update'); @@ -657,8 +657,8 @@ class Editor { sectionRenderNode.markClean(); let previousSectionRenderNode = previousSection && previousSection.renderNode; - this.post.insertSectionAfter(section, previousSection); - this._renderTree.node.insertAfter(sectionRenderNode, previousSectionRenderNode); + this.post.sections.insertAfter(section, previousSection); + this._renderTree.node.childNodes.insertAfter(sectionRenderNode, previousSectionRenderNode); } // may cause duplicates to be included @@ -802,16 +802,12 @@ class Editor { let newRenderNode = this._renderTree.buildRenderNode(newSection); let renderNodes = this.cursor.activeSections.map(s => s.renderNode); let lastRenderNode = renderNodes[renderNodes.length-1]; - lastRenderNode.parentNode.insertAfter(newRenderNode, lastRenderNode); - this.post.insertSectionAfter(newSection, lastRenderNode.postNode); + lastRenderNode.parent.childNodes.insertAfter(newRenderNode, lastRenderNode); + this.post.sections.insertAfter(newSection, lastRenderNode.postNode); renderNodes.forEach(renderNode => renderNode.scheduleForRemoval()); this.trigger('update'); } - removeSection(section) { - this.post.removeSection(section); - } - destroy() { this.removeAllEventListeners(); this.removeAllViews(); diff --git a/src/js/models/card.js b/src/js/models/card.js index 820ac42df..ab2f7aa2b 100644 --- a/src/js/models/card.js +++ b/src/js/models/card.js @@ -1,7 +1,10 @@ +import LinkedItem from "content-kit-editor/utils/linked-item"; + export const CARD_TYPE = 'card-section'; -export default class Card { +export default class Card extends LinkedItem { constructor(name, payload) { + super(); this.name = name; this.payload = payload; this.type = CARD_TYPE; diff --git a/src/js/models/cursor.js b/src/js/models/cursor.js index f70f3c0d1..88df32b58 100644 --- a/src/js/models/cursor.js +++ b/src/js/models/cursor.js @@ -98,15 +98,12 @@ export default class Cursor { const {result:startSection} = detectParentNode(startContainer, isSectionElement); const {result:endSection} = detectParentNode(endContainer, isSectionElement); - const startIndex = sections.indexOf(startSection), - endIndex = sections.indexOf(endSection) + 1; - - return sections.slice(startIndex, endIndex); + return sections.readRange(startSection, endSection); } // moves cursor to the start of the section moveToSection(section) { - const marker = section.markers[0]; + const marker = section.markers.head; if (!marker) { throw new Error('Cannot move cursor to section without a marker'); } const markerElement = marker.renderNode.element; @@ -124,8 +121,8 @@ export default class Cursor { const startSection = sections[0], endSection = sections[sections.length - 1]; - const startNode = startSection.markers[0].renderNode.element, - endNode = endSection.markers[endSection.markers.length - 1].renderNode.element; + const startNode = startSection.markers.head.renderNode.element, + endNode = endSection.markers.tail.renderNode.element; const startOffset = 0, endOffset = endNode.textContent.length; diff --git a/src/js/models/marker.js b/src/js/models/marker.js index 4a087c44b..8ce20b41e 100644 --- a/src/js/models/marker.js +++ b/src/js/models/marker.js @@ -7,9 +7,11 @@ import { detect, difference } from 'content-kit-editor/utils/array-utils'; +import LinkedItem from "content-kit-editor/utils/linked-item"; -const Marker = class Marker { +const Marker = class Marker extends LinkedItem { constructor(value='', markups=[]) { + super(); this.value = value; this.markups = []; this.type = MARKER_TYPE; @@ -107,29 +109,13 @@ const Marker = class Marker { } get openedMarkups() { - let previousMarkups = this.previousSibling && this.previousSibling.markups; - return difference(this.markups, previousMarkups || []); + return difference(this.markups, (this.prev ? this.prev.markups : [])); } get closedMarkups() { - let nextMarkups = this.nextSibling && this.nextSibling.markups; - return difference(this.markups, nextMarkups || []); + return difference(this.markups, (this.next ? this.next.markups : [])); } - // FIXME this should be implemented as a linked list - get nextSibling() { - let index = this.section.markers.indexOf(this); - if (index > -1 && index < this.section.markers.length-1) { - return this.section.markers[index + 1]; - } - } - - get previousSibling() { - let index = this.section.markers.indexOf(this); - if (index > 0) { - return this.section.markers[index - 1]; - } - } }; export default Marker; diff --git a/src/js/models/markup-section.js b/src/js/models/markup-section.js index 24164668b..1a65588bc 100644 --- a/src/js/models/markup-section.js +++ b/src/js/models/markup-section.js @@ -7,15 +7,25 @@ export const VALID_MARKUP_SECTION_TAGNAMES = [ 'p', 'h3', 'h2', 'h1', 'blockquote', 'ul', 'ol' ].map(normalizeTagName); export const MARKUP_SECTION_TYPE = 'markup-section'; +import LinkedList from "content-kit-editor/utils/linked-list"; +import LinkedItem from "content-kit-editor/utils/linked-item"; -export default class Section { +export default class Section extends LinkedItem { constructor(tagName, markers=[]) { - this.markers = []; + super(); + this.markers = new LinkedList({ + adoptItem: (marker) => { + marker.section = this; + }, + freeItem: (marker) => { + marker.section = null; + } + }); this.tagName = tagName || DEFAULT_TAG_NAME; this.type = MARKUP_SECTION_TYPE; this.element = null; - markers.forEach(m => this.appendMarker(m)); + markers.forEach(m => this.markers.append(m)); } set tagName(val) { @@ -49,50 +59,10 @@ export default class Section { */ splitMarker(marker, offset, endOffset=marker.length) { const newMarkers = marker.split(offset, endOffset); - this.replaceMarker(marker, newMarkers); + this.markers.splice(marker, 1, newMarkers); return newMarkers; } - replaceMarker(oldMarker, newMarkers=[]) { - let previousMarker = oldMarker; - - let i = newMarkers.length; - while (i--) { - let currentMarker = newMarkers[i]; - this.insertMarkerAfter(currentMarker, previousMarker); - } - - this.removeMarker(oldMarker); - } - - prependMarker(marker) { - marker.section = this; - this.markers.unshift(marker); - } - - appendMarker(marker) { - marker.section = this; - this.markers.push(marker); - } - - removeMarker(marker) { - const index = this.markers.indexOf(marker); - if (index === -1) { - throw new Error('Cannot remove not-found marker'); - } - this.markers.splice(index, 1); - } - - insertMarkerAfter(marker, previousMarker) { - const index = this.markers.indexOf(previousMarker); - if (index === -1) { - throw new Error('Cannot insert marker after: ' + previousMarker); - } - - marker.section = this; - this.markers.splice(index + 1, 0, marker); - } - /** * @return {Array} 2 new sections */ @@ -107,12 +77,9 @@ export default class Section { new this.constructor(this.tagName, []) ]; } - const middleIndex = this.markers.indexOf(middle); - for (let i=0; i middleIndex) { right.push(this.markers[i]); } - } + left = (middle.prev ? this.markers.readRange(null, middle.prev) : []); + right = (middle.next ? this.markers.readRange(middle.next, null) : []); let leftLength = left.reduce((prev, cur) => prev + cur.length, 0); let middleOffset = offset - leftLength; @@ -129,7 +96,9 @@ export default class Section { // mutates this by appending the other section's (cloned) markers to it join(otherSection) { - otherSection.markers.forEach(m => this.appendMarker(m.clone())); + otherSection.markers.forEach(m => { + this.markers.append(m.clone()); + }); } /** @@ -141,26 +110,27 @@ export default class Section { * @return {Marker} The marker that contains this offset */ markerContaining(offset, leftInclusive=true) { - var length=0, i=0; + let length = 0; + let lastMarker = null; - if (offset === 0) { return this.markers[0]; } - - while (length < offset && i < this.markers.length) { - length += this.markers[i].length; - i++; + if (offset === 0) { + return this.markers.head; } + this.markers.detect((marker) => { + if (length < offset) { + lastMarker = marker; + length += marker.length; + return false; + } else { + return true; // stop iteration + } + }); + if (length > offset) { - return this.markers[i-1]; + return lastMarker; } else if (length === offset) { - return this.markers[leftInclusive ? i : i-1]; - } - } - - get nextSibling() { - const index = this.post.sections.indexOf(this); - if (index !== -1) { - return this.post.sections[index+1]; + return (leftInclusive ? lastMarker.next : lastMarker); } } } diff --git a/src/js/models/post.js b/src/js/models/post.js index c5659c7d2..df5b37f9c 100644 --- a/src/js/models/post.js +++ b/src/js/models/post.js @@ -1,23 +1,17 @@ export const POST_TYPE = 'post'; +import LinkedList from "content-kit-editor/utils/linked-list"; -// FIXME: making sections a linked-list would greatly improve this export default class Post { constructor() { this.type = POST_TYPE; - this.sections = []; - } - appendSection(section) { - section.post = this; - this.sections.push(section); - } - prependSection(section) { - section.post = this; - this.sections.unshift(section); - } - replaceSection(section, newSection) { - section.post = this; - this.insertSectionAfter(newSection, section); - this.removeSection(section); + this.sections = new LinkedList({ + adoptItem(section) { + section.post = this; + }, + freeItem(section) { + section.post = null; + } + }); } cutMarkers(markers) { let firstSection = markers[0].section, @@ -27,7 +21,7 @@ export default class Post { let removedSections = [], changedSections = [firstSection, lastSection]; - let previousMarker = markers[0].previousSibling; + let previousMarker = markers[0].prev; markers.forEach(marker => { if (marker.section !== currentSection) { // this marker is in a section we haven't seen yet @@ -39,13 +33,13 @@ export default class Post { } currentSection = marker.section; - currentSection.removeMarker(marker); + currentSection.markers.remove(marker); }); // add a blank marker to any sections that are now empty changedSections.forEach(section => { if (section.isEmpty()) { - section.appendMarker(this.builder.createBlankMarker()); + section.markers.append(this.builder.createBlankMarker()); } }); @@ -55,7 +49,7 @@ export default class Post { currentMarker = previousMarker; currentOffset = currentMarker.length; } else { - currentMarker = firstSection.markers[0]; + currentMarker = firstSection.markers.head; currentOffset = 0; } @@ -77,44 +71,11 @@ export default class Post { if (currentMarker === endMarker) { currentMarker = null; - } else if (currentMarker.nextSibling) { - currentMarker = currentMarker.nextSibling; + } else if (currentMarker.next) { + currentMarker = currentMarker.next; } else { - let nextSection = currentMarker.section.nextSibling; - currentMarker = nextSection && nextSection.markers[0]; - } - } - } - - insertSectionAfter(section, previousSection) { - section.post = this; - let foundIndex = -1; - - for (let i=0; i { - item.parent = this; - item.renderTree = this.renderTree; - }, - freeItem: item => { - item.parent = null; - item.renderTree = null; - } - }); + this._childNodes = null; + } + get childNodes() { + if (!this._childNodes) { + this._childNodes = new LinkedList({ + adoptItem: item => { + item.parent = this; + item.renderTree = this.renderTree; + }, + freeItem: item => { + item.parent = null; + item.renderTree = null; + } + }); + } + return this._childNodes; } scheduleForRemoval() { this.isRemoved = true; @@ -34,15 +40,6 @@ export default class RenderNode extends LinkedItem { markClean() { this.isDirty = false; } - appendChild(child) { - this.childNodes.append(child); - } - removeChild(child) { - this.childNodes.remove(child); - } - insertAfter(node, prev) { - this.childNodes.insertAfter(node, prev); - } set element(element) { this._element = element; this.renderTree.elements.set(element, this); diff --git a/src/js/parsers/dom.js b/src/js/parsers/dom.js index e6acbe9ae..ebc339657 100644 --- a/src/js/parsers/dom.js +++ b/src/js/parsers/dom.js @@ -77,23 +77,23 @@ function parseMarkers(section, builder, topNode) { if (currentNode.firstChild) { if (isValidMarkerElement(currentNode) && text !== null) { - section.appendMarker(builder.createMarker(text, markups.slice())); + section.markers.append(builder.createMarker(text, markups.slice())); text = null; } currentNode = currentNode.firstChild; } else if (currentNode.nextSibling) { if (currentNode === topNode) { - section.appendMarker(builder.createMarker(text, markups.slice())); + section.markers.append(builder.createMarker(text, markups.slice())); break; } else { currentNode = currentNode.nextSibling; if (currentNode.nodeType === ELEMENT_NODE && isValidMarkerElement(currentNode) && text !== null) { - section.appendMarker(builder.createMarker(text, markups.slice())); + section.markers.append(builder.createMarker(text, markups.slice())); text = null; } } } else { - section.appendMarker(builder.createMarker(text, markups.slice())); + section.markers.append(builder.createMarker(text, markups.slice())); while (currentNode && !currentNode.nextSibling && currentNode !== topNode) { currentNode = currentNode.parentNode; @@ -169,7 +169,7 @@ NewHTMLParser.prototype = { if (!isEmptyTextNode(sectionElement)) { section = this.parseSection(previousSection, sectionElement); if (section !== previousSection) { - post.appendSection(section); + post.sections.append(section); previousSection = section; } } diff --git a/src/js/parsers/mobiledoc.js b/src/js/parsers/mobiledoc.js index ec1904950..23fa41be7 100644 --- a/src/js/parsers/mobiledoc.js +++ b/src/js/parsers/mobiledoc.js @@ -55,17 +55,17 @@ export default class MobiledocParser { parseCardSection([type, name, payload], post) { const section = this.builder.createCardSection(name, payload); - post.appendSection(section); + post.sections.append(section); } parseImageSection([type, src], post) { const section = this.builder.createImageSection(src); - post.appendSection(section); + post.sections.append(section); } parseMarkupSection([type, tagName, markers], post) { const section = this.builder.createMarkupSection(tagName); - post.appendSection(section); + post.sections.append(section); this.parseMarkers(markers, section); } @@ -78,7 +78,7 @@ export default class MobiledocParser { this.markups.push(this.markerTypes[index]); }); const marker = this.builder.createMarker(value, this.markups.slice()); - section.appendMarker(marker); + section.markers.append(marker); this.markups = this.markups.slice(0, this.markups.length-closeCount); } } diff --git a/src/js/parsers/post.js b/src/js/parsers/post.js index 4daa861dd..ffe7397de 100644 --- a/src/js/parsers/post.js +++ b/src/js/parsers/post.js @@ -21,7 +21,7 @@ export default class PostParser { const post = this.builder.createPost(); forEach(element.childNodes, child => { - post.appendSection(this.sectionParser.parse(child)); + post.sections.append(this.sectionParser.parse(child)); }); return post; @@ -99,12 +99,12 @@ export default class PostParser { if (previousMarker) { // insert this marker after the previous one - section.insertMarkerAfter(marker, previousMarker); - section.renderNode.insertAfter(renderNode, previousMarker.renderNode); + section.markers.insertAfter(marker, previousMarker); + section.renderNode.childNodes.insertAfter(renderNode, previousMarker.renderNode); } else { // insert marker at the beginning of the section - section.prependMarker(marker); - section.renderNode.insertAfter(renderNode, null); + section.markers.prepend(marker); + section.renderNode.childNodes.insertAfter(renderNode, null); } // find the nextMarkerElement, set it on the render node @@ -128,7 +128,7 @@ export default class PostParser { renderNode.scheduleForRemoval(); } - renderNode = renderNode.nextSibling; + renderNode = renderNode.next; } } } diff --git a/src/js/parsers/section.js b/src/js/parsers/section.js index 86dcdbc51..1e08ad504 100644 --- a/src/js/parsers/section.js +++ b/src/js/parsers/section.js @@ -35,11 +35,11 @@ export default class SectionParser { // close a trailing text nodes if it exists if (state.text.length) { let marker = this.builder.createMarker(state.text, state.markups); - state.section.appendMarker(marker); + state.section.markers.append(marker); } if (section.markers.length === 0) { - section.appendMarker(this.builder.createBlankMarker()); + section.markers.append(this.builder.createBlankMarker()); } return section; @@ -64,7 +64,7 @@ export default class SectionParser { if (state.text.length) { // close previous text marker let marker = this.builder.createMarker(state.text, state.markups); - state.section.appendMarker(marker); + state.section.markers.append(marker); state.text = ''; } @@ -79,7 +79,7 @@ export default class SectionParser { // close the marker started for this node and pop // its markup from the stack let marker = this.builder.createMarker(state.text, state.markups); - state.section.appendMarker(marker); + state.section.markers.append(marker); state.markups.pop(); state.text = ''; } diff --git a/src/js/renderers/editor-dom.js b/src/js/renderers/editor-dom.js index 52d8c0e7a..a8f06a3e5 100644 --- a/src/js/renderers/editor-dom.js +++ b/src/js/renderers/editor-dom.js @@ -78,7 +78,7 @@ function renderMarker(marker, element, previousRenderNode) { let previousSibling = previousRenderNode.element; let previousSiblingPenultimate = penultimateParentOf(previousSibling, nextMarkerElement); - nextMarkerElement.insertBefore(currentElement, previousSiblingPenultimate.nextSibling); + nextMarkerElement.insertBefore(currentElement, previousSiblingPenultimate.next); } else { element.insertBefore(currentElement, element.firstChild); } @@ -205,7 +205,7 @@ let destroyHooks = { }, [MARKUP_SECTION_TYPE](renderNode, section) { let post = renderNode.parent.postNode; - post.removeSection(section); + post.sections.remove(section); // Some formatting commands remove the element from the DOM during // formatting. Do not error if this is the case. if (renderNode.element.parentNode) { @@ -223,10 +223,8 @@ let destroyHooks = { element = element.parentNode; } - // FIXME is it ok that this marker may have already been removed from the - // section? - if (marker.section.markers.indexOf(marker) !== -1) { - marker.section.removeMarker(marker); + if (marker.section) { + marker.section.markers.remove(marker); } if (element.parentNode) { @@ -237,7 +235,7 @@ let destroyHooks = { [IMAGE_SECTION_TYPE](renderNode, section) { let post = renderNode.parent.postNode; - post.removeSection(section); + post.sections.remove(section); renderNode.element.parentNode.removeChild(renderNode.element); }, @@ -246,7 +244,7 @@ let destroyHooks = { renderNode.cardNode.teardown(); } let post = renderNode.parent.postNode; - post.removeSection(section); + post.sections.remove(section); renderNode.element.parentNode.removeChild(renderNode.element); } }; @@ -258,7 +256,7 @@ function removeChildren(parentNode) { let nextChild = child.next; if (child.isRemoved) { destroyHooks[child.postNode.type](child, child.postNode); - parentNode.removeChild(child); + parentNode.childNodes.remove(child); } child = nextChild; } @@ -271,7 +269,7 @@ function lookupNode(renderTree, parentNode, postNode, previousNode) { return postNode.renderNode; } else { let renderNode = new RenderNode(postNode); - parentNode.insertAfter(renderNode, previousNode); + parentNode.childNodes.insertAfter(renderNode, previousNode); postNode.renderNode = renderNode; return renderNode; } diff --git a/src/js/utils/array-utils.js b/src/js/utils/array-utils.js index b4f38a4c5..78f4be88d 100644 --- a/src/js/utils/array-utils.js +++ b/src/js/utils/array-utils.js @@ -1,7 +1,11 @@ -function detect(array, callback) { - for (let i=0; i { + visit(visitor, node, opcodes); + }); } diff --git a/src/js/utils/linked-list.js b/src/js/utils/linked-list.js index 25e72996d..4c4c58ada 100644 --- a/src/js/utils/linked-list.js +++ b/src/js/utils/linked-list.js @@ -2,6 +2,7 @@ export default class LinkedList { constructor(options) { this.head = null; this.tail = null; + this.length = 0; if (options) { let {adoptItem, freeItem} = options; this.adoptItem = adoptItem; @@ -53,15 +54,18 @@ export default class LinkedList { } this.tail = item; } + this.length++; } remove(item) { if (this.freeItem) { this.freeItem(item); } + let didRemove = false; if (item.next && item.prev) { // Middle of the list item.next.prev = item.prev; item.prev.next = item.next; + didRemove = true; } else { if (item === this.head) { // Head of the list @@ -69,6 +73,7 @@ export default class LinkedList { item.next.prev = null; } this.head = item.next; + didRemove = true; } if (item === this.tail) { // Tail of the list @@ -76,9 +81,66 @@ export default class LinkedList { item.prev.next = null; } this.tail = item.prev; + didRemove = true; } } + if (didRemove) { + this.length--; + } item.prev = null; item.next = null; } + forEach(callback) { + let item = this.head; + let index = 0; + while (item) { + callback(item, index); + index++; + item = item.next; + } + } + readRange(startItem, endItem) { + let items = []; + let item = startItem || this.head; + while (item) { + items.push(item); + if (item === endItem) { + break; + } + item = item.next; + } + return items; + } + toArray() { + return this.readRange(); + } + detect(callback, item=this.head) { + while (item) { + if (callback(item)) { + return item; + } + item = item.next; + } + } + objectAt(targetIndex) { + let index = -1; + return this.detect(() => { + index++; + return (targetIndex === index); + }); + } + splice(targetItem, removalCount, newItems) { + let item = targetItem; + let nextItem = item.next; + let count = 0; + while (item && count < removalCount) { + count++; + nextItem = item.next; + this.remove(item); + item = nextItem; + } + newItems.forEach((newItem) => { + this.insertBefore(newItem, nextItem); + }); + } } diff --git a/tests/acceptance/embed-intent-test.js b/tests/acceptance/embed-intent-test.js index 55147d490..7a1bb2d52 100644 --- a/tests/acceptance/embed-intent-test.js +++ b/tests/acceptance/embed-intent-test.js @@ -61,7 +61,7 @@ Helpers.skipInPhantom('typing inserts section', (assert) => { assert.ok($('.ck-embed-intent').is(':visible'), 'embed intent appears'); }); -Helpers.skipInPhantom('add card between sections', (assert) => { +Helpers.skipInPhantom('add image card between sections', (assert) => { editor = new Editor(editorElement, {mobiledoc: mobileDocWith3Sections}); assert.equal(editorElement.childNodes.length, 3, 'has 3 paragraphs to start'); diff --git a/tests/unit/models/markup-section-test.js b/tests/unit/models/markup-section-test.js index 03981973b..4143d273d 100644 --- a/tests/unit/models/markup-section-test.js +++ b/tests/unit/models/markup-section-test.js @@ -14,7 +14,7 @@ test('a section can append a marker', (assert) => { const s1 = new Section(); const m1 = new Marker('hello'); - s1.appendMarker(m1); + s1.markers.append(m1); assert.equal(s1.markers.length, 1); }); @@ -94,11 +94,11 @@ test('a section can be split, splitting its markers', (assert) => { assert.equal(s1.markers.length, 1, 's1 has marker'); assert.equal(s2.markers.length, 1, 's2 has marker'); - assert.ok(s1.markers[0].hasMarkup('b')); - assert.equal(s1.markers[0].value, 'hi th'); + assert.ok(s1.markers.head.hasMarkup('b')); + assert.equal(s1.markers.head.value, 'hi th'); - assert.ok(s2.markers[0].hasMarkup('b')); - assert.equal(s2.markers[0].value, 'ere!'); + assert.ok(s2.markers.head.hasMarkup('b')); + assert.equal(s2.markers.head.value, 'ere!'); }); test('a section can be split, splitting its markers when multiple markers', (assert) => { @@ -110,9 +110,9 @@ test('a section can be split, splitting its markers when multiple markers', (ass assert.equal(s1.markers.length, 2, 's1 has 2 markers'); assert.equal(s2.markers.length, 1, 's2 has 1 marker'); - assert.equal(s1.markers[0].value, 'hi '); - assert.equal(s1.markers[1].value, 'th'); - assert.equal(s2.markers[0].value, 'ere!'); + assert.equal(s1.markers.head.value, 'hi '); + assert.equal(s1.markers.tail.value, 'th'); + assert.equal(s2.markers.head.value, 'ere!'); }); test('#splitMarker splits the marker at the offset', (assert) => { @@ -122,9 +122,9 @@ test('#splitMarker splits the marker at the offset', (assert) => { s.splitMarker(m2, 3); assert.equal(s.markers.length, 3, 'adds a 3rd marker'); - assert.equal(s.markers[0].value, 'hi ', 'original marker unchanged'); - assert.equal(s.markers[1].value, 'the'); - assert.equal(s.markers[2].value, 're!'); + assert.equal(s.markers.head.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'); }); test('#splitMarker splits the marker at the end offset if provided', (assert) => { @@ -134,10 +134,10 @@ test('#splitMarker splits the marker at the end offset if provided', (assert) => s.splitMarker(m2, 1, 3); assert.equal(s.markers.length, 4, 'adds a marker for the split and has one on each side'); - assert.equal(s.markers[0].value, 'hi ', 'original marker unchanged'); - assert.equal(s.markers[1].value, 't'); - assert.equal(s.markers[2].value, 'he'); - assert.equal(s.markers[3].value, 're!'); + assert.equal(s.markers.head.value, 'hi ', 'original marker unchanged'); + assert.equal(s.markers.objectAt(1).value, 't'); + assert.equal(s.markers.objectAt(2).value, 'he'); + assert.equal(s.markers.tail.value, 're!'); }); test('#splitMarker does not create an empty marker if offset=0', (assert) => { @@ -147,6 +147,6 @@ test('#splitMarker does not create an empty marker if offset=0', (assert) => { s.splitMarker(m2, 0); assert.equal(s.markers.length, 2, 'still 2 markers'); - assert.equal(s.markers[0].value, 'hi ', 'original 1st marker unchanged'); - assert.equal(s.markers[1].value, 'there!', 'original 2nd marker unchanged'); + assert.equal(s.markers.head.value, 'hi ', 'original 1st marker unchanged'); + assert.equal(s.markers.tail.value, 'there!', 'original 2nd marker unchanged'); }); diff --git a/tests/unit/parsers/dom-test.js b/tests/unit/parsers/dom-test.js index 1c3081ba4..297dab7e7 100644 --- a/tests/unit/parsers/dom-test.js +++ b/tests/unit/parsers/dom-test.js @@ -33,11 +33,11 @@ test('blank textnodes are ignored', (assert) => { let post = parser.parse(buildDOM('

first line

\n

second line

')); let expectedFirst = builder.createMarkupSection('P'); - expectedFirst.appendMarker(builder.createMarker('first line')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('first line')); + expectedPost.sections.append(expectedFirst); let expectedSecond = builder.createMarkupSection('P'); - expectedSecond.appendMarker(builder.createMarker('second line')); - expectedPost.appendSection(expectedSecond); + expectedSecond.markers.append(builder.createMarker('second line')); + expectedPost.sections.append(expectedSecond); assert.deepEqual(post, expectedPost); }); @@ -46,11 +46,11 @@ test('textnode adjacent to p tag becomes section', (assert) => { const post = parser.parse(buildDOM('

first line

second line')); let expectedFirst = builder.createMarkupSection('P'); - expectedFirst.appendMarker(builder.createMarker('first line')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('first line')); + expectedPost.sections.append(expectedFirst); let expectedSecond = builder.createMarkupSection('P', [], true); - expectedSecond.appendMarker(builder.createMarker('second line')); - expectedPost.appendSection(expectedSecond); + expectedSecond.markers.append(builder.createMarker('second line')); + expectedPost.sections.append(expectedSecond); assert.deepEqual(post, expectedPost); }); @@ -59,8 +59,8 @@ test('p tag (section markup) should create a block', (assert) => { const post = parser.parse(buildDOM('

text

')); let expectedFirst = builder.createMarkupSection('P'); - expectedFirst.appendMarker(builder.createMarker('text')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('text')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -69,10 +69,10 @@ test('strong tag (stray markup) without a block should create a block', (assert) const post = parser.parse(buildDOM('text')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('text', [ + expectedFirst.markers.append(builder.createMarker('text', [ builder.createMarkup('STRONG') ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -82,13 +82,13 @@ test('strong tag with inner em (stray markup) without a block should create a bl let expectedFirst = builder.createMarkupSection('P', [], true); let strong = builder.createMarkup('STRONG'); - expectedFirst.appendMarker(builder.createMarker('stray', [ + expectedFirst.markers.append(builder.createMarker('stray', [ strong, builder.createMarkup('EM') ])); - expectedFirst.appendMarker(builder.createMarker(' markup tags', [strong])); - expectedFirst.appendMarker(builder.createMarker('.')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker(' markup tags', [strong])); + expectedFirst.markers.append(builder.createMarker('.')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -97,8 +97,8 @@ test('stray text (stray markup) should create a block', (assert) => { const post = parser.parse(buildDOM('text')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('text')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('text')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -107,12 +107,12 @@ test('text node, strong tag, text node (stray markup) without a block should cre const post = parser.parse(buildDOM('start bold end')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('start ')); - expectedFirst.appendMarker(builder.createMarker('bold', [ + expectedFirst.markers.append(builder.createMarker('start ')); + expectedFirst.markers.append(builder.createMarker('bold', [ builder.createMarkup('STRONG') ])); - expectedFirst.appendMarker(builder.createMarker(' end')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker(' end')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -121,10 +121,10 @@ test('italic tag (stray markup) without a block should create a block', (assert) const post = parser.parse(buildDOM('text')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('text', [ + expectedFirst.markers.append(builder.createMarker('text', [ builder.createMarkup('EM') ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -133,8 +133,8 @@ test('u tag (stray markup) without a block should strip U and create a block', ( const post = parser.parse(buildDOM('text')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('text')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('text')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -144,10 +144,10 @@ test('a tag (stray markup) without a block should create a block', (assert) => { const post = parser.parse(buildDOM('text')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('text', [ + expectedFirst.markers.append(builder.createMarker('text', [ builder.createMarkup('A', ['href', url]) ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -157,9 +157,9 @@ test('markup: break', (assert) => { const post = parser.parse(buildDOM('line
break')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('line ')); - expectedFirst.appendMarker(builder.createMarker('break')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('line ')); + expectedFirst.markers.append(builder.createMarker('break')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -169,9 +169,9 @@ test('sub tag (stray markup) without a block should filter SUB and create a bloc const post = parser.parse(buildDOM('footnote1')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('footnote')); - expectedFirst.appendMarker(builder.createMarker('1')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('footnote')); + expectedFirst.markers.append(builder.createMarker('1')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -180,9 +180,9 @@ test('sup tag (stray markup) without a block should filter SUP and create a bloc const post = parser.parse(buildDOM('e=mc2')); let expectedFirst = builder.createMarkupSection('P', [], true); - expectedFirst.appendMarker(builder.createMarker('e=mc')); - expectedFirst.appendMarker(builder.createMarker('2')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('e=mc')); + expectedFirst.markers.append(builder.createMarker('2')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -191,13 +191,13 @@ test('list (stray markup) without a block should create a block', (assert) => { const post = parser.parse(buildDOM('
  • Item 1
  • Item 2
')); let expectedFirst = builder.createMarkupSection('UL'); - expectedFirst.appendMarker(builder.createMarker('Item 1', [ + expectedFirst.markers.append(builder.createMarker('Item 1', [ builder.createMarkup('LI') ])); - expectedFirst.appendMarker(builder.createMarker('Item 2', [ + expectedFirst.markers.append(builder.createMarker('Item 2', [ builder.createMarkup('LI') ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -206,41 +206,41 @@ test('nested tags (section markup) should create a block', (assert) => { const post = parser.parse(buildDOM('

Double. Double staggered start. Double staggered end. Double staggered middle.

')); let expectedFirst = builder.createMarkupSection('P'); - expectedFirst.appendMarker(builder.createMarker('Double.', [ + expectedFirst.markers.append(builder.createMarker('Double.', [ builder.createMarkup('EM'), builder.createMarkup('STRONG') ])); - expectedFirst.appendMarker(builder.createMarker(' ')); + expectedFirst.markers.append(builder.createMarker(' ')); let firstStrong = builder.createMarkup('STRONG'); - expectedFirst.appendMarker(builder.createMarker('Double staggered', [ + expectedFirst.markers.append(builder.createMarker('Double staggered', [ firstStrong, builder.createMarkup('EM') ])); - expectedFirst.appendMarker(builder.createMarker(' start.', [firstStrong])); - expectedFirst.appendMarker(builder.createMarker(' ')); + expectedFirst.markers.append(builder.createMarker(' start.', [firstStrong])); + expectedFirst.markers.append(builder.createMarker(' ')); let secondStrong = builder.createMarkup('STRONG'); - expectedFirst.appendMarker(builder.createMarker('Double ', [ + expectedFirst.markers.append(builder.createMarker('Double ', [ secondStrong ])); - expectedFirst.appendMarker(builder.createMarker('staggered end.', [ + expectedFirst.markers.append(builder.createMarker('staggered end.', [ secondStrong, builder.createMarkup('EM') ])); - expectedFirst.appendMarker(builder.createMarker(' ')); + expectedFirst.markers.append(builder.createMarker(' ')); let thirdStrong = builder.createMarkup('STRONG'); - expectedFirst.appendMarker(builder.createMarker('Double ', [ + expectedFirst.markers.append(builder.createMarker('Double ', [ thirdStrong ])); - expectedFirst.appendMarker(builder.createMarker('staggered', [ + expectedFirst.markers.append(builder.createMarker('staggered', [ thirdStrong, builder.createMarkup('EM') ])); - expectedFirst.appendMarker(builder.createMarker(' middle.', [thirdStrong])); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker(' middle.', [thirdStrong])); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); - let sectionMarkers = post.sections[0].markers; - assert.equal(sectionMarkers[2].markups[0], sectionMarkers[3].markups[0]); + let sectionMarkers = post.sections.head.markers; + assert.equal(sectionMarkers.objectAt(2).markups[0], sectionMarkers.objectAt(3).markups[0]); }); /* @@ -345,10 +345,10 @@ test('attributes', (assert) => { const post = parser.parse(buildDOM(`

Link to google.com

`)); let expectedFirst = builder.createMarkupSection('P'); - expectedFirst.appendMarker(builder.createMarker('Link to google.com', [ + expectedFirst.markers.append(builder.createMarker('Link to google.com', [ builder.createMarkup('A', ['href', href, 'rel', rel]) ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -357,10 +357,10 @@ test('attributes filters out inline styles and classes', (assert) => { const post = parser.parse(buildDOM('

test

')); let expectedFirst = builder.createMarkupSection('P'); - expectedFirst.appendMarker(builder.createMarker('test', [ + expectedFirst.markers.append(builder.createMarker('test', [ builder.createMarkup('B') ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -369,8 +369,8 @@ test('blocks: paragraph', (assert) => { const post = parser.parse(buildDOM('

TEXT

')); let expectedFirst = builder.createMarkupSection('P'); - expectedFirst.appendMarker(builder.createMarker('TEXT')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('TEXT')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -379,8 +379,8 @@ test('blocks: heading', (assert) => { const post = parser.parse(buildDOM('

TEXT

')); let expectedFirst = builder.createMarkupSection('H2'); - expectedFirst.appendMarker(builder.createMarker('TEXT')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('TEXT')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -389,8 +389,8 @@ test('blocks: subheading', (assert) => { const post = parser.parse(buildDOM('

TEXT

')); let expectedFirst = builder.createMarkupSection('H3'); - expectedFirst.appendMarker(builder.createMarker('TEXT')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('TEXT')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -414,8 +414,8 @@ test('blocks: quote', (assert) => { const post = parser.parse(buildDOM('
quote
')); let expectedFirst = builder.createMarkupSection('BLOCKQUOTE'); - expectedFirst.appendMarker(builder.createMarker('quote')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker('quote')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -424,14 +424,14 @@ test('blocks: list', (assert) => { const post = parser.parse(buildDOM('
  • Item 1
  • Item 2
')); let expectedFirst = builder.createMarkupSection('UL'); - expectedFirst.appendMarker(builder.createMarker('Item 1', [ + expectedFirst.markers.append(builder.createMarker('Item 1', [ builder.createMarkup('LI') ])); - expectedFirst.appendMarker(builder.createMarker(' ')); - expectedFirst.appendMarker(builder.createMarker('Item 2', [ + expectedFirst.markers.append(builder.createMarker(' ')); + expectedFirst.markers.append(builder.createMarker('Item 2', [ builder.createMarkup('LI') ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -440,14 +440,14 @@ test('blocks: ordered list', (assert) => { const post = parser.parse(buildDOM('
  1. Item 1
  2. Item 2
')); let expectedFirst = builder.createMarkupSection('OL'); - expectedFirst.appendMarker(builder.createMarker('Item 1', [ + expectedFirst.markers.append(builder.createMarker('Item 1', [ builder.createMarkup('LI') ])); - expectedFirst.appendMarker(builder.createMarker(' ')); - expectedFirst.appendMarker(builder.createMarker('Item 2', [ + expectedFirst.markers.append(builder.createMarker(' ')); + expectedFirst.markers.append(builder.createMarker('Item 2', [ builder.createMarkup('LI') ])); - expectedPost.appendSection(expectedFirst); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); @@ -507,13 +507,13 @@ test('converts tags to mapped values', (assert) => { let expectedFirst = builder.createMarkupSection('P'); let bold = builder.createMarkup('B'); - expectedFirst.appendMarker(builder.createMarker('Converts', [ + expectedFirst.markers.append(builder.createMarker('Converts', [ bold, builder.createMarkup('I') ])); - expectedFirst.appendMarker(builder.createMarker(' tags', [bold])); - expectedFirst.appendMarker(builder.createMarker('.')); - expectedPost.appendSection(expectedFirst); + expectedFirst.markers.append(builder.createMarker(' tags', [bold])); + expectedFirst.markers.append(builder.createMarker('.')); + expectedPost.sections.append(expectedFirst); assert.deepEqual(post, expectedPost); }); diff --git a/tests/unit/parsers/mobiledoc-test.js b/tests/unit/parsers/mobiledoc-test.js index f008c9886..f398151f3 100644 --- a/tests/unit/parsers/mobiledoc-test.js +++ b/tests/unit/parsers/mobiledoc-test.js @@ -43,8 +43,8 @@ test('#parse doc without marker types', (assert) => { let section = builder.createMarkupSection('P', [], false); let marker = builder.createMarker('hello world'); - section.appendMarker(marker); - post.appendSection(section); + section.markers.append(marker); + post.sections.append(section); assert.deepEqual( parsed, @@ -80,8 +80,8 @@ test('#parse doc with marker type', (assert) => { builder.createMarker('brave new', [aMarkerType, bMarkerType]), builder.createMarker('world', [aMarkerType]) ]; - markers.forEach(marker => section.appendMarker(marker)); - post.appendSection(section); + markers.forEach(marker => section.markers.append(marker)); + post.sections.append(section); assert.deepEqual( parsed, @@ -103,7 +103,7 @@ test('#parse doc with image section', (assert) => { const parsed = parser.parse(mobiledoc); let section = builder.createImageSection(DATA_URL); - post.appendSection(section); + post.sections.append(section); assert.deepEqual( parsed, post @@ -124,7 +124,7 @@ test('#parse doc with custom card type', (assert) => { const parsed = parser.parse(mobiledoc); let section = builder.createCardSection('custom-card'); - post.appendSection(section); + post.sections.append(section); assert.deepEqual( parsed, post diff --git a/tests/unit/parsers/post-test.js b/tests/unit/parsers/post-test.js index 515234977..a80dad8cd 100644 --- a/tests/unit/parsers/post-test.js +++ b/tests/unit/parsers/post-test.js @@ -30,9 +30,9 @@ test('#parse can parse a section element', (assert) => { assert.ok(post, 'gets post'); assert.equal(post.sections.length, 1, 'has 1 section'); - const s1 = post.sections[0]; + const s1 = post.sections.head; assert.equal(s1.markers.length, 1, 's1 has 1 marker'); - assert.equal(s1.markers[0].value, 'some text', 'has text'); + assert.equal(s1.markers.head.value, 'some text', 'has text'); }); test('#parse can parse multiple elements', (assert) => { @@ -51,11 +51,11 @@ test('#parse can parse multiple elements', (assert) => { assert.ok(post, 'gets post'); assert.equal(post.sections.length, 2, 'has 2 sections'); - const [s1, s2] = post.sections; + const [s1, s2] = post.sections.toArray(); assert.equal(s1.markers.length, 1, 's1 has 1 marker'); - assert.equal(s1.markers[0].value, 'some text'); + assert.equal(s1.markers.head.value, 'some text'); assert.equal(s2.markers.length, 1, 's2 has 1 marker'); - assert.equal(s2.markers[0].value, 'some other text'); + assert.equal(s2.markers.head.value, 'some other text'); }); diff --git a/tests/unit/parsers/section-test.js b/tests/unit/parsers/section-test.js index fc365ec25..0ec51be26 100644 --- a/tests/unit/parsers/section-test.js +++ b/tests/unit/parsers/section-test.js @@ -29,7 +29,7 @@ test('#parse parses simple dom', (assert) => { const section = parser.parse(element); assert.equal(section.tagName, 'p'); assert.equal(section.markers.length, 2, 'has 2 markers'); - const [m1, m2] = section.markers; + const [m1, m2] = section.markers.toArray(); assert.equal(m1.value, 'hello there'); assert.equal(m2.value, 'i am bold'); @@ -51,7 +51,7 @@ test('#parse parses nested markups', (assert) => { const section = parser.parse(element); assert.equal(section.markers.length, 3, 'has 3 markers'); - const [m1, m2, m3] = section.markers; + const [m1, m2, m3] = section.markers.toArray(); assert.equal(m1.value, 'i am bold'); assert.equal(m2.value, 'i am bold and italic'); @@ -74,7 +74,7 @@ test('#parse ignores non-markup elements like spans', (assert) => { const section = parser.parse(element); assert.equal(section.tagName, 'p'); assert.equal(section.markers.length, 1, 'has 1 markers'); - const [m1] = section.markers; + const [m1] = section.markers.toArray(); assert.equal(m1.value, 'i was in span'); }); @@ -89,7 +89,7 @@ test('#parse reads attributes', (assert) => { ); const section = parser.parse(element); assert.equal(section.markers.length, 1, 'has 1 markers'); - const [m1] = section.markers; + const [m1] = section.markers.toArray(); assert.equal(m1.value, 'i am a link'); assert.ok(m1.hasMarkup('a'), 'has "a" markup'); assert.equal(m1.getMarkup('a').attributes.href, 'google.com'); @@ -110,7 +110,7 @@ test('#parse joins contiguous text nodes separated by non-markup elements', (ass const section = parser.parse(element); assert.equal(section.tagName, 'p'); assert.equal(section.markers.length, 1, 'has 1 markers'); - const [m1] = section.markers; + const [m1] = section.markers.toArray(); assert.equal(m1.value, 'span 1span 2'); }); diff --git a/tests/unit/renderers/editor-dom-test.js b/tests/unit/renderers/editor-dom-test.js index a69f093c7..2c2dffa49 100644 --- a/tests/unit/renderers/editor-dom-test.js +++ b/tests/unit/renderers/editor-dom-test.js @@ -40,9 +40,9 @@ test("It renders a dirty post", (assert) => { test("It renders a dirty post with un-rendered sections", (assert) => { let post = builder.createPost(); let sectionA = builder.createMarkupSection('P'); - post.appendSection(sectionA); + post.sections.append(sectionA); let sectionB = builder.createMarkupSection('P'); - post.appendSection(sectionB); + post.sections.append(sectionB); let renderNode = new RenderNode(post); let renderTree = new RenderTree(renderNode); @@ -80,7 +80,7 @@ test("It renders a dirty post with un-rendered sections", (assert) => { test(`Remove nodes with ${testInfo.name} section`, (assert) => { let post = builder.createPost(); let section = testInfo.section(builder); - post.appendSection(section); + post.sections.append(section); let postElement = document.createElement('div'); let sectionElement = document.createElement('p'); @@ -95,7 +95,7 @@ test("It renders a dirty post with un-rendered sections", (assert) => { let sectionRenderNode = renderTree.buildRenderNode(section); sectionRenderNode.element = sectionElement; sectionRenderNode.scheduleForRemoval(); - postRenderNode.appendChild(sectionRenderNode); + postRenderNode.childNodes.append(sectionRenderNode); render(renderTree); @@ -113,8 +113,8 @@ test("It renders a dirty post with un-rendered sections", (assert) => { test('renders a post with marker', (assert) => { let post = builder.createPost(); let section = builder.createMarkupSection('P'); - post.appendSection(section); - section.appendMarker( + post.sections.append(section); + section.markers.append( builder.createMarker('Hi', [ builder.createMarkup('STRONG') ]) @@ -130,19 +130,19 @@ test('renders a post with marker', (assert) => { test('renders a post with multiple markers', (assert) => { let post = builder.createPost(); let section = builder.createMarkupSection('P'); - post.appendSection(section); + post.sections.append(section); let b = builder.createMarkup('B'); let i = builder.createMarkup('I'); - section.appendMarker(builder.createMarker('hello ')); - section.appendMarker( + section.markers.append(builder.createMarker('hello ')); + section.markers.append( builder.createMarker('bold, ', [b]) ); - section.appendMarker( + section.markers.append( builder.createMarker('italic,', [b,i]) ); - section.appendMarker( + section.markers.append( builder.createMarker(' world.') ); @@ -158,7 +158,7 @@ test('renders a post with image', (assert) => { let url = DATA_URL; let post = builder.createPost(); let section = builder.createImageSection(url); - post.appendSection(section); + post.sections.append(section); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -178,7 +178,7 @@ test('renders a card section', (assert) => { } } }; - post.appendSection(cardSection); + post.sections.append(cardSection); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -202,7 +202,7 @@ test('renders a card section into a non-contenteditable element', (assert) => { } } }; - post.appendSection(cardSection); + post.sections.append(cardSection); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -248,9 +248,9 @@ test('rerender a marker after adding a markup to it', (assert) => { const marker1 = builder.createMarker('text1', [b]); const marker2 = builder.createMarker('text2'); - section.appendMarker(marker1); - section.appendMarker(marker2); - post.appendSection(section); + section.markers.append(marker1); + section.markers.append(marker2); + post.sections.append(section); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -277,9 +277,9 @@ test('rerender a marker after removing a markup from it', (assert) => { const marker1 = builder.createMarker('text1'); const marker2 = builder.createMarker('text2', [bMarkup]); - section.appendMarker(marker1); - section.appendMarker(marker2); - post.appendSection(section); + section.markers.append(marker1); + section.markers.append(marker2); + post.sections.append(section); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -306,9 +306,9 @@ test('rerender a marker after removing a markup from it (when changed marker is const marker1 = builder.createMarker('text1', [bMarkup]); const marker2 = builder.createMarker('text2'); - section.appendMarker(marker1); - section.appendMarker(marker2); - post.appendSection(section); + section.markers.append(marker1); + section.markers.append(marker2); + post.sections.append(section); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -335,9 +335,9 @@ test('rerender a marker after removing a markup from it (when both markers have const marker1 = builder.createMarker('text1', [bMarkup]); const marker2 = builder.createMarker('text2', [bMarkup]); - section.appendMarker(marker1); - section.appendMarker(marker2); - post.appendSection(section); + section.markers.append(marker1); + section.markers.append(marker2); + post.sections.append(section); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -364,9 +364,9 @@ test('rerender a marker after removing a markup from it (when both markers have const marker1 = builder.createMarker('text1', [bMarkup]); const marker2 = builder.createMarker('text2', [bMarkup]); - section.appendMarker(marker1); - section.appendMarker(marker2); - post.appendSection(section); + section.markers.append(marker1); + section.markers.append(marker2); + post.sections.append(section); let node = new RenderNode(post); let renderTree = new RenderTree(node); @@ -400,8 +400,8 @@ test('render when contiguous markers have out-of-order markups', (assert) => { ]; const m1 = markers[0]; - markers.forEach(m => section.appendMarker(m)); - post.appendSection(section); + markers.forEach(m => section.markers.append(m)); + post.sections.append(section); let node = new RenderNode(post); let renderTree = new RenderTree(node); diff --git a/tests/unit/renderers/mobiledoc-test.js b/tests/unit/renderers/mobiledoc-test.js index 25d0c2cf8..289a809d2 100644 --- a/tests/unit/renderers/mobiledoc-test.js +++ b/tests/unit/renderers/mobiledoc-test.js @@ -27,8 +27,8 @@ test('renders a blank post', (assert) => { test('renders a post with marker', (assert) => { let post = builder.createPost(); let section = builder.createMarkupSection('P'); - post.appendSection(section); - section.appendMarker( + post.sections.append(section); + section.markers.append( builder.createMarker('Hi', [ builder.createMarkup('STRONG') ]) @@ -52,14 +52,14 @@ test('renders a post with marker', (assert) => { test('renders a post section with markers sharing a markup', (assert) => { let post = builder.createPost(); let section = builder.createMarkupSection('P'); - post.appendSection(section); + post.sections.append(section); let markup = builder.createMarkup('STRONG'); - section.appendMarker( + section.markers.append( builder.createMarker('Hi', [ markup ]) ); - section.appendMarker( + section.markers.append( builder.createMarker(' Guy', [ markup ]) @@ -85,7 +85,7 @@ test('renders a post with image', (assert) => { let url = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="; let post = builder.createPost(); let section = builder.createImageSection(url); - post.appendSection(section); + post.sections.append(section); let mobiledoc = render(post); assert.deepEqual(mobiledoc, { @@ -102,7 +102,7 @@ test('renders a post with image', (assert) => { test('renders a post with image and null src', (assert) => { let post = builder.createPost(); let section = builder.createImageSection(); - post.appendSection(section); + post.sections.append(section); let mobiledoc = render(post); assert.deepEqual(mobiledoc, { @@ -121,7 +121,7 @@ test('renders a post with card', (assert) => { let payload = { bar: 'baz' }; let post = builder.createPost(); let section = builder.createCardSection(cardName, payload); - post.appendSection(section); + post.sections.append(section); let mobiledoc = render(post); assert.deepEqual(mobiledoc, { diff --git a/tests/unit/utils/linked-list-test.js b/tests/unit/utils/linked-list-test.js index 7df611b75..ea87c9a82 100644 --- a/tests/unit/utils/linked-list-test.js +++ b/tests/unit/utils/linked-list-test.js @@ -9,6 +9,7 @@ test('initial state', (assert) => { let list = new LinkedList(); assert.equal(list.head, null, 'head is null'); assert.equal(list.tail, null ,'tail is null'); + assert.equal(list.length, 0, 'length is one'); }); ['append', 'prepend', 'insertBefore', 'insertAfter'].forEach(method => { @@ -16,6 +17,7 @@ test('initial state', (assert) => { let list = new LinkedList(); let item = new LinkedItem(); list[method](item); + assert.equal(list.length, 1, 'length is one'); assert.equal(list.head, item, 'head is item'); assert.equal(list.tail, item, 'tail is item'); assert.equal(item.next, null, 'item next is null'); @@ -41,6 +43,7 @@ test(`#append second item`, (assert) => { let itemTwo = new LinkedItem(); list.append(itemOne); list.append(itemTwo); + assert.equal(list.length, 2, 'length is two'); assert.equal(list.head, itemOne, 'head is itemOne'); assert.equal(list.tail, itemTwo, 'tail is itemTwo'); assert.equal(itemOne.prev, null, 'itemOne prev is null'); @@ -55,6 +58,7 @@ test(`#prepend first item`, (assert) => { let itemTwo = new LinkedItem(); list.prepend(itemTwo); list.prepend(itemOne); + assert.equal(list.length, 2, 'length is two'); assert.equal(list.head, itemOne, 'head is itemOne'); assert.equal(list.tail, itemTwo, 'tail is itemTwo'); assert.equal(itemOne.prev, null, 'itemOne prev is null'); @@ -71,6 +75,7 @@ test(`#insertBefore a middle item`, (assert) => { list.prepend(itemOne); list.append(itemThree); list.insertBefore(itemTwo, itemThree); + assert.equal(list.length, 3, 'length is three'); assert.equal(list.head, itemOne, 'head is itemOne'); assert.equal(list.tail, itemThree, 'tail is itemThree'); assert.equal(itemOne.prev, null, 'itemOne prev is null'); @@ -104,6 +109,7 @@ test(`#remove an only item`, (assert) => { let item = new LinkedItem(); list.append(item); list.remove(item); + assert.equal(list.length, 0, 'length is zero'); assert.equal(list.head, null, 'head is null'); assert.equal(list.tail, null, 'tail is null'); assert.equal(item.prev, null, 'item prev is null'); @@ -130,6 +136,7 @@ test(`#remove a first item`, (assert) => { list.append(itemOne); list.append(itemTwo); list.remove(itemOne); + assert.equal(list.length, 1, 'length is one'); assert.equal(list.head, itemTwo, 'head is itemTwo'); assert.equal(list.tail, itemTwo, 'tail is itemTwo'); assert.equal(itemOne.prev, null, 'itemOne prev is null'); @@ -171,3 +178,145 @@ test(`#remove a middle item`, (assert) => { assert.equal(itemThree.prev, itemOne, 'itemThree prev is itemOne'); assert.equal(itemThree.next, null, 'itemThree next is null'); }); + +test(`#forEach iterates many`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + let itemTwo = new LinkedItem(); + let itemThree = new LinkedItem(); + list.append(itemOne); + list.append(itemTwo); + list.append(itemThree); + let items = []; + let indexes = []; + list.forEach((item, index) => { + items.push(item); + indexes.push(index); + }); + assert.deepEqual(items, [itemOne, itemTwo, itemThree], 'items correct'); + assert.deepEqual(indexes, [0, 1, 2], 'indexes correct'); +}); + +test(`#forEach iterates one`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + list.append(itemOne); + let items = []; + let indexes = []; + list.forEach((item, index) => { + items.push(item); + indexes.push(index); + }); + assert.deepEqual(items, [itemOne], 'items correct'); + assert.deepEqual(indexes, [0], 'indexes correct'); +}); + +test(`#readRange walks from start to end`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + let itemTwo = new LinkedItem(); + let itemThree = new LinkedItem(); + list.append(itemOne); + list.append(itemTwo); + list.append(itemThree); + let items = []; + let indexes = []; + list.forEach((item, index) => { + items.push(item); + indexes.push(index); + }); + assert.deepEqual(list.readRange(itemOne, itemOne), [itemOne], 'items correct'); + assert.deepEqual(list.readRange(itemTwo, itemThree), [itemTwo, itemThree], 'items correct'); + assert.deepEqual(list.readRange(itemOne, itemTwo), [itemOne, itemTwo], 'items correct'); + assert.deepEqual(list.readRange(itemOne, null), [itemOne, itemTwo, itemThree], 'items correct'); +}); + +test(`#toArray builds array`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + list.append(itemOne); + assert.deepEqual(list.toArray(), [itemOne], 'items correct'); +}); + +test(`#toArray builds many array`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + let itemTwo = new LinkedItem(); + let itemThree = new LinkedItem(); + list.append(itemOne); + list.append(itemTwo); + list.append(itemThree); + assert.deepEqual(list.toArray(), [itemOne, itemTwo, itemThree], 'items correct'); +}); + +test(`#detect finds`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + let itemTwo = new LinkedItem(); + let itemThree = new LinkedItem(); + list.append(itemOne); + list.append(itemTwo); + list.append(itemThree); + assert.equal(list.detect(item => item === itemOne), itemOne, 'itemOne detected'); + assert.equal(list.detect(item => item === itemTwo), itemTwo, 'itemTwo detected'); + assert.equal(list.detect(item => item === itemThree), itemThree, 'itemThree detected'); + assert.equal(list.detect(() => false), undefined, 'no item detected'); +}); + +test(`#objectAt looks up by index`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + list.append(itemOne); + assert.equal(list.objectAt(0), itemOne, 'itemOne looked up'); + + let itemTwo = new LinkedItem(); + let itemThree = new LinkedItem(); + list.append(itemTwo); + list.append(itemThree); + assert.equal(list.objectAt(0), itemOne, 'itemOne looked up'); + assert.equal(list.objectAt(1), itemTwo, 'itemTwo looked up'); + assert.equal(list.objectAt(2), itemThree, 'itemThree looked up'); +}); + +test(`#splice removes a target and inserts an array of items`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + let itemTwo = new LinkedItem(); + let itemThree = new LinkedItem(); + list.append(itemOne); + list.append(itemThree); + + list.splice(itemOne, 1, [itemTwo]); + + assert.equal(list.head, itemTwo, 'itemOne is head'); + assert.equal(list.objectAt(1), itemThree, 'itemThree is present'); +}); + +test(`#splice remove nothing and inserts an array of nothing`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + let itemTwo = new LinkedItem(); + list.append(itemOne); + list.append(itemTwo); + + list.splice(itemTwo, 0, []); + + assert.equal(list.head, itemOne, 'itemOne is head'); + assert.equal(list.objectAt(1), itemTwo, 'itemTwo is present'); +}); + +test(`#splice can reorganize items`, (assert) => { + let list = new LinkedList(); + let itemOne = new LinkedItem(); + let itemTwo = new LinkedItem(); + let itemThree = new LinkedItem(); + list.append(itemOne); + list.append(itemTwo); + list.append(itemThree); + + list.splice(itemOne, 3, [itemThree, itemOne, itemTwo]); + + assert.equal(list.head, itemThree, 'itemThree is head'); + assert.equal(list.objectAt(1), itemOne, 'itemOne is present'); + assert.equal(list.objectAt(2), itemTwo, 'itemTwo is present'); +});