Skip to content

Commit

Permalink
Implement unknownAtomHandler & lifecycle hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
rlivsey authored and mixonic committed Feb 2, 2016
1 parent cfbee1c commit 67e556a
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 48 deletions.
5 changes: 4 additions & 1 deletion src/js/editor/editor.js
Expand Up @@ -56,6 +56,9 @@ const defaults = {
unknownCardHandler: ({env}) => {
throw new Error(`Unknown card encountered: ${env.name}`);
},
unknownAtomHandler: ({env}) => {
throw new Error(`Unknown atom encountered: ${env.name}`);
},
mobiledoc: null,
html: null
};
Expand Down Expand Up @@ -92,7 +95,7 @@ class Editor {
DEFAULT_KEY_COMMANDS.forEach(kc => this.registerKeyCommand(kc));

this._parser = new DOMParser(this.builder);
this._renderer = new Renderer(this, this.cards, this.atoms, this.unknownCardHandler, this.cardOptions);
this._renderer = new Renderer(this, this.cards, this.atoms, this.unknownCardHandler, this.unknownAtomHandler, this.cardOptions);

this.post = this.loadPost();
this._renderTree = new RenderTree(this.post);
Expand Down
40 changes: 28 additions & 12 deletions src/js/models/atom-node.js
@@ -1,4 +1,4 @@
import { clearChildNodes } from '../utils/dom-utils';
import assert from '../utils/assert';

export default class AtomNode {
constructor(editor, atom, model, element, atomOptions) {
Expand All @@ -8,37 +8,53 @@ export default class AtomNode {
this.atomOptions = atomOptions;
this.element = element;

this._teardown = null;
this._teardownCallback = null;
this._rendered = null;
}

render() {
this.teardown();

let fragment = document.createDocumentFragment();

this._teardown = this.atom.render({
let rendered = this.atom.render({
options: this.atomOptions,
env: this.env,
value: this.model.value,
payload: this.model.payload,
fragment
payload: this.model.payload
});

this.element.appendChild(fragment);
this._validateAndAppendRenderResult(rendered);
}

get env() {
return {
name: this.atom.name
name: this.atom.name,
onTeardown: (callback) => this._teardownCallback = callback
};
}

teardown() {
if (this._teardown) {
this._teardown();
if (this._teardownCallback) {
this._teardownCallback();
this._teardownCallback = null;
}
if (this._rendered) {
this.element.removeChild(this._rendered);
this._rendered = null;
}
}

_validateAndAppendRenderResult(rendered) {
if (!rendered) {
return;
}

clearChildNodes(this.element);
let { atom: { name } } = this;
assert(
`Atom "${name}" must render dom (render value was: "${rendered}")`,
!!rendered.nodeType
);
this.element.appendChild(rendered);
this._rendered = rendered;
}

}
64 changes: 39 additions & 25 deletions src/js/renderers/editor-dom.js
Expand Up @@ -230,19 +230,20 @@ function validateAtoms(atoms=[]) {
atom.type === 'dom'
);
assert(
`Card "${atom.name}" must define \`render\` method`,
`Atom "${atom.name}" must define \`render\` method`,
!!atom.render
);
});
return atoms;
}

class Visitor {
constructor(editor, cards, atoms, unknownCardHandler, options) {
constructor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options) {
this.editor = editor;
this.cards = validateCards(cards);
this.atoms = validateAtoms(atoms);
this.unknownCardHandler = unknownCardHandler;
this.unknownAtomHandler = unknownAtomHandler;
this.options = options;
}

Expand All @@ -265,6 +266,24 @@ class Visitor {
};
}

_findAtom(atomName) {
let atom = detect(this.atoms, atom => atom.name === atomName);
return atom || this._createUnknownAtom(atomName);
}

_createUnknownAtom(atomName) {
assert(
`Unknown atom "${atomName}" found, but no unknownAtomHandler is defined`,
!!this.unknownAtomHandler
);

return {
name: atomName,
type: 'dom',
render: this.unknownAtomHandler
};
}

[POST_TYPE](renderNode, post, visit) {
if (!renderNode.element) {
renderNode.element = document.createElement('div');
Expand Down Expand Up @@ -378,22 +397,16 @@ class Visitor {

const {editor, options} = this;
const atomElement = renderAtom(parentElement, renderNode.prev);
const atom = detect(this.atoms, atom => atom.name === atomModel.name);
const atom = this._findAtom(atomModel.name);

if (atom) {
const atomNode = new AtomNode(
editor, atom, atomModel, atomElement, options
);
const atomNode = new AtomNode(
editor, atom, atomModel, atomElement, options
);

atomNode.render();
atomNode.render();

renderNode.atomNode = atomNode;
renderNode.element = atomElement;
} else {
const env = { name: atomModel.name };
this.unknownAtomHandler( // TODO - pass this in...
atomElement, options, env, atomModel.payload);
}
renderNode.atomNode = atomNode;
renderNode.element = atomElement;
}
}

Expand Down Expand Up @@ -444,15 +457,16 @@ let destroyHooks = {
}
removeRenderNodeSectionFromParent(renderNode, section);
removeRenderNodeElementFromParent(renderNode);
}
},

[ATOM_TYPE](renderNode, atom) {
if (renderNode.atomNode) {
renderNode.atomNode.teardown();
}

// [ATOM_TYPE](renderNode, atom) {
// if (renderNode.atomNode) {
// renderNode.atomNode.teardown();
// }
//
// // TODO - same/similar logic as markers?
// }
// an atom is a kind of marker so just call its destroy hook vs copying here
destroyHooks[MARKER_TYPE](renderNode, atom);
}
};

// removes children from parentNode (a RenderNode) that are scheduled for removal
Expand Down Expand Up @@ -485,9 +499,9 @@ function lookupNode(renderTree, parentNode, postNode, previousNode) {
}

export default class Renderer {
constructor(editor, cards, atoms, unknownCardHandler, options) {
constructor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options) {
this.editor = editor;
this.visitor = new Visitor(editor, cards, atoms, unknownCardHandler, options);
this.visitor = new Visitor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options);
this.nodes = [];
this.hasRendered = false;
}
Expand Down
7 changes: 4 additions & 3 deletions src/js/utils/paste-utils.js
Expand Up @@ -42,13 +42,14 @@ export function setClipboardCopyData(copyEvent, editor) {
const mobiledoc = post.cloneRange(range);

let unknownCardHandler = () => {}; // ignore unknown cards
let {result: innerHTML } =
new HTMLRenderer({unknownCardHandler}).render(mobiledoc);
let unknownAtomHandler = () => {}; // ignore unknown atoms
let {result: innerHTML} =
new HTMLRenderer({unknownCardHandler, unknownAtomHandler}).render(mobiledoc);

const html =
`<div data-mobiledoc='${JSON.stringify(mobiledoc)}'>${innerHTML}</div>`;
const {result: plain} =
new TextRenderer({unknownCardHandler}).render(mobiledoc);
new TextRenderer({unknownCardHandler, unknownAtomHandler}).render(mobiledoc);

clipboardData.setData(MIME_TEXT_PLAIN, plain);
clipboardData.setData(MIME_TEXT_HTML, html);
Expand Down
7 changes: 5 additions & 2 deletions tests/helpers/mobiledoc.js
@@ -1,6 +1,7 @@
import PostAbstractHelpers from './post-abstract';
import mobiledocRenderers from 'mobiledoc-kit/renderers/mobiledoc';
import MobiledocRenderer_0_2, { MOBILEDOC_VERSION } from 'mobiledoc-kit/renderers/mobiledoc/0-2';
import MobiledocRenderer_0_2, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_2 } from 'mobiledoc-kit/renderers/mobiledoc/0-2';
import MobiledocRenderer_0_3, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3 } from 'mobiledoc-kit/renderers/mobiledoc/0-3';
import Editor from 'mobiledoc-kit/editor/editor';
import { mergeWithOptions } from 'mobiledoc-kit/utils/merge';

Expand All @@ -17,8 +18,10 @@ import { mergeWithOptions } from 'mobiledoc-kit/utils/merge';
function build(treeFn, version) {
let post = PostAbstractHelpers.build(treeFn);
switch (version) {
case MOBILEDOC_VERSION:
case MOBILEDOC_VERSION_0_2:
return MobiledocRenderer_0_2.render(post);
case MOBILEDOC_VERSION_0_3:
return MobiledocRenderer_0_3.render(post);
case undefined:
case null:
return mobiledocRenderers.render(post);
Expand Down

0 comments on commit 67e556a

Please sign in to comment.