Skip to content

Commit

Permalink
Port sections to a linked list
Browse files Browse the repository at this point in the history
  • Loading branch information
mixonic committed Aug 10, 2015
1 parent ff2a9c6 commit 8c6f343
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 69 deletions.
5 changes: 1 addition & 4 deletions src/js/models/cursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,7 @@ 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.takeRange(startSection, endSection);
}

// moves cursor to the start of the section
Expand Down
9 changes: 4 additions & 5 deletions src/js/models/markup-section.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ export const VALID_MARKUP_SECTION_TAGNAMES = [
'p', 'h3', 'h2', 'h1', 'blockquote', 'ul', 'ol'
].map(normalizeTagName);
export const MARKUP_SECTION_TYPE = 'markup-section';
import LinkedItem from "content-kit-editor/utils/linked-item";

export default class Section {
export default class Section extends LinkedItem {
constructor(tagName, markers=[]) {
super();
this.markers = [];
this.tagName = tagName || DEFAULT_TAG_NAME;
this.type = MARKUP_SECTION_TYPE;
Expand Down Expand Up @@ -158,9 +160,6 @@ export default class Section {
}

get nextSibling() {
const index = this.post.sections.indexOf(this);
if (index !== -1) {
return this.post.sections[index+1];
}
return this.next;
}
}
57 changes: 20 additions & 37 deletions src/js/models/post.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
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 = [];
this.sections = new LinkedList({
adoptItem(section) {
section.post = this;
},
freeItem(section) {
section.post = null;
}
});
}
appendSection(section) {
section.post = this;
this.sections.push(section);
this.sections.append(section);
}
prependSection(section) {
section.post = this;
this.sections.unshift(section);
this.sections.prepend(section);
}
replaceSection(section, newSection) {
section.post = this;
this.insertSectionAfter(newSection, section);
this.removeSection(section);
this.sections.insertAfter(newSection, section);
this.sections.remove(section);
}
cutMarkers(markers) {
let firstSection = markers[0].section,
Expand Down Expand Up @@ -80,42 +84,21 @@ export default class Post {
} else if (currentMarker.nextSibling) {
currentMarker = currentMarker.nextSibling;
} else {
let nextSection = currentMarker.section.nextSibling;
let nextSection = currentMarker.section.next;
currentMarker = nextSection && nextSection.markers[0];
}
}
}

insertSectionAfter(section, previousSection) {
section.post = this;
let foundIndex = -1;

for (let i=0; i<this.sections.length; i++) {
if (this.sections[i] === previousSection) {
foundIndex = i;
break;
}
}

this.sections.splice(foundIndex+1, 0, section);
insertSectionAfter(section, nextSection) {
this.sections.insertAfter(section, nextSection);
}

removeSection(section) {
var i, l;
for (i=0,l=this.sections.length;i<l;i++) {
if (this.sections[i] === section) {
this.sections.splice(i, 1);
return;
}
}
this.sections.remove(section);
}

getPreviousSection(section) {
var i, l;
if (this.sections[0] !== section) {
for (i=1,l=this.sections.length;i<l;i++) {
if (this.sections[i] === section) {
return this.sections[i-1];
}
}
}
return section.prev;
}
}
26 changes: 16 additions & 10 deletions src/js/models/render-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@ export default class RenderNode extends LinkedItem {
this.isDirty = true;
this.isRemoved = false;
this.postNode = postNode;
this.childNodes = new LinkedList({
adoptItem: item => {
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;
Expand Down
20 changes: 14 additions & 6 deletions src/js/utils/array-utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
function detect(array, callback) {
for (let i=0; i<array.length; i++) {
if (callback(array[i])) {
return array[i];
function detect(enumerable, callback) {
if (enumerable.detect) {
return enumerable.detect(callback);
} else {
for (let i=0; i<enumerable.length; i++) {
if (callback(enumerable[i])) {
return enumerable[i];
}
}
}
}
Expand All @@ -21,8 +25,12 @@ function any(array, callback) {
* actually arrays, like NodeList
*/
function forEach(enumerable, callback) {
for (let i=0; i<enumerable.length; i++) {
callback(enumerable[i]);
if (enumerable.forEach) {
enumerable.forEach(callback);
} else {
for (let i=0; i<enumerable.length; i++) {
callback(enumerable[i], i);
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/js/utils/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function visitArray(visitor, nodes, opcodes) {
if (!nodes || nodes.length === 0) {
return;
}
for (var i=0, l=nodes.length; i<l; i++) {
visit(visitor, nodes[i], opcodes);
}
nodes.forEach(node => {
visit(visitor, node, opcodes);
});
}
41 changes: 41 additions & 0 deletions src/js/utils/linked-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,32 +54,72 @@ 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
if (item.next) {
item.next.prev = null;
}
this.head = item.next;
didRemove = true;
}
if (item === this.tail) {
// Tail of the list
if (item.prev) {
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;
}
}
takeRange(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.takeRange();
}
detect(callback, item=this.head) {
while (item) {
if (callback(item)) {
return item;
}
item = item.next;
}
}
}
2 changes: 1 addition & 1 deletion tests/acceptance/embed-intent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/parsers/dom-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ test('nested tags (section markup) should create a block', (assert) => {
expectedPost.appendSection(expectedFirst);

assert.deepEqual(post, expectedPost);
let sectionMarkers = post.sections[0].markers;
let sectionMarkers = post.sections.head.markers;
assert.equal(sectionMarkers[2].markups[0], sectionMarkers[3].markups[0]);
});

Expand Down
4 changes: 2 additions & 2 deletions tests/unit/parsers/post-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ 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');
});
Expand All @@ -51,7 +51,7 @@ 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');

Expand Down
Loading

0 comments on commit 8c6f343

Please sign in to comment.