Skip to content

Commit

Permalink
Add PostNodeBuilder, remove post-builder, Markup.create
Browse files Browse the repository at this point in the history
  • Loading branch information
bantic committed Aug 6, 2015
1 parent f89d346 commit ad9d9f9
Show file tree
Hide file tree
Showing 19 changed files with 409 additions and 398 deletions.
4 changes: 2 additions & 2 deletions src/js/commands/bold.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import TextFormatCommand from './text-format';
import Markup from '../models/markup';
import {
any
} from '../utils/array-utils';
Expand All @@ -11,7 +10,8 @@ export default class BoldCommand extends TextFormatCommand {
button: '<i class="ck-icon-bold"></i>'
});
this.editor = editor;
this.markup = Markup.create('strong');
const { builder } = this.editor;
this.markup = builder.createMarkup('strong');
}
exec() {
this.editor.applyMarkupToSelection(this.markup);
Expand Down
6 changes: 2 additions & 4 deletions src/js/commands/image.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import Command from './base';
import { generateBuilder } from '../utils/post-builder';

export default class ImageCommand extends Command {
constructor() {
super({
name: 'image',
button: '<i class="ck-icon-image"></i>'
});
this.builder = generateBuilder();
}

exec() {
let {post} = this.editor;
let {post, builder} = this.editor;
let sections = this.editor.activeSections;
let lastSection = sections[sections.length - 1];
let section = this.builder.generateCardSection('image');
let section = builder.createCardSection('image');
post.insertSectionAfter(section, lastSection);
sections.forEach(section => section.renderNode.scheduleForRemoval());

Expand Down
4 changes: 2 additions & 2 deletions src/js/commands/italic.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import TextFormatCommand from './text-format';
import Markup from '../models/markup';
import {
any
} from '../utils/array-utils';
Expand All @@ -11,7 +10,8 @@ export default class ItalicCommand extends TextFormatCommand {
button: '<i class="ck-icon-italic"></i>'
});
this.editor = editor;
this.markup = Markup.create('em');
const { builder } = this.editor;
this.markup = builder.createMarkup('em');
}
exec() {
this.editor.applyMarkupToSelection(this.markup);
Expand Down
10 changes: 6 additions & 4 deletions src/js/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import mixin from '../utils/mixin';
import EventListenerMixin from '../utils/event-listener';
import Cursor from '../models/cursor';
import { MARKUP_SECTION_TYPE } from '../models/markup-section';
import { generateBuilder } from '../utils/post-builder';
import PostNodeBuilder from '../models/post-node-builder';

export const EDITOR_ELEMENT_CLASS_NAME = 'ck-editor';

Expand Down Expand Up @@ -219,12 +219,14 @@ class Editor {
this._views = [];
this.element = element;

this.builder = new PostNodeBuilder();

// FIXME: This should merge onto this.options
mergeWithOptions(this, defaults, options);

this.cards.push(ImageCard);

this._parser = PostParser;
this._parser = new PostParser(this.builder);
this._renderer = new Renderer(this, this.cards, this.unknownCardHandler, this.cardOptions);

this.applyClassName(EDITOR_ELEMENT_CLASS_NAME);
Expand Down Expand Up @@ -287,7 +289,7 @@ class Editor {
}

parseModelFromMobiledoc(mobiledoc) {
this.post = new MobiledocParser().parse(mobiledoc);
this.post = new MobiledocParser(this.builder).parse(mobiledoc);
this._renderTree = new RenderTree();
let node = this._renderTree.buildRenderNode(this.post);
this._renderTree.node = node;
Expand Down Expand Up @@ -413,7 +415,7 @@ class Editor {
section.insertMarkerAfter(leftMarker, marker);
markerRenderNode.scheduleForRemoval();

const newSection = generateBuilder().generateMarkupSection('P');
const newSection = this.builder.createMarkupSection('p');
newSection.appendMarker(rightMarker);

let nodeForMove = markerRenderNode.nextSibling;
Expand Down
18 changes: 0 additions & 18 deletions src/js/models/markup.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ export const VALID_MARKUP_TAGNAMES = [
'li'
].map(normalizeTagName);

const markupMap = {};

class Markup {
/*
* @param {attributes} array flat array of key1,value1,key2,value2,...
Expand All @@ -27,22 +25,6 @@ class Markup {
}
}

// Use `create` to make a new markup so that we can use singletons in the
// markupMap
static create(tagName, attributes=[]) {
tagName = normalizeTagName(tagName);

if (attributes.length === 0) {
if (!markupMap[tagName]) {
markupMap[tagName] = new Markup(tagName);
}

return markupMap[tagName];
} else {
return new Markup(tagName, attributes);
}
}

static isValidElement(element) {
let tagName = normalizeTagName(element.tagName);
return VALID_MARKUP_TAGNAMES.indexOf(tagName) !== -1;
Expand Down
70 changes: 70 additions & 0 deletions src/js/models/post-node-builder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Post from '../models/post';
import MarkupSection from '../models/markup-section';
import ImageSection from '../models/image';
import Marker from '../models/marker';
import Markup from '../models/markup';
import Card from '../models/card';
import { normalizeTagName } from '../utils/dom-utils';

export default class PostNodeBuilder {
constructor() {
this.markupCache = {};
}

createPost() {
const post = new Post();
post.builder = this;
return post;
}

createMarkupSection(tagName, markers=[], isGenerated=false) {
tagName = normalizeTagName(tagName);
const section = new MarkupSection(tagName, markers);
if (isGenerated) {
section.isGenerated = true;
}
return section;
}

createImageSection(url) {
let section = new ImageSection();
if (url) {
section.src = url;
}
return section;
}

createCardSection(name, payload={}) {
return new Card(name, payload);
}

createMarker(value, markups=[]) {
const marker = new Marker(value, markups);
marker.builder = this;
return marker;
}

createBlankMarker() {
return new Marker('__BLANK__');
}

createMarkup(tagName, attributes) {
tagName = normalizeTagName(tagName);

let markup;

if (attributes) {
// FIXME: This could also be cached
markup = new Markup(tagName, attributes);
} else {
if (this.markupCache[tagName]) {
markup = this.markupCache[tagName];
} else {
markup = new Markup(tagName, attributes);
}
}

markup.builder = this;
return markup;
}
}
48 changes: 22 additions & 26 deletions src/js/parsers/dom.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { generateBuilder } from '../utils/post-builder';
import { trim } from 'content-kit-utils';
import { VALID_MARKUP_SECTION_TAGNAMES } from '../models/markup-section';
import { VALID_MARKUP_TAGNAMES } from '../models/markup';
Expand Down Expand Up @@ -36,42 +35,39 @@ function sortAttributes(attributes) {
return sortedAttributes;
}

// FIXME: should probably always return an array
/**
* @return {array} attributes as key1,value1,key2,value2,etc
*/
function readAttributes(node) {
var attributes = null;
var attributes = [];

if (node.hasAttributes()) {
attributes = [];
var i, l;
for (i=0,l=node.attributes.length;i<l;i++) {
if (ALLOWED_ATTRIBUTES.indexOf(node.attributes[i].name) !== -1) {
attributes.push(node.attributes[i].name);
attributes.push(node.attributes[i].value);
}
}
if (attributes.length === 0) {
return null;
} else {
return sortAttributes(attributes);
}
}

return null;
return sortAttributes(attributes);
}

function isValidMarkerElement(element) {
let tagName = normalizeTagName(element.tagName);
return VALID_MARKUP_TAGNAMES.indexOf(tagName) !== -1;
}

function parseMarkers(section, postBuilder, topNode) {
function parseMarkers(section, builder, topNode) {
var markups = [];
var text = null;
var currentNode = topNode;
while (currentNode) {
switch(currentNode.nodeType) {
case ELEMENT_NODE:
if (isValidMarkerElement(currentNode)) {
markups.push(postBuilder.generateMarkup(currentNode.tagName, readAttributes(currentNode)));
markups.push(builder.createMarkup(currentNode.tagName, readAttributes(currentNode)));
}
break;
case TEXT_NODE:
Expand All @@ -81,23 +77,23 @@ function parseMarkers(section, postBuilder, topNode) {

if (currentNode.firstChild) {
if (isValidMarkerElement(currentNode) && text !== null) {
section.appendMarker(postBuilder.generateMarker(markups.slice(), text));
section.appendMarker(builder.createMarker(text, markups.slice()));
text = null;
}
currentNode = currentNode.firstChild;
} else if (currentNode.nextSibling) {
if (currentNode === topNode) {
section.appendMarker(postBuilder.generateMarker(markups.slice(), text));
section.appendMarker(builder.createMarker(text, markups.slice()));
break;
} else {
currentNode = currentNode.nextSibling;
if (currentNode.nodeType === ELEMENT_NODE && isValidMarkerElement(currentNode) && text !== null) {
section.appendMarker(postBuilder.generateMarker(markups.slice(), text));
section.appendMarker(builder.createMarker(text, markups.slice()));
text = null;
}
}
} else {
section.appendMarker(postBuilder.generateMarker(markups.slice(), text));
section.appendMarker(builder.createMarker(text, markups.slice()));

while (currentNode && !currentNode.nextSibling && currentNode !== topNode) {
currentNode = currentNode.parentNode;
Expand All @@ -120,48 +116,48 @@ function parseMarkers(section, postBuilder, topNode) {
}
}

function NewHTMLParser() {
this.postBuilder = generateBuilder();
function NewHTMLParser(builder) {
this.builder = builder;
}

NewHTMLParser.prototype = {
parseSection: function(previousSection, sectionElement) {
var postBuilder = this.postBuilder;
var builder = this.builder;
var section;
switch(sectionElement.nodeType) {
case ELEMENT_NODE:
let tagName = normalizeTagName(sectionElement.tagName);
// <p> <h2>, etc
if (VALID_MARKUP_SECTION_TAGNAMES.indexOf(tagName) !== -1) {
section = postBuilder.generateMarkupSection(tagName, readAttributes(sectionElement));
section = builder.createMarkupSection(tagName);
var node = sectionElement.firstChild;
while (node) {
parseMarkers(section, postBuilder, node);
parseMarkers(section, builder, node);
node = node.nextSibling;
}
// <strong> <b>, etc
} else {
if (previousSection && previousSection.isGenerated) {
section = previousSection;
} else {
section = postBuilder.generateMarkupSection('P', {}, true);
section = builder.createMarkupSection('P', [], true);
}
parseMarkers(section, postBuilder, sectionElement);
parseMarkers(section, builder, sectionElement);
}
break;
case TEXT_NODE:
if (previousSection && previousSection.isGenerated) {
section = previousSection;
} else {
section = postBuilder.generateMarkupSection('P', {}, true);
section = builder.createMarkupSection('P', [], true);
}
parseMarkers(section, postBuilder, sectionElement);
parseMarkers(section, builder, sectionElement);
break;
}
return section;
},
parse: function(postElement) {
var post = this.postBuilder.generatePost();
var post = this.builder.createPost();
var i, l, section, previousSection, sectionElement;
// FIXME: Instead of storing isGenerated on sections, and passing
// the previous section to the parser, we could instead do a two-pass
Expand Down
Loading

0 comments on commit ad9d9f9

Please sign in to comment.