diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js index 91c053882..e201b7031 100644 --- a/src/js/editor/editor.js +++ b/src/js/editor/editor.js @@ -333,15 +333,15 @@ class Editor { if (currentMarker.length === 0 && currentMarker.section.markers.length > 1) { leftRenderNode.scheduleForRemoval(); - let isFirstRenderNode = leftRenderNode === leftRenderNode.parentNode.firstChild; + let isFirstRenderNode = leftRenderNode === leftRenderNode.parent.childNodes.head; if (isFirstRenderNode) { // move cursor to start of next node - nextCursorMarker = leftRenderNode.nextSibling.postNode; + nextCursorMarker = leftRenderNode.next.postNode; nextCursorOffset = 0; } else { // move cursor to end of prev node - nextCursorMarker = leftRenderNode.previousSibling.postNode; - nextCursorOffset = leftRenderNode.previousSibling.postNode.length; + nextCursorMarker = leftRenderNode.prev.postNode; + nextCursorOffset = leftRenderNode.prev.postNode.length; } } else { leftRenderNode.markDirty(); @@ -418,7 +418,7 @@ class Editor { const newSection = this.builder.createMarkupSection('p'); newSection.appendMarker(rightMarker); - let nodeForMove = markerRenderNode.nextSibling; + let nodeForMove = markerRenderNode.next; while (nodeForMove) { nodeForMove.scheduleForRemoval(); let movedMarker = nodeForMove.postNode.clone(); diff --git a/src/js/models/render-node.js b/src/js/models/render-node.js index da4fa7ae2..9cbc85c4b 100644 --- a/src/js/models/render-node.js +++ b/src/js/models/render-node.js @@ -1,75 +1,47 @@ -export default class RenderNode { +import LinkedItem from "content-kit-editor/utils/linked-item"; +import LinkedList from "content-kit-editor/utils/linked-list"; + +export default class RenderNode extends LinkedItem { constructor(postNode) { - this.parentNode = null; + super(); + this.parent = null; this.isDirty = true; this.isRemoved = false; this.postNode = postNode; - - this.firstChild = null; - this.lastChild = null; - this.nextSibling = null; - this.previousSibling = null; + this.childNodes = new LinkedList({ + adoptItem: item => { + item.parent = this; + item.renderTree = this.renderTree; + }, + freeItem: item => { + item.parent = null; + item.renderTree = null; + } + }); } scheduleForRemoval() { this.isRemoved = true; - if (this.parentNode) { - this.parentNode.markDirty(); + if (this.parent) { + this.parent.markDirty(); } } markDirty() { this.isDirty = true; - if (this.parentNode) { - this.parentNode.markDirty(); + if (this.parent) { + this.parent.markDirty(); } } markClean() { this.isDirty = false; } appendChild(child) { - if (!this.firstChild) { - this.firstChild = child; - } - if (this.lastChild) { - child.previousSibling = this.lastChild; - this.lastChild.nextSibling = child; - } - this.lastChild = child; - child.parentNode = this; - child.renderTree = this.renderTree; + this.childNodes.append(child); } removeChild(child) { - if (child.nextSibling) { - child.nextSibling.previousSibling = child.previousSibling; - } else { - this.lastChild = child.previousSibling; - } - if (child.previousSibling) { - child.previousSibling.nextSibling = child.nextSibling; - } else { - this.firstChild = child.nextSibling; - } + this.childNodes.remove(child); } - insertAfter(node, previousChild) { - if (previousChild) { - node.previousSibling = previousChild; - if (previousChild.nextSibling) { - previousChild.nextSibling.previousSibling = node; - node.nextSibling = previousChild.nextSibling; - } else { - this.lastChild = node; - } - previousChild.nextSibling = node; - } else { - node.nextSibling = this.firstChild; - if (node.nextSibling) { - node.nextSibling.previousSibling = node; - } else { - this.lastChild = node; - } - this.firstChild = node; - } - node.parentNode = this; - node.renderTree = this.renderTree; + insertAfter(node, prev) { + this.childNodes.insertAfter(node, prev); } set element(element) { this._element = element; diff --git a/src/js/renderers/editor-dom.js b/src/js/renderers/editor-dom.js index f68973c03..52d8c0e7a 100644 --- a/src/js/renderers/editor-dom.js +++ b/src/js/renderers/editor-dom.js @@ -114,18 +114,18 @@ class Visitor { if (!hasRendered) { let element = renderNode.element; - if (renderNode.previousSibling) { - let previousElement = renderNode.previousSibling.element; + if (renderNode.prev) { + let previousElement = renderNode.prev.element; let nextElement = previousElement.nextSibling; if (nextElement) { nextElement.parentNode.insertBefore(element, nextElement); } } if (!element.parentNode) { - renderNode.parentNode.element.appendChild(element); + renderNode.parent.element.appendChild(element); } } else { - renderNode.parentNode.element.replaceChild(element, originalElement); + renderNode.parent.element.replaceChild(element, originalElement); } // remove all elements so that we can rerender @@ -138,12 +138,12 @@ class Visitor { [MARKER_TYPE](renderNode, marker) { let parentElement; - if (renderNode.previousSibling) { - parentElement = getNextMarkerElement(renderNode.previousSibling); + if (renderNode.prev) { + parentElement = getNextMarkerElement(renderNode.prev); } else { - parentElement = renderNode.parentNode.element; + parentElement = renderNode.parent.element; } - let textNode = renderMarker(marker, parentElement, renderNode.previousSibling); + let textNode = renderMarker(marker, parentElement, renderNode.prev); renderNode.element = textNode; } @@ -156,15 +156,15 @@ class Visitor { } else { let element = document.createElement('img'); element.src = section.src; - if (renderNode.previousSibling) { - let previousElement = renderNode.previousSibling.element; + if (renderNode.prev) { + let previousElement = renderNode.prev.element; let nextElement = previousElement.nextSibling; if (nextElement) { nextElement.parentNode.insertBefore(element, nextElement); } } if (!element.parentNode) { - renderNode.parentNode.element.appendChild(element); + renderNode.parent.element.appendChild(element); } renderNode.element = element; } @@ -178,15 +178,15 @@ class Visitor { const element = document.createElement('div'); element.contentEditable = 'false'; renderNode.element = element; - if (renderNode.previousSibling) { - let previousElement = renderNode.previousSibling.element; + if (renderNode.prev) { + let previousElement = renderNode.prev.element; let nextElement = previousElement.nextSibling; if (nextElement) { nextElement.parentNode.insertBefore(element, nextElement); } } if (!element.parentNode) { - renderNode.parentNode.element.appendChild(element); + renderNode.parent.element.appendChild(element); } if (card) { @@ -204,7 +204,7 @@ let destroyHooks = { throw new Error('post destruction is not supported by the renderer'); }, [MARKUP_SECTION_TYPE](renderNode, section) { - let post = renderNode.parentNode.postNode; + let post = renderNode.parent.postNode; post.removeSection(section); // Some formatting commands remove the element from the DOM during // formatting. Do not error if this is the case. @@ -236,7 +236,7 @@ let destroyHooks = { }, [IMAGE_SECTION_TYPE](renderNode, section) { - let post = renderNode.parentNode.postNode; + let post = renderNode.parent.postNode; post.removeSection(section); renderNode.element.parentNode.removeChild(renderNode.element); }, @@ -245,7 +245,7 @@ let destroyHooks = { if (renderNode.cardNode) { renderNode.cardNode.teardown(); } - let post = renderNode.parentNode.postNode; + let post = renderNode.parent.postNode; post.removeSection(section); renderNode.element.parentNode.removeChild(renderNode.element); } @@ -253,9 +253,9 @@ let destroyHooks = { // removes children from parentNode that are scheduled for removal function removeChildren(parentNode) { - let child = parentNode.firstChild; + let child = parentNode.childNodes.head; while (child) { - let nextChild = child.nextSibling; + let nextChild = child.next; if (child.isRemoved) { destroyHooks[child.postNode.type](child, child.postNode); parentNode.removeChild(child); @@ -271,7 +271,6 @@ function lookupNode(renderTree, parentNode, postNode, previousNode) { return postNode.renderNode; } else { let renderNode = new RenderNode(postNode); - renderNode.renderTree = renderTree; parentNode.insertAfter(renderNode, previousNode); postNode.renderNode = renderNode; return renderNode; diff --git a/src/js/utils/linked-list.js b/src/js/utils/linked-list.js index 6c861f27a..25e72996d 100644 --- a/src/js/utils/linked-list.js +++ b/src/js/utils/linked-list.js @@ -1,7 +1,12 @@ export default class LinkedList { - constructor() { + constructor(options) { this.head = null; this.tail = null; + if (options) { + let {adoptItem, freeItem} = options; + this.adoptItem = adoptItem; + this.freeItem = freeItem; + } } prepend(item) { this.insertBefore(item, this.head); @@ -18,6 +23,9 @@ export default class LinkedList { } insertBefore(item, nextItem) { this.remove(item); + if (this.adoptItem) { + this.adoptItem(item); + } if (nextItem && nextItem.prev) { // middle of the items let prevItem = nextItem.prev; @@ -47,6 +55,9 @@ export default class LinkedList { } } remove(item) { + if (this.freeItem) { + this.freeItem(item); + } if (item.next && item.prev) { // Middle of the list item.next.prev = item.prev; diff --git a/tests/unit/renderers/editor-dom-test.js b/tests/unit/renderers/editor-dom-test.js index 4612b94e8..4b33fce1e 100644 --- a/tests/unit/renderers/editor-dom-test.js +++ b/tests/unit/renderers/editor-dom-test.js @@ -53,14 +53,14 @@ test("It renders a dirty post with un-rendered sections", (assert) => { assert.equal(renderTree.node.element.outerHTML, '