Skip to content

Commit

Permalink
Add dom option for renderer, SimpleDOM tests
Browse files Browse the repository at this point in the history
The DOM renderer, HTML renderer, and AMP renderer have significant
duplication. This PR makes the dom renderer codebase SimpleDOM friendly
and tested to be so. The HTML renderer and AMP rendere should be
re-implemented as wrappers around the DOM renderer.
  • Loading branch information
mixonic committed Feb 11, 2016
1 parent 85c42e9 commit 0ed823b
Show file tree
Hide file tree
Showing 11 changed files with 382 additions and 313 deletions.
9 changes: 8 additions & 1 deletion Brocfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@
var multiBuilder = require('broccoli-multi-builder');
var mergeTrees = require('broccoli-merge-trees');
var testBuilder = require('broccoli-test-builder');
var Funnel = require('broccoli-funnel');

var options = {
packageName: 'mobiledoc-dom-renderer'
};

var simpleDOMTree = new Funnel('node_modules/simple-dom/dist/', {
include: ['simple-dom.js'],
destDir: 'tests'
});

module.exports = mergeTrees([
multiBuilder.build('amd', options),
multiBuilder.build('global', options),
multiBuilder.build('commonjs', options),
testBuilder.build()
testBuilder.build(),
simpleDOMTree
]);
5 changes: 2 additions & 3 deletions lib/cards/image.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { createElement } from '../utils/dom';
import RENDER_TYPE from '../utils/render-type';

export default {
name: 'image',
type: RENDER_TYPE,
render({payload}) {
let img = createElement('img');
render({payload, env: {dom}}) {
let img = dom.createElement('img');
img.src = payload.src;
return img;
}
Expand Down
19 changes: 14 additions & 5 deletions lib/renderer-factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,30 @@
cardOptions,
unknownCardHandler,
unknownAtomHandler,
sectionElementRenderer
sectionElementRenderer,
dom
}={}) {
cards = cards || [];
validateCards(cards);
atoms = atoms || [];
validateAtoms(atoms);
cardOptions = cardOptions || {};

this.state = {
if (!dom) {
if (!window) {
throw new Error('A `dom` option must be provided to the renderer when running without window.document');
}
dom = window.document;
}

this.options = {
cards,
atoms,
cardOptions,
unknownCardHandler,
unknownAtomHandler,
sectionElementRenderer
sectionElementRenderer,
dom
};
}

Expand All @@ -71,9 +80,9 @@
case MOBILEDOC_VERSION_0_2:
case undefined:
case null:
return new Renderer_0_2(mobiledoc, this.state).render();
return new Renderer_0_2(mobiledoc, this.options).render();
case MOBILEDOC_VERSION_0_3:
return new Renderer_0_3(mobiledoc, this.state).render();
return new Renderer_0_3(mobiledoc, this.options).render();
default:
throw new Error(`Unexpected Mobiledoc version "${version}"`);
}
Expand Down
62 changes: 32 additions & 30 deletions lib/renderers/0-2.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
import {
createElement,
appendChild,
removeChild,
createTextNode,
setAttribute,
createDocumentFragment
} from '../utils/dom';
import { createTextNode } from '../utils/dom';
import ImageCard from '../cards/image';
import RENDER_TYPE from '../utils/render-type';
import {
Expand All @@ -24,14 +17,14 @@ export const MOBILEDOC_VERSION = '0.2.0';
const IMAGE_SECTION_TAG_NAME = 'img';
const CARD_ELEMENT_TAG_NAME = 'div';

function createElementFromMarkerType([tagName, attributes]=['', []]){
let element = createElement(tagName);
function createElementFromMarkerType(dom, [tagName, attributes]=['', []]){
let element = dom.createElement(tagName);
attributes = attributes || [];

for (let i=0,l=attributes.length; i<l; i=i+2) {
let propName = attributes[i],
propValue = attributes[i+1];
setAttribute(element, propName, propValue);
element.setAttribute(propName, propValue);
}
return element;
}
Expand All @@ -43,13 +36,14 @@ function validateVersion(version) {
}

export default class Renderer {
constructor(mobiledoc, state) {
constructor(mobiledoc, options) {
let {
cards,
cardOptions,
unknownCardHandler,
sectionElementRenderer
} = state;
sectionElementRenderer,
dom
} = options;
let {
version,
sections: sectionData
Expand All @@ -58,7 +52,8 @@ export default class Renderer {

const [markerTypes, sections] = sectionData;

this.root = createDocumentFragment();
this.dom = dom;
this.root = dom.createDocumentFragment();
this.markerTypes = markerTypes;
this.sections = sections;
this.cards = cards;
Expand Down Expand Up @@ -88,11 +83,16 @@ export default class Renderer {
this.sections.forEach(section => {
let rendered = this.renderSection(section);
if (rendered) {
appendChild(this.root, rendered);
this.root.appendChild(rendered);
}
});
// maintain a reference to child nodes so they can be cleaned up later by teardown
this._renderedChildNodes = Array.prototype.slice.call(this.root.childNodes);
this._renderedChildNodes = [];
let node = this.root.firstChild;
while (node) {
this._renderedChildNodes.push(node);
node = node.nextSibling;
}
return { result: this.root, teardown: () => this.teardown() };
}

Expand All @@ -103,7 +103,7 @@ export default class Renderer {
for (let i=0; i < this._renderedChildNodes.length; i++) {
let node = this._renderedChildNodes[i];
if (node.parentNode) {
removeChild(node.parentNode, node);
node.parentNode.removeChild(node);
}
}
}
Expand Down Expand Up @@ -136,16 +136,16 @@ export default class Renderer {
let markerType = this.markerTypes[openTypes[j]];
let [tagName] = markerType;
if (isValidMarkerType(tagName)) {
let openedElement = createElementFromMarkerType(markerType);
appendChild(currentElement, openedElement);
let openedElement = createElementFromMarkerType(this.dom, markerType);
currentElement.appendChild(openedElement);
elements.push(openedElement);
currentElement = openedElement;
} else {
closeCount--;
}
}

appendChild(currentElement, createTextNode(text));
currentElement.appendChild(createTextNode(this.dom, text));

for (let j=0, m=closeCount; j<m; j++) {
elements.pop();
Expand All @@ -155,7 +155,7 @@ export default class Renderer {
}

renderListItem(markers) {
const element = createElement('li');
const element = this.dom.createElement('li');
this.renderMarkersOnElement(element, markers);
return element;
}
Expand All @@ -164,15 +164,15 @@ export default class Renderer {
if (!isValidSectionTagName(tagName, LIST_SECTION_TYPE)) {
return;
}
const element = createElement(tagName);
const element = this.dom.createElement(tagName);
listItems.forEach(li => {
appendChild(element, (this.renderListItem(li)));
element.appendChild(this.renderListItem(li));
});
return element;
}

renderImageSection([type, src]) {
let element = createElement(IMAGE_SECTION_TAG_NAME);
let element = this.dom.createElement(IMAGE_SECTION_TAG_NAME);
element.src = src;
return element;
}
Expand Down Expand Up @@ -201,6 +201,7 @@ export default class Renderer {
let env = {
name: card.name,
isInEditor: false,
dom: this.dom,
onTeardown: (callback) => this._registerTeardownCallback(callback)
};

Expand All @@ -223,13 +224,13 @@ export default class Renderer {
this._validateCardRender(rendered, card.name);

if (rendered) {
appendChild(cardWrapper, rendered);
cardWrapper.appendChild(rendered);
}
return cardWrapper;
}

_createCardElement() {
return createElement(CARD_ELEMENT_TAG_NAME);
return this.dom.createElement(CARD_ELEMENT_TAG_NAME);
}

_validateCardRender(rendered, cardName) {
Expand All @@ -247,13 +248,14 @@ export default class Renderer {
return;
}

let renderer = createElement;
let element;
let lowerCaseTagName = tagName.toLowerCase();
if (this.sectionElementRenderer[lowerCaseTagName]) {
renderer = this.sectionElementRenderer[lowerCaseTagName];
element = this.sectionElementRenderer[lowerCaseTagName](tagName);
} else {
element = this.dom.createElement(tagName);
}

let element = renderer(tagName);
this.renderMarkersOnElement(element, markers);
return element;
}
Expand Down
Loading

0 comments on commit 0ed823b

Please sign in to comment.