mobiledoc-dom-renderer
Advanced tools
Comparing version 0.1.18 to 0.2.0
@@ -1,35 +0,31 @@ | ||
define('mobiledoc-dom-renderer/cards/image', ['exports', 'mobiledoc-dom-renderer/utils/dom'], function (exports, _mobiledocDomRendererUtilsDom) { | ||
define('mobiledoc-dom-renderer/cards/image', ['exports', 'mobiledoc-dom-renderer/utils/dom', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererUtilsDom, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
var ImageCard = { | ||
exports['default'] = { | ||
name: 'image', | ||
display: { | ||
setup: function setup(element, options, env, payload) { | ||
if (payload.src) { | ||
var img = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
img.src = payload.src; | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(element, img); | ||
} | ||
} | ||
type: _mobiledocDomRendererUtilsRenderType['default'], | ||
render: function render(_ref) { | ||
var payload = _ref.payload; | ||
var img = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
img.src = payload.src; | ||
return img; | ||
} | ||
}; | ||
exports['default'] = ImageCard; | ||
}); | ||
define('mobiledoc-dom-renderer', ['exports', 'mobiledoc-dom-renderer/renderer'], function (exports, _mobiledocDomRendererRenderer) { | ||
define('mobiledoc-dom-renderer', ['exports', 'mobiledoc-dom-renderer/renderer-factory', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererRendererFactory, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
exports.registerGlobal = registerGlobal; | ||
exports.RENDER_TYPE = _mobiledocDomRendererUtilsRenderType['default']; | ||
function registerGlobal(window) { | ||
window.MobiledocDOMRenderer = _mobiledocDomRendererRenderer['default']; | ||
window.MobiledocDOMRenderer = _mobiledocDomRendererRendererFactory['default']; | ||
} | ||
exports['default'] = _mobiledocDomRendererRenderer['default']; | ||
exports['default'] = _mobiledocDomRendererRendererFactory['default']; | ||
}); | ||
define('mobiledoc-dom-renderer/renderer', ['exports', 'mobiledoc-dom-renderer/utils/dom', 'mobiledoc-dom-renderer/cards/image'], function (exports, _mobiledocDomRendererUtilsDom, _mobiledocDomRendererCardsImage) { | ||
define('mobiledoc-dom-renderer/renderer-factory', ['exports', 'mobiledoc-dom-renderer/renderer', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererRenderer, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); | ||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | ||
@@ -39,7 +35,2 @@ | ||
var MARKUP_SECTION_TYPE = 1; | ||
var IMAGE_SECTION_TYPE = 2; | ||
var LIST_SECTION_TYPE = 3; | ||
var CARD_SECTION_TYPE = 10; | ||
/** | ||
@@ -53,2 +44,63 @@ * runtime DOM renderer | ||
function validateCards(cards) { | ||
if (!Array.isArray(cards)) { | ||
throw new Error('`cards` must be passed as an array, not an object.'); | ||
} | ||
for (var i = 0; i < cards.length; i++) { | ||
var card = cards[i]; | ||
if (card.type !== _mobiledocDomRendererUtilsRenderType['default']) { | ||
throw new Error('Card "' + card.name + '" must be of type "' + _mobiledocDomRendererUtilsRenderType['default'] + '", is type "' + card.type + '"'); | ||
} | ||
if (!card.render) { | ||
throw new Error('Card "' + card.name + '" must define `render`'); | ||
} | ||
} | ||
} | ||
var RendererFactory = (function () { | ||
function RendererFactory() { | ||
var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var cards = _ref.cards; | ||
var atoms = _ref.atoms; | ||
var cardOptions = _ref.cardOptions; | ||
var unknownCardHandler = _ref.unknownCardHandler; | ||
_classCallCheck(this, RendererFactory); | ||
cards = cards || []; | ||
validateCards(cards); | ||
atoms = atoms || []; | ||
cardOptions = cardOptions || {}; | ||
this.state = { cards: cards, atoms: atoms, cardOptions: cardOptions, unknownCardHandler: unknownCardHandler }; | ||
} | ||
_createClass(RendererFactory, [{ | ||
key: 'render', | ||
value: function render(mobiledoc) { | ||
return new _mobiledocDomRendererRenderer['default'](mobiledoc, this.state).render(); | ||
} | ||
}]); | ||
return RendererFactory; | ||
})(); | ||
exports['default'] = RendererFactory; | ||
}); | ||
define('mobiledoc-dom-renderer/renderer', ['exports', 'mobiledoc-dom-renderer/utils/dom', 'mobiledoc-dom-renderer/cards/image', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererUtilsDom, _mobiledocDomRendererCardsImage, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); | ||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } | ||
var MARKUP_SECTION_TYPE = 1; | ||
var IMAGE_SECTION_TYPE = 2; | ||
var LIST_SECTION_TYPE = 3; | ||
var CARD_SECTION_TYPE = 10; | ||
function createElementFromMarkerType() { | ||
@@ -73,165 +125,258 @@ var _ref = arguments.length <= 0 || arguments[0] === undefined ? ['', []] : arguments[0]; | ||
function renderMarkersOnElement(element, markers, renderState) { | ||
var elements = [element]; | ||
var currentElement = element; | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error('Unexpected Mobiledoc version "' + version + '"'); | ||
} | ||
} | ||
for (var i = 0, l = markers.length; i < l; i++) { | ||
var marker = markers[i]; | ||
var Renderer = (function () { | ||
function Renderer(mobiledoc, state) { | ||
_classCallCheck(this, Renderer); | ||
var _marker = _slicedToArray(marker, 3); | ||
var cards = state.cards; | ||
var cardOptions = state.cardOptions; | ||
var atoms = state.atoms; | ||
var unknownCardHandler = state.unknownCardHandler; | ||
var version = mobiledoc.version; | ||
var sectionData = mobiledoc.sections; | ||
var openTypes = _marker[0]; | ||
var closeTypes = _marker[1]; | ||
var text = _marker[2]; | ||
validateVersion(version); | ||
for (var j = 0, m = openTypes.length; j < m; j++) { | ||
var markerType = renderState.markerTypes[openTypes[j]]; | ||
var openedElement = createElementFromMarkerType(markerType); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
} | ||
var _sectionData = _slicedToArray(sectionData, 2); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, (0, _mobiledocDomRendererUtilsDom.createTextNode)(text)); | ||
var markerTypes = _sectionData[0]; | ||
var sections = _sectionData[1]; | ||
for (var j = 0, m = closeTypes; j < m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
} | ||
this.root = (0, _mobiledocDomRendererUtilsDom.createDocumentFragment)(); | ||
this.markerTypes = markerTypes; | ||
this.sections = sections; | ||
this.cards = cards; | ||
this.atoms = atoms; | ||
this.cardOptions = cardOptions; | ||
this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; | ||
this._teardownCallbacks = []; | ||
this._renderedChildNodes = []; | ||
} | ||
} | ||
function renderListItem(markers, renderState) { | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('li'); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
_createClass(Renderer, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _this = this; | ||
function renderListSection(_ref3, renderState) { | ||
var _ref32 = _slicedToArray(_ref3, 3); | ||
this.sections.forEach(function (section) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(_this.root, _this.renderSection(section)); | ||
}); | ||
// maintain a reference to child nodes so they can be cleaned up later by teardown | ||
this._renderedChildNodes = Array.prototype.slice.call(this.root.childNodes); | ||
return { result: this.root, teardown: function teardown() { | ||
return _this.teardown(); | ||
} }; | ||
} | ||
}, { | ||
key: 'teardown', | ||
value: function teardown() { | ||
for (var i = 0; i < this._teardownCallbacks.length; i++) { | ||
this._teardownCallbacks[i](); | ||
} | ||
for (var i = 0; i < this._renderedChildNodes.length; i++) { | ||
var node = this._renderedChildNodes[i]; | ||
if (node.parentNode) { | ||
(0, _mobiledocDomRendererUtilsDom.removeChild)(node.parentNode, node); | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'renderSection', | ||
value: function renderSection(section) { | ||
var _section = _slicedToArray(section, 1); | ||
var type = _ref32[0]; | ||
var tagName = _ref32[1]; | ||
var listItems = _ref32[2]; | ||
var type = _section[0]; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
listItems.forEach(function (li) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(element, renderListItem(li, renderState)); | ||
}); | ||
return element; | ||
} | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return this.renderMarkupSection(section); | ||
case IMAGE_SECTION_TYPE: | ||
return this.renderImageSection(section); | ||
case LIST_SECTION_TYPE: | ||
return this.renderListSection(section); | ||
case CARD_SECTION_TYPE: | ||
return this.renderCardSection(section); | ||
default: | ||
throw new Error('Cannot render mobiledoc section of type "' + type + '"'); | ||
} | ||
} | ||
}, { | ||
key: 'renderMarkersOnElement', | ||
value: function renderMarkersOnElement(element, markers) { | ||
var elements = [element]; | ||
var currentElement = element; | ||
function renderImageSection(_ref4) { | ||
var _ref42 = _slicedToArray(_ref4, 2); | ||
for (var i = 0, l = markers.length; i < l; i++) { | ||
var marker = markers[i]; | ||
var type = _ref42[0]; | ||
var src = _ref42[1]; | ||
var _marker = _slicedToArray(marker, 3); | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
var openTypes = _marker[0]; | ||
var closeTypes = _marker[1]; | ||
var text = _marker[2]; | ||
function renderCardSection(_ref5, renderState) { | ||
var _ref52 = _slicedToArray(_ref5, 3); | ||
for (var j = 0, m = openTypes.length; j < m; j++) { | ||
var markerType = this.markerTypes[openTypes[j]]; | ||
var openedElement = createElementFromMarkerType(markerType); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
} | ||
var type = _ref52[0]; | ||
var name = _ref52[1]; | ||
var payload = _ref52[2]; | ||
var cards = renderState.cards; | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, (0, _mobiledocDomRendererUtilsDom.createTextNode)(text)); | ||
var card = cards[name]; | ||
if (!card) { | ||
throw new Error('Cannot render unknown card named ' + name); | ||
} | ||
if (!payload) { | ||
payload = {}; | ||
} | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('div'); | ||
var cardOptions = renderState.options.cardOptions || {}; | ||
card.display.setup(element, cardOptions, { name: name }, payload); | ||
return element; | ||
} | ||
for (var j = 0, m = closeTypes; j < m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'renderListItem', | ||
value: function renderListItem(markers) { | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('li'); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
}, { | ||
key: 'renderListSection', | ||
value: function renderListSection(_ref3) { | ||
var _this2 = this; | ||
function renderMarkupSection(_ref6, renderState) { | ||
var _ref62 = _slicedToArray(_ref6, 3); | ||
var _ref32 = _slicedToArray(_ref3, 3); | ||
var type = _ref62[0]; | ||
var tagName = _ref62[1]; | ||
var markers = _ref62[2]; | ||
var type = _ref32[0]; | ||
var tagName = _ref32[1]; | ||
var listItems = _ref32[2]; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
listItems.forEach(function (li) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(element, _this2.renderListItem(li)); | ||
}); | ||
return element; | ||
} | ||
}, { | ||
key: 'renderImageSection', | ||
value: function renderImageSection(_ref4) { | ||
var _ref42 = _slicedToArray(_ref4, 2); | ||
function renderSection(section, renderState) { | ||
var _section = _slicedToArray(section, 1); | ||
var type = _ref42[0]; | ||
var src = _ref42[1]; | ||
var type = _section[0]; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
}, { | ||
key: 'findCard', | ||
value: function findCard(name) { | ||
for (var i = 0; i < this.cards.length; i++) { | ||
if (this.cards[i].name === name) { | ||
return this.cards[i]; | ||
} | ||
} | ||
if (name === _mobiledocDomRendererCardsImage['default'].name) { | ||
return _mobiledocDomRendererCardsImage['default']; | ||
} | ||
return this._createUnknownCard(name); | ||
} | ||
}, { | ||
key: '_createUnknownCard', | ||
value: function _createUnknownCard(name) { | ||
return { | ||
name: name, | ||
type: _mobiledocDomRendererUtilsRenderType['default'], | ||
render: this.unknownCardHandler | ||
}; | ||
} | ||
}, { | ||
key: '_createCardArgument', | ||
value: function _createCardArgument(card) { | ||
var _this3 = this; | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return renderMarkupSection(section, renderState); | ||
case IMAGE_SECTION_TYPE: | ||
return renderImageSection(section, renderState); | ||
case LIST_SECTION_TYPE: | ||
return renderListSection(section, renderState); | ||
case CARD_SECTION_TYPE: | ||
return renderCardSection(section, renderState); | ||
default: | ||
throw new Error('Unimplement renderer for type ' + type); | ||
} | ||
} | ||
var payload = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error('Unexpected Mobiledoc version "' + version + '"'); | ||
} | ||
} | ||
var env = { | ||
name: card.name, | ||
isInEditor: false, | ||
onTeardown: function onTeardown(callback) { | ||
return _this3._registerTeardownCallback(callback); | ||
} | ||
}; | ||
var Renderer = (function () { | ||
function Renderer() { | ||
_classCallCheck(this, Renderer); | ||
} | ||
var options = this.cardOptions; | ||
_createClass(Renderer, [{ | ||
key: 'render', | ||
return { env: env, options: options, payload: payload }; | ||
} | ||
}, { | ||
key: '_registerTeardownCallback', | ||
value: function _registerTeardownCallback(callback) { | ||
this._teardownCallbacks.push(callback); | ||
} | ||
}, { | ||
key: 'renderCardSection', | ||
value: function renderCardSection(_ref5) { | ||
var _ref52 = _slicedToArray(_ref5, 3); | ||
/** | ||
* @param {Mobiledoc} mobiledoc | ||
* @param {DOMNode} [rootElement] defaults to an empty div | ||
* @param {Object} [cards] Each top-level property on the object is considered | ||
* to be a card's name, its value is an object with `setup` and (optional) `teardown` | ||
* properties | ||
* @return DOMNode | ||
*/ | ||
value: function render(_ref7) { | ||
var root = arguments.length <= 1 || arguments[1] === undefined ? (0, _mobiledocDomRendererUtilsDom.createElement)('div') : arguments[1]; | ||
var cards = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; | ||
var version = _ref7.version; | ||
var sectionData = _ref7.sections; | ||
var options = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; | ||
var type = _ref52[0]; | ||
var name = _ref52[1]; | ||
var payload = _ref52[2]; | ||
validateVersion(version); | ||
var card = this.findCard(name); | ||
var _sectionData = _slicedToArray(sectionData, 2); | ||
var cardWrapper = this._createCardElement(); | ||
var cardArg = this._createCardArgument(card, payload); | ||
var rendered = card.render(cardArg); | ||
var markerTypes = _sectionData[0]; | ||
var sections = _sectionData[1]; | ||
this._validateCardRender(rendered, card.name); | ||
cards.image = cards.image || _mobiledocDomRendererCardsImage['default']; | ||
var renderState = { root: root, markerTypes: markerTypes, cards: cards, options: options }; | ||
if (rendered) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(cardWrapper, rendered); | ||
} | ||
return cardWrapper; | ||
} | ||
}, { | ||
key: '_createCardElement', | ||
value: function _createCardElement() { | ||
return (0, _mobiledocDomRendererUtilsDom.createElement)('div'); | ||
} | ||
}, { | ||
key: '_validateCardRender', | ||
value: function _validateCardRender(rendered, cardName) { | ||
if (!rendered) { | ||
return; | ||
} | ||
if (Array.isArray(cards)) { | ||
throw new Error('`cards` must be passed as an object, not an array.'); | ||
if (typeof rendered !== 'object') { | ||
throw new Error('Card "' + cardName + '" must render ' + _mobiledocDomRendererUtilsRenderType['default'] + ', but result was "' + rendered + '"'); | ||
} | ||
} | ||
}, { | ||
key: 'renderMarkupSection', | ||
value: function renderMarkupSection(_ref6) { | ||
var _ref62 = _slicedToArray(_ref6, 3); | ||
sections.forEach(function (section) { | ||
var rendered = renderSection(section, renderState); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(renderState.root, rendered); | ||
}); | ||
var type = _ref62[0]; | ||
var tagName = _ref62[1]; | ||
var markers = _ref62[2]; | ||
return root; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
}, { | ||
key: '_defaultUnknownCardHandler', | ||
get: function get() { | ||
return function (_ref7) { | ||
var name = _ref7.env.name; | ||
throw new Error('Card "' + name + '" not found but no unknownCardHandler was registered'); | ||
}; | ||
} | ||
}]); | ||
@@ -249,4 +394,6 @@ | ||
exports.appendChild = appendChild; | ||
exports.removeChild = removeChild; | ||
exports.createTextNode = createTextNode; | ||
exports.setAttribute = setAttribute; | ||
exports.createDocumentFragment = createDocumentFragment; | ||
@@ -261,2 +408,6 @@ function createElement(tagName) { | ||
function removeChild(target, child) { | ||
target.removeChild(child); | ||
} | ||
function addHTMLSpaces(text) { | ||
@@ -274,2 +425,11 @@ var nbsp = ' '; | ||
} | ||
function createDocumentFragment() { | ||
return document.createDocumentFragment(); | ||
} | ||
}); | ||
define('mobiledoc-dom-renderer/utils/render-type', ['exports'], function (exports) { | ||
'use strict'; | ||
exports['default'] = 'dom'; | ||
});//# sourceMappingURL=mobiledoc-dom-renderer.map |
@@ -5,15 +5,14 @@ 'use strict'; | ||
var ImageCard = { | ||
var _utilsRenderType = require('../utils/render-type'); | ||
exports['default'] = { | ||
name: 'image', | ||
display: { | ||
setup: function setup(element, options, env, payload) { | ||
if (payload.src) { | ||
var img = (0, _utilsDom.createElement)('img'); | ||
img.src = payload.src; | ||
(0, _utilsDom.appendChild)(element, img); | ||
} | ||
} | ||
type: _utilsRenderType['default'], | ||
render: function render(_ref) { | ||
var payload = _ref.payload; | ||
var img = (0, _utilsDom.createElement)('img'); | ||
img.src = payload.src; | ||
return img; | ||
} | ||
}; | ||
exports['default'] = ImageCard; | ||
}; |
@@ -5,8 +5,12 @@ 'use strict'; | ||
var _renderer = require('./renderer'); | ||
var _rendererFactory = require('./renderer-factory'); | ||
var _utilsRenderType = require('./utils/render-type'); | ||
exports.RENDER_TYPE = _utilsRenderType['default']; | ||
function registerGlobal(window) { | ||
window.MobiledocDOMRenderer = _renderer['default']; | ||
window.MobiledocDOMRenderer = _rendererFactory['default']; | ||
} | ||
exports['default'] = _renderer['default']; | ||
exports['default'] = _rendererFactory['default']; |
@@ -13,2 +13,4 @@ 'use strict'; | ||
var _utilsRenderType = require('./utils/render-type'); | ||
var MARKUP_SECTION_TYPE = 1; | ||
@@ -19,10 +21,2 @@ var IMAGE_SECTION_TYPE = 2; | ||
/** | ||
* runtime DOM renderer | ||
* renders a mobiledoc to DOM | ||
* | ||
* input: mobiledoc | ||
* output: DOM | ||
*/ | ||
function createElementFromMarkerType() { | ||
@@ -47,165 +41,258 @@ var _ref = arguments.length <= 0 || arguments[0] === undefined ? ['', []] : arguments[0]; | ||
function renderMarkersOnElement(element, markers, renderState) { | ||
var elements = [element]; | ||
var currentElement = element; | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error('Unexpected Mobiledoc version "' + version + '"'); | ||
} | ||
} | ||
for (var i = 0, l = markers.length; i < l; i++) { | ||
var marker = markers[i]; | ||
var Renderer = (function () { | ||
function Renderer(mobiledoc, state) { | ||
_classCallCheck(this, Renderer); | ||
var _marker = _slicedToArray(marker, 3); | ||
var cards = state.cards; | ||
var cardOptions = state.cardOptions; | ||
var atoms = state.atoms; | ||
var unknownCardHandler = state.unknownCardHandler; | ||
var version = mobiledoc.version; | ||
var sectionData = mobiledoc.sections; | ||
var openTypes = _marker[0]; | ||
var closeTypes = _marker[1]; | ||
var text = _marker[2]; | ||
validateVersion(version); | ||
for (var j = 0, m = openTypes.length; j < m; j++) { | ||
var markerType = renderState.markerTypes[openTypes[j]]; | ||
var openedElement = createElementFromMarkerType(markerType); | ||
(0, _utilsDom.appendChild)(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
} | ||
var _sectionData = _slicedToArray(sectionData, 2); | ||
(0, _utilsDom.appendChild)(currentElement, (0, _utilsDom.createTextNode)(text)); | ||
var markerTypes = _sectionData[0]; | ||
var sections = _sectionData[1]; | ||
for (var j = 0, m = closeTypes; j < m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
} | ||
this.root = (0, _utilsDom.createDocumentFragment)(); | ||
this.markerTypes = markerTypes; | ||
this.sections = sections; | ||
this.cards = cards; | ||
this.atoms = atoms; | ||
this.cardOptions = cardOptions; | ||
this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; | ||
this._teardownCallbacks = []; | ||
this._renderedChildNodes = []; | ||
} | ||
} | ||
function renderListItem(markers, renderState) { | ||
var element = (0, _utilsDom.createElement)('li'); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
_createClass(Renderer, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _this = this; | ||
function renderListSection(_ref3, renderState) { | ||
var _ref32 = _slicedToArray(_ref3, 3); | ||
this.sections.forEach(function (section) { | ||
(0, _utilsDom.appendChild)(_this.root, _this.renderSection(section)); | ||
}); | ||
// maintain a reference to child nodes so they can be cleaned up later by teardown | ||
this._renderedChildNodes = Array.prototype.slice.call(this.root.childNodes); | ||
return { result: this.root, teardown: function teardown() { | ||
return _this.teardown(); | ||
} }; | ||
} | ||
}, { | ||
key: 'teardown', | ||
value: function teardown() { | ||
for (var i = 0; i < this._teardownCallbacks.length; i++) { | ||
this._teardownCallbacks[i](); | ||
} | ||
for (var i = 0; i < this._renderedChildNodes.length; i++) { | ||
var node = this._renderedChildNodes[i]; | ||
if (node.parentNode) { | ||
(0, _utilsDom.removeChild)(node.parentNode, node); | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'renderSection', | ||
value: function renderSection(section) { | ||
var _section = _slicedToArray(section, 1); | ||
var type = _ref32[0]; | ||
var tagName = _ref32[1]; | ||
var listItems = _ref32[2]; | ||
var type = _section[0]; | ||
var element = (0, _utilsDom.createElement)(tagName); | ||
listItems.forEach(function (li) { | ||
(0, _utilsDom.appendChild)(element, renderListItem(li, renderState)); | ||
}); | ||
return element; | ||
} | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return this.renderMarkupSection(section); | ||
case IMAGE_SECTION_TYPE: | ||
return this.renderImageSection(section); | ||
case LIST_SECTION_TYPE: | ||
return this.renderListSection(section); | ||
case CARD_SECTION_TYPE: | ||
return this.renderCardSection(section); | ||
default: | ||
throw new Error('Cannot render mobiledoc section of type "' + type + '"'); | ||
} | ||
} | ||
}, { | ||
key: 'renderMarkersOnElement', | ||
value: function renderMarkersOnElement(element, markers) { | ||
var elements = [element]; | ||
var currentElement = element; | ||
function renderImageSection(_ref4) { | ||
var _ref42 = _slicedToArray(_ref4, 2); | ||
for (var i = 0, l = markers.length; i < l; i++) { | ||
var marker = markers[i]; | ||
var type = _ref42[0]; | ||
var src = _ref42[1]; | ||
var _marker = _slicedToArray(marker, 3); | ||
var element = (0, _utilsDom.createElement)('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
var openTypes = _marker[0]; | ||
var closeTypes = _marker[1]; | ||
var text = _marker[2]; | ||
function renderCardSection(_ref5, renderState) { | ||
var _ref52 = _slicedToArray(_ref5, 3); | ||
for (var j = 0, m = openTypes.length; j < m; j++) { | ||
var markerType = this.markerTypes[openTypes[j]]; | ||
var openedElement = createElementFromMarkerType(markerType); | ||
(0, _utilsDom.appendChild)(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
} | ||
var type = _ref52[0]; | ||
var name = _ref52[1]; | ||
var payload = _ref52[2]; | ||
var cards = renderState.cards; | ||
(0, _utilsDom.appendChild)(currentElement, (0, _utilsDom.createTextNode)(text)); | ||
var card = cards[name]; | ||
if (!card) { | ||
throw new Error('Cannot render unknown card named ' + name); | ||
} | ||
if (!payload) { | ||
payload = {}; | ||
} | ||
var element = (0, _utilsDom.createElement)('div'); | ||
var cardOptions = renderState.options.cardOptions || {}; | ||
card.display.setup(element, cardOptions, { name: name }, payload); | ||
return element; | ||
} | ||
for (var j = 0, m = closeTypes; j < m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'renderListItem', | ||
value: function renderListItem(markers) { | ||
var element = (0, _utilsDom.createElement)('li'); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
}, { | ||
key: 'renderListSection', | ||
value: function renderListSection(_ref3) { | ||
var _this2 = this; | ||
function renderMarkupSection(_ref6, renderState) { | ||
var _ref62 = _slicedToArray(_ref6, 3); | ||
var _ref32 = _slicedToArray(_ref3, 3); | ||
var type = _ref62[0]; | ||
var tagName = _ref62[1]; | ||
var markers = _ref62[2]; | ||
var type = _ref32[0]; | ||
var tagName = _ref32[1]; | ||
var listItems = _ref32[2]; | ||
var element = (0, _utilsDom.createElement)(tagName); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
var element = (0, _utilsDom.createElement)(tagName); | ||
listItems.forEach(function (li) { | ||
(0, _utilsDom.appendChild)(element, _this2.renderListItem(li)); | ||
}); | ||
return element; | ||
} | ||
}, { | ||
key: 'renderImageSection', | ||
value: function renderImageSection(_ref4) { | ||
var _ref42 = _slicedToArray(_ref4, 2); | ||
function renderSection(section, renderState) { | ||
var _section = _slicedToArray(section, 1); | ||
var type = _ref42[0]; | ||
var src = _ref42[1]; | ||
var type = _section[0]; | ||
var element = (0, _utilsDom.createElement)('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
}, { | ||
key: 'findCard', | ||
value: function findCard(name) { | ||
for (var i = 0; i < this.cards.length; i++) { | ||
if (this.cards[i].name === name) { | ||
return this.cards[i]; | ||
} | ||
} | ||
if (name === _cardsImage['default'].name) { | ||
return _cardsImage['default']; | ||
} | ||
return this._createUnknownCard(name); | ||
} | ||
}, { | ||
key: '_createUnknownCard', | ||
value: function _createUnknownCard(name) { | ||
return { | ||
name: name, | ||
type: _utilsRenderType['default'], | ||
render: this.unknownCardHandler | ||
}; | ||
} | ||
}, { | ||
key: '_createCardArgument', | ||
value: function _createCardArgument(card) { | ||
var _this3 = this; | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return renderMarkupSection(section, renderState); | ||
case IMAGE_SECTION_TYPE: | ||
return renderImageSection(section, renderState); | ||
case LIST_SECTION_TYPE: | ||
return renderListSection(section, renderState); | ||
case CARD_SECTION_TYPE: | ||
return renderCardSection(section, renderState); | ||
default: | ||
throw new Error('Unimplement renderer for type ' + type); | ||
} | ||
} | ||
var payload = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error('Unexpected Mobiledoc version "' + version + '"'); | ||
} | ||
} | ||
var env = { | ||
name: card.name, | ||
isInEditor: false, | ||
onTeardown: function onTeardown(callback) { | ||
return _this3._registerTeardownCallback(callback); | ||
} | ||
}; | ||
var Renderer = (function () { | ||
function Renderer() { | ||
_classCallCheck(this, Renderer); | ||
} | ||
var options = this.cardOptions; | ||
_createClass(Renderer, [{ | ||
key: 'render', | ||
return { env: env, options: options, payload: payload }; | ||
} | ||
}, { | ||
key: '_registerTeardownCallback', | ||
value: function _registerTeardownCallback(callback) { | ||
this._teardownCallbacks.push(callback); | ||
} | ||
}, { | ||
key: 'renderCardSection', | ||
value: function renderCardSection(_ref5) { | ||
var _ref52 = _slicedToArray(_ref5, 3); | ||
/** | ||
* @param {Mobiledoc} mobiledoc | ||
* @param {DOMNode} [rootElement] defaults to an empty div | ||
* @param {Object} [cards] Each top-level property on the object is considered | ||
* to be a card's name, its value is an object with `setup` and (optional) `teardown` | ||
* properties | ||
* @return DOMNode | ||
*/ | ||
value: function render(_ref7) { | ||
var root = arguments.length <= 1 || arguments[1] === undefined ? (0, _utilsDom.createElement)('div') : arguments[1]; | ||
var cards = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; | ||
var version = _ref7.version; | ||
var sectionData = _ref7.sections; | ||
var options = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; | ||
var type = _ref52[0]; | ||
var name = _ref52[1]; | ||
var payload = _ref52[2]; | ||
validateVersion(version); | ||
var card = this.findCard(name); | ||
var _sectionData = _slicedToArray(sectionData, 2); | ||
var cardWrapper = this._createCardElement(); | ||
var cardArg = this._createCardArgument(card, payload); | ||
var rendered = card.render(cardArg); | ||
var markerTypes = _sectionData[0]; | ||
var sections = _sectionData[1]; | ||
this._validateCardRender(rendered, card.name); | ||
cards.image = cards.image || _cardsImage['default']; | ||
var renderState = { root: root, markerTypes: markerTypes, cards: cards, options: options }; | ||
if (rendered) { | ||
(0, _utilsDom.appendChild)(cardWrapper, rendered); | ||
} | ||
return cardWrapper; | ||
} | ||
}, { | ||
key: '_createCardElement', | ||
value: function _createCardElement() { | ||
return (0, _utilsDom.createElement)('div'); | ||
} | ||
}, { | ||
key: '_validateCardRender', | ||
value: function _validateCardRender(rendered, cardName) { | ||
if (!rendered) { | ||
return; | ||
} | ||
if (Array.isArray(cards)) { | ||
throw new Error('`cards` must be passed as an object, not an array.'); | ||
if (typeof rendered !== 'object') { | ||
throw new Error('Card "' + cardName + '" must render ' + _utilsRenderType['default'] + ', but result was "' + rendered + '"'); | ||
} | ||
} | ||
}, { | ||
key: 'renderMarkupSection', | ||
value: function renderMarkupSection(_ref6) { | ||
var _ref62 = _slicedToArray(_ref6, 3); | ||
sections.forEach(function (section) { | ||
var rendered = renderSection(section, renderState); | ||
(0, _utilsDom.appendChild)(renderState.root, rendered); | ||
}); | ||
var type = _ref62[0]; | ||
var tagName = _ref62[1]; | ||
var markers = _ref62[2]; | ||
return root; | ||
var element = (0, _utilsDom.createElement)(tagName); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
}, { | ||
key: '_defaultUnknownCardHandler', | ||
get: function get() { | ||
return function (_ref7) { | ||
var name = _ref7.env.name; | ||
throw new Error('Card "' + name + '" not found but no unknownCardHandler was registered'); | ||
}; | ||
} | ||
}]); | ||
@@ -212,0 +299,0 @@ |
@@ -5,4 +5,6 @@ 'use strict'; | ||
exports.appendChild = appendChild; | ||
exports.removeChild = removeChild; | ||
exports.createTextNode = createTextNode; | ||
exports.setAttribute = setAttribute; | ||
exports.createDocumentFragment = createDocumentFragment; | ||
@@ -17,2 +19,6 @@ function createElement(tagName) { | ||
function removeChild(target, child) { | ||
target.removeChild(child); | ||
} | ||
function addHTMLSpaces(text) { | ||
@@ -29,2 +35,6 @@ var nbsp = ' '; | ||
node.setAttribute(propName, value); | ||
} | ||
function createDocumentFragment() { | ||
return document.createDocumentFragment(); | ||
} |
@@ -191,36 +191,32 @@ ;(function() { | ||
define('mobiledoc-dom-renderer/cards/image', ['exports', 'mobiledoc-dom-renderer/utils/dom'], function (exports, _mobiledocDomRendererUtilsDom) { | ||
define('mobiledoc-dom-renderer/cards/image', ['exports', 'mobiledoc-dom-renderer/utils/dom', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererUtilsDom, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
var ImageCard = { | ||
exports['default'] = { | ||
name: 'image', | ||
display: { | ||
setup: function setup(element, options, env, payload) { | ||
if (payload.src) { | ||
var img = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
img.src = payload.src; | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(element, img); | ||
} | ||
} | ||
type: _mobiledocDomRendererUtilsRenderType['default'], | ||
render: function render(_ref) { | ||
var payload = _ref.payload; | ||
var img = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
img.src = payload.src; | ||
return img; | ||
} | ||
}; | ||
exports['default'] = ImageCard; | ||
}); | ||
define('mobiledoc-dom-renderer', ['exports', 'mobiledoc-dom-renderer/renderer'], function (exports, _mobiledocDomRendererRenderer) { | ||
define('mobiledoc-dom-renderer', ['exports', 'mobiledoc-dom-renderer/renderer-factory', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererRendererFactory, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
exports.registerGlobal = registerGlobal; | ||
exports.RENDER_TYPE = _mobiledocDomRendererUtilsRenderType['default']; | ||
function registerGlobal(window) { | ||
window.MobiledocDOMRenderer = _mobiledocDomRendererRenderer['default']; | ||
window.MobiledocDOMRenderer = _mobiledocDomRendererRendererFactory['default']; | ||
} | ||
exports['default'] = _mobiledocDomRendererRenderer['default']; | ||
exports['default'] = _mobiledocDomRendererRendererFactory['default']; | ||
}); | ||
define('mobiledoc-dom-renderer/renderer', ['exports', 'mobiledoc-dom-renderer/utils/dom', 'mobiledoc-dom-renderer/cards/image'], function (exports, _mobiledocDomRendererUtilsDom, _mobiledocDomRendererCardsImage) { | ||
define('mobiledoc-dom-renderer/renderer-factory', ['exports', 'mobiledoc-dom-renderer/renderer', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererRenderer, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); | ||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | ||
@@ -230,7 +226,2 @@ | ||
var MARKUP_SECTION_TYPE = 1; | ||
var IMAGE_SECTION_TYPE = 2; | ||
var LIST_SECTION_TYPE = 3; | ||
var CARD_SECTION_TYPE = 10; | ||
/** | ||
@@ -244,2 +235,63 @@ * runtime DOM renderer | ||
function validateCards(cards) { | ||
if (!Array.isArray(cards)) { | ||
throw new Error('`cards` must be passed as an array, not an object.'); | ||
} | ||
for (var i = 0; i < cards.length; i++) { | ||
var card = cards[i]; | ||
if (card.type !== _mobiledocDomRendererUtilsRenderType['default']) { | ||
throw new Error('Card "' + card.name + '" must be of type "' + _mobiledocDomRendererUtilsRenderType['default'] + '", is type "' + card.type + '"'); | ||
} | ||
if (!card.render) { | ||
throw new Error('Card "' + card.name + '" must define `render`'); | ||
} | ||
} | ||
} | ||
var RendererFactory = (function () { | ||
function RendererFactory() { | ||
var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
var cards = _ref.cards; | ||
var atoms = _ref.atoms; | ||
var cardOptions = _ref.cardOptions; | ||
var unknownCardHandler = _ref.unknownCardHandler; | ||
_classCallCheck(this, RendererFactory); | ||
cards = cards || []; | ||
validateCards(cards); | ||
atoms = atoms || []; | ||
cardOptions = cardOptions || {}; | ||
this.state = { cards: cards, atoms: atoms, cardOptions: cardOptions, unknownCardHandler: unknownCardHandler }; | ||
} | ||
_createClass(RendererFactory, [{ | ||
key: 'render', | ||
value: function render(mobiledoc) { | ||
return new _mobiledocDomRendererRenderer['default'](mobiledoc, this.state).render(); | ||
} | ||
}]); | ||
return RendererFactory; | ||
})(); | ||
exports['default'] = RendererFactory; | ||
}); | ||
define('mobiledoc-dom-renderer/renderer', ['exports', 'mobiledoc-dom-renderer/utils/dom', 'mobiledoc-dom-renderer/cards/image', 'mobiledoc-dom-renderer/utils/render-type'], function (exports, _mobiledocDomRendererUtilsDom, _mobiledocDomRendererCardsImage, _mobiledocDomRendererUtilsRenderType) { | ||
'use strict'; | ||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); | ||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } | ||
var MARKUP_SECTION_TYPE = 1; | ||
var IMAGE_SECTION_TYPE = 2; | ||
var LIST_SECTION_TYPE = 3; | ||
var CARD_SECTION_TYPE = 10; | ||
function createElementFromMarkerType() { | ||
@@ -264,165 +316,258 @@ var _ref = arguments.length <= 0 || arguments[0] === undefined ? ['', []] : arguments[0]; | ||
function renderMarkersOnElement(element, markers, renderState) { | ||
var elements = [element]; | ||
var currentElement = element; | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error('Unexpected Mobiledoc version "' + version + '"'); | ||
} | ||
} | ||
for (var i = 0, l = markers.length; i < l; i++) { | ||
var marker = markers[i]; | ||
var Renderer = (function () { | ||
function Renderer(mobiledoc, state) { | ||
_classCallCheck(this, Renderer); | ||
var _marker = _slicedToArray(marker, 3); | ||
var cards = state.cards; | ||
var cardOptions = state.cardOptions; | ||
var atoms = state.atoms; | ||
var unknownCardHandler = state.unknownCardHandler; | ||
var version = mobiledoc.version; | ||
var sectionData = mobiledoc.sections; | ||
var openTypes = _marker[0]; | ||
var closeTypes = _marker[1]; | ||
var text = _marker[2]; | ||
validateVersion(version); | ||
for (var j = 0, m = openTypes.length; j < m; j++) { | ||
var markerType = renderState.markerTypes[openTypes[j]]; | ||
var openedElement = createElementFromMarkerType(markerType); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
} | ||
var _sectionData = _slicedToArray(sectionData, 2); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, (0, _mobiledocDomRendererUtilsDom.createTextNode)(text)); | ||
var markerTypes = _sectionData[0]; | ||
var sections = _sectionData[1]; | ||
for (var j = 0, m = closeTypes; j < m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
} | ||
this.root = (0, _mobiledocDomRendererUtilsDom.createDocumentFragment)(); | ||
this.markerTypes = markerTypes; | ||
this.sections = sections; | ||
this.cards = cards; | ||
this.atoms = atoms; | ||
this.cardOptions = cardOptions; | ||
this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; | ||
this._teardownCallbacks = []; | ||
this._renderedChildNodes = []; | ||
} | ||
} | ||
function renderListItem(markers, renderState) { | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('li'); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
_createClass(Renderer, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _this = this; | ||
function renderListSection(_ref3, renderState) { | ||
var _ref32 = _slicedToArray(_ref3, 3); | ||
this.sections.forEach(function (section) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(_this.root, _this.renderSection(section)); | ||
}); | ||
// maintain a reference to child nodes so they can be cleaned up later by teardown | ||
this._renderedChildNodes = Array.prototype.slice.call(this.root.childNodes); | ||
return { result: this.root, teardown: function teardown() { | ||
return _this.teardown(); | ||
} }; | ||
} | ||
}, { | ||
key: 'teardown', | ||
value: function teardown() { | ||
for (var i = 0; i < this._teardownCallbacks.length; i++) { | ||
this._teardownCallbacks[i](); | ||
} | ||
for (var i = 0; i < this._renderedChildNodes.length; i++) { | ||
var node = this._renderedChildNodes[i]; | ||
if (node.parentNode) { | ||
(0, _mobiledocDomRendererUtilsDom.removeChild)(node.parentNode, node); | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'renderSection', | ||
value: function renderSection(section) { | ||
var _section = _slicedToArray(section, 1); | ||
var type = _ref32[0]; | ||
var tagName = _ref32[1]; | ||
var listItems = _ref32[2]; | ||
var type = _section[0]; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
listItems.forEach(function (li) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(element, renderListItem(li, renderState)); | ||
}); | ||
return element; | ||
} | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return this.renderMarkupSection(section); | ||
case IMAGE_SECTION_TYPE: | ||
return this.renderImageSection(section); | ||
case LIST_SECTION_TYPE: | ||
return this.renderListSection(section); | ||
case CARD_SECTION_TYPE: | ||
return this.renderCardSection(section); | ||
default: | ||
throw new Error('Cannot render mobiledoc section of type "' + type + '"'); | ||
} | ||
} | ||
}, { | ||
key: 'renderMarkersOnElement', | ||
value: function renderMarkersOnElement(element, markers) { | ||
var elements = [element]; | ||
var currentElement = element; | ||
function renderImageSection(_ref4) { | ||
var _ref42 = _slicedToArray(_ref4, 2); | ||
for (var i = 0, l = markers.length; i < l; i++) { | ||
var marker = markers[i]; | ||
var type = _ref42[0]; | ||
var src = _ref42[1]; | ||
var _marker = _slicedToArray(marker, 3); | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
var openTypes = _marker[0]; | ||
var closeTypes = _marker[1]; | ||
var text = _marker[2]; | ||
function renderCardSection(_ref5, renderState) { | ||
var _ref52 = _slicedToArray(_ref5, 3); | ||
for (var j = 0, m = openTypes.length; j < m; j++) { | ||
var markerType = this.markerTypes[openTypes[j]]; | ||
var openedElement = createElementFromMarkerType(markerType); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
} | ||
var type = _ref52[0]; | ||
var name = _ref52[1]; | ||
var payload = _ref52[2]; | ||
var cards = renderState.cards; | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(currentElement, (0, _mobiledocDomRendererUtilsDom.createTextNode)(text)); | ||
var card = cards[name]; | ||
if (!card) { | ||
throw new Error('Cannot render unknown card named ' + name); | ||
} | ||
if (!payload) { | ||
payload = {}; | ||
} | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('div'); | ||
var cardOptions = renderState.options.cardOptions || {}; | ||
card.display.setup(element, cardOptions, { name: name }, payload); | ||
return element; | ||
} | ||
for (var j = 0, m = closeTypes; j < m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
} | ||
} | ||
} | ||
}, { | ||
key: 'renderListItem', | ||
value: function renderListItem(markers) { | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('li'); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
}, { | ||
key: 'renderListSection', | ||
value: function renderListSection(_ref3) { | ||
var _this2 = this; | ||
function renderMarkupSection(_ref6, renderState) { | ||
var _ref62 = _slicedToArray(_ref6, 3); | ||
var _ref32 = _slicedToArray(_ref3, 3); | ||
var type = _ref62[0]; | ||
var tagName = _ref62[1]; | ||
var markers = _ref62[2]; | ||
var type = _ref32[0]; | ||
var tagName = _ref32[1]; | ||
var listItems = _ref32[2]; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
listItems.forEach(function (li) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(element, _this2.renderListItem(li)); | ||
}); | ||
return element; | ||
} | ||
}, { | ||
key: 'renderImageSection', | ||
value: function renderImageSection(_ref4) { | ||
var _ref42 = _slicedToArray(_ref4, 2); | ||
function renderSection(section, renderState) { | ||
var _section = _slicedToArray(section, 1); | ||
var type = _ref42[0]; | ||
var src = _ref42[1]; | ||
var type = _section[0]; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
}, { | ||
key: 'findCard', | ||
value: function findCard(name) { | ||
for (var i = 0; i < this.cards.length; i++) { | ||
if (this.cards[i].name === name) { | ||
return this.cards[i]; | ||
} | ||
} | ||
if (name === _mobiledocDomRendererCardsImage['default'].name) { | ||
return _mobiledocDomRendererCardsImage['default']; | ||
} | ||
return this._createUnknownCard(name); | ||
} | ||
}, { | ||
key: '_createUnknownCard', | ||
value: function _createUnknownCard(name) { | ||
return { | ||
name: name, | ||
type: _mobiledocDomRendererUtilsRenderType['default'], | ||
render: this.unknownCardHandler | ||
}; | ||
} | ||
}, { | ||
key: '_createCardArgument', | ||
value: function _createCardArgument(card) { | ||
var _this3 = this; | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return renderMarkupSection(section, renderState); | ||
case IMAGE_SECTION_TYPE: | ||
return renderImageSection(section, renderState); | ||
case LIST_SECTION_TYPE: | ||
return renderListSection(section, renderState); | ||
case CARD_SECTION_TYPE: | ||
return renderCardSection(section, renderState); | ||
default: | ||
throw new Error('Unimplement renderer for type ' + type); | ||
} | ||
} | ||
var payload = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error('Unexpected Mobiledoc version "' + version + '"'); | ||
} | ||
} | ||
var env = { | ||
name: card.name, | ||
isInEditor: false, | ||
onTeardown: function onTeardown(callback) { | ||
return _this3._registerTeardownCallback(callback); | ||
} | ||
}; | ||
var Renderer = (function () { | ||
function Renderer() { | ||
_classCallCheck(this, Renderer); | ||
} | ||
var options = this.cardOptions; | ||
_createClass(Renderer, [{ | ||
key: 'render', | ||
return { env: env, options: options, payload: payload }; | ||
} | ||
}, { | ||
key: '_registerTeardownCallback', | ||
value: function _registerTeardownCallback(callback) { | ||
this._teardownCallbacks.push(callback); | ||
} | ||
}, { | ||
key: 'renderCardSection', | ||
value: function renderCardSection(_ref5) { | ||
var _ref52 = _slicedToArray(_ref5, 3); | ||
/** | ||
* @param {Mobiledoc} mobiledoc | ||
* @param {DOMNode} [rootElement] defaults to an empty div | ||
* @param {Object} [cards] Each top-level property on the object is considered | ||
* to be a card's name, its value is an object with `setup` and (optional) `teardown` | ||
* properties | ||
* @return DOMNode | ||
*/ | ||
value: function render(_ref7) { | ||
var root = arguments.length <= 1 || arguments[1] === undefined ? (0, _mobiledocDomRendererUtilsDom.createElement)('div') : arguments[1]; | ||
var cards = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; | ||
var version = _ref7.version; | ||
var sectionData = _ref7.sections; | ||
var options = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; | ||
var type = _ref52[0]; | ||
var name = _ref52[1]; | ||
var payload = _ref52[2]; | ||
validateVersion(version); | ||
var card = this.findCard(name); | ||
var _sectionData = _slicedToArray(sectionData, 2); | ||
var cardWrapper = this._createCardElement(); | ||
var cardArg = this._createCardArgument(card, payload); | ||
var rendered = card.render(cardArg); | ||
var markerTypes = _sectionData[0]; | ||
var sections = _sectionData[1]; | ||
this._validateCardRender(rendered, card.name); | ||
cards.image = cards.image || _mobiledocDomRendererCardsImage['default']; | ||
var renderState = { root: root, markerTypes: markerTypes, cards: cards, options: options }; | ||
if (rendered) { | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(cardWrapper, rendered); | ||
} | ||
return cardWrapper; | ||
} | ||
}, { | ||
key: '_createCardElement', | ||
value: function _createCardElement() { | ||
return (0, _mobiledocDomRendererUtilsDom.createElement)('div'); | ||
} | ||
}, { | ||
key: '_validateCardRender', | ||
value: function _validateCardRender(rendered, cardName) { | ||
if (!rendered) { | ||
return; | ||
} | ||
if (Array.isArray(cards)) { | ||
throw new Error('`cards` must be passed as an object, not an array.'); | ||
if (typeof rendered !== 'object') { | ||
throw new Error('Card "' + cardName + '" must render ' + _mobiledocDomRendererUtilsRenderType['default'] + ', but result was "' + rendered + '"'); | ||
} | ||
} | ||
}, { | ||
key: 'renderMarkupSection', | ||
value: function renderMarkupSection(_ref6) { | ||
var _ref62 = _slicedToArray(_ref6, 3); | ||
sections.forEach(function (section) { | ||
var rendered = renderSection(section, renderState); | ||
(0, _mobiledocDomRendererUtilsDom.appendChild)(renderState.root, rendered); | ||
}); | ||
var type = _ref62[0]; | ||
var tagName = _ref62[1]; | ||
var markers = _ref62[2]; | ||
return root; | ||
var element = (0, _mobiledocDomRendererUtilsDom.createElement)(tagName); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
}, { | ||
key: '_defaultUnknownCardHandler', | ||
get: function get() { | ||
return function (_ref7) { | ||
var name = _ref7.env.name; | ||
throw new Error('Card "' + name + '" not found but no unknownCardHandler was registered'); | ||
}; | ||
} | ||
}]); | ||
@@ -440,4 +585,6 @@ | ||
exports.appendChild = appendChild; | ||
exports.removeChild = removeChild; | ||
exports.createTextNode = createTextNode; | ||
exports.setAttribute = setAttribute; | ||
exports.createDocumentFragment = createDocumentFragment; | ||
@@ -452,2 +599,6 @@ function createElement(tagName) { | ||
function removeChild(target, child) { | ||
target.removeChild(child); | ||
} | ||
function addHTMLSpaces(text) { | ||
@@ -465,5 +616,14 @@ var nbsp = ' '; | ||
} | ||
function createDocumentFragment() { | ||
return document.createDocumentFragment(); | ||
} | ||
}); | ||
define('mobiledoc-dom-renderer/utils/render-type', ['exports'], function (exports) { | ||
'use strict'; | ||
exports['default'] = 'dom'; | ||
}); | ||
require("mobiledoc-dom-renderer")["registerGlobal"](window, document); | ||
})(); | ||
//# sourceMappingURL=mobiledoc-dom-renderer.map |
@@ -12,2 +12,7 @@ QUnit.module('JSHint - tests/jshint/cards'); | ||
QUnit.module('JSHint - tests/jshint'); | ||
QUnit.test('tests/jshint/renderer-factory.js should pass jshint', function(assert) { | ||
assert.ok(true, 'tests/jshint/renderer-factory.js should pass jshint.'); | ||
}); | ||
QUnit.module('JSHint - tests/jshint'); | ||
QUnit.test('tests/jshint/renderer.js should pass jshint', function(assert) { | ||
@@ -27,2 +32,7 @@ assert.ok(true, 'tests/jshint/renderer.js should pass jshint.'); | ||
QUnit.module('JSHint - tests/jshint/utils'); | ||
QUnit.test('tests/jshint/utils/render-type.js should pass jshint', function(assert) { | ||
assert.ok(true, 'tests/jshint/utils/render-type.js should pass jshint.'); | ||
}); | ||
define('tests/unit/renderer-test', ['exports', 'mobiledoc-dom-renderer'], function (exports, _mobiledocDomRenderer) { | ||
@@ -33,16 +43,100 @@ /* global QUnit */ | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
var _QUnit = QUnit; | ||
var test = _QUnit.test; | ||
var _module = _QUnit.module; | ||
var MOBILEDOC_VERSION = '0.2.0'; | ||
var dataUri = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="; | ||
var renderer = undefined; | ||
QUnit.module('Unit: Mobiledoc DOM Renderer', { | ||
_module('Unit: Mobiledoc DOM Renderer', { | ||
beforeEach: function beforeEach() { | ||
renderer = new _mobiledocDomRenderer['default'](); | ||
}, | ||
afterEach: function afterEach() { | ||
if (renderer) { | ||
renderer = null; | ||
} | ||
} | ||
}); | ||
test('rendering unknown card without unknownCardHandler throws', function (assert) { | ||
var cardName = 'not-known'; | ||
var mobiledoc = { | ||
version: MOBILEDOC_VERSION, | ||
sections: [[], [[10, cardName]]] | ||
}; | ||
renderer = new _mobiledocDomRenderer['default']({ cards: [], unknownCardHandler: undefined }); | ||
assert.throws(function () { | ||
return renderer.render(mobiledoc); | ||
}, new RegExp('Card "' + cardName + '" not found.*no unknownCardHandler')); | ||
}); | ||
test('rendering unknown card uses unknownCardHandler', function (assert) { | ||
assert.expect(5); | ||
var cardName = 'my-card'; | ||
var expectedOptions = {}; | ||
var expectedPayload = {}; | ||
var unknownCardHandler = function unknownCardHandler(_ref) { | ||
var env = _ref.env; | ||
var options = _ref.options; | ||
var payload = _ref.payload; | ||
assert.equal(env.name, cardName, 'name is correct'); | ||
assert.ok(!env.isInEditor, 'not in editor'); | ||
assert.ok(!!env.onTeardown, 'has onTeardown'); | ||
assert.deepEqual(options, expectedOptions, 'correct options'); | ||
assert.deepEqual(payload, expectedPayload, 'correct payload'); | ||
}; | ||
var mobiledoc = { | ||
version: MOBILEDOC_VERSION, | ||
sections: [[], [[10, cardName, expectedPayload]]] | ||
}; | ||
renderer = new _mobiledocDomRenderer['default']({ | ||
cards: [], cardOptions: expectedOptions, unknownCardHandler: unknownCardHandler | ||
}); | ||
renderer.render(mobiledoc); | ||
}); | ||
test('throws if card render returns invalid result', function (assert) { | ||
var card = { | ||
name: 'bad', | ||
type: 'dom', | ||
render: function render() { | ||
return 'string'; | ||
} | ||
}; | ||
var mobiledoc = { | ||
version: MOBILEDOC_VERSION, | ||
sections: [[], [[10, card.name]]] | ||
}; | ||
renderer = new _mobiledocDomRenderer['default']({ cards: [card] }); | ||
assert.throws(function () { | ||
return renderer.render(mobiledoc); | ||
}, /Card "bad" must render dom/); | ||
}); | ||
test('card may render nothing', function (assert) { | ||
var card = { | ||
name: 'ok', | ||
type: 'dom', | ||
render: function render() {} | ||
}; | ||
var mobiledoc = { | ||
version: MOBILEDOC_VERSION, | ||
sections: [[], [[10, card.name]]] | ||
}; | ||
renderer = new _mobiledocDomRenderer['default']({ cards: [card] }); | ||
renderer.render(mobiledoc); | ||
assert.ok(true, 'No error thrown'); | ||
}); | ||
test('it exists', function (assert) { | ||
@@ -53,2 +147,38 @@ assert.ok(_mobiledocDomRenderer['default'], 'class exists'); | ||
test('throws if given a card with an invalid type', function (assert) { | ||
var cardName = 'bad'; | ||
var card = { | ||
name: cardName, | ||
type: 'text', | ||
render: function render() {} | ||
}; | ||
var cards = [card]; | ||
assert.throws(function () { | ||
new _mobiledocDomRenderer['default']({ cards: cards }); | ||
}, // jshint ignore: line | ||
new RegExp('Card "' + cardName + '" must be of type "' + _mobiledocDomRenderer.RENDER_TYPE + '"')); | ||
}); | ||
test('throws if given a card without a render method', function (assert) { | ||
var cardName = 'bad'; | ||
var card = { | ||
name: cardName, | ||
type: _mobiledocDomRenderer.RENDER_TYPE, | ||
render: undefined | ||
}; | ||
var cards = [card]; | ||
assert.throws(function () { | ||
new _mobiledocDomRenderer['default']({ cards: cards }); | ||
}, // jshint ignore: line | ||
new RegExp('Card "' + cardName + '" must define `render`')); | ||
}); | ||
test('throws if given an object of cards', function (assert) { | ||
var cards = {}; | ||
assert.throws(function () { | ||
new _mobiledocDomRenderer['default']({ cards: cards }); | ||
}, // jshint ignore: line | ||
new RegExp('`cards` must be passed as an array')); | ||
}); | ||
test('renders an empty mobiledoc', function (assert) { | ||
@@ -61,5 +191,8 @@ var mobiledoc = { | ||
}; | ||
var rendered = renderer.render(mobiledoc); | ||
assert.ok(rendered, 'renders output'); | ||
var _renderer$render = renderer.render(mobiledoc); | ||
var rendered = _renderer$render.result; | ||
assert.ok(!!rendered, 'renders result'); | ||
assert.equal(rendered.childNodes.length, 0, 'has no sections'); | ||
@@ -75,3 +208,5 @@ }); | ||
}; | ||
var rendered = renderer.render(mobiledoc); | ||
var renderResult = renderer.render(mobiledoc); | ||
var rendered = renderResult.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
@@ -89,3 +224,7 @@ assert.equal(rendered.childNodes[0].tagName, 'P', 'renders a P'); | ||
}; | ||
var rendered = renderer.render(mobiledoc); | ||
var _renderer$render2 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render2.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
@@ -104,3 +243,7 @@ var sectionEl = rendered.childNodes[0]; | ||
}; | ||
var rendered = renderer.render(mobiledoc); | ||
var _renderer$render3 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render3.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
@@ -123,3 +266,7 @@ var sectionEl = rendered.childNodes[0]; | ||
}; | ||
var rendered = renderer.render(mobiledoc); | ||
var _renderer$render4 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render4.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
@@ -132,3 +279,2 @@ var sectionEl = rendered.childNodes[0]; | ||
test('renders a mobiledoc with image section', function (assert) { | ||
var url = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="; | ||
var mobiledoc = { | ||
@@ -138,29 +284,39 @@ version: MOBILEDOC_VERSION, | ||
[// sections | ||
[2, url]]] | ||
[2, dataUri]]] | ||
}; | ||
var rendered = renderer.render(mobiledoc); | ||
var _renderer$render5 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render5.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
var sectionEl = rendered.childNodes[0]; | ||
assert.equal(sectionEl.src, url); | ||
assert.equal(sectionEl.src, dataUri); | ||
}); | ||
test('renders a mobiledoc with card section', function (assert) { | ||
assert.expect(4); | ||
assert.expect(7); | ||
var cardName = 'title-card'; | ||
var payload = { | ||
name: 'bob' | ||
}; | ||
var options = { | ||
cardOptions: { foo: 'bar' } | ||
}; | ||
var expectedOptions = options.cardOptions; | ||
var expectedPayload = { name: 'bob' }; | ||
var expectedOptions = { foo: 'bar' }; | ||
var TitleCard = { | ||
name: cardName, | ||
display: { | ||
setup: function setup(element, options, env, setupPayload) { | ||
assert.deepEqual(payload, setupPayload); | ||
assert.deepEqual(options, expectedOptions); | ||
element.innerHTML = setupPayload.name; | ||
} | ||
type: 'dom', | ||
render: function render(_ref2) { | ||
var env = _ref2.env; | ||
var options = _ref2.options; | ||
var payload = _ref2.payload; | ||
var name = env.name; | ||
var isInEditor = env.isInEditor; | ||
var onTeardown = env.onTeardown; | ||
assert.equal(name, cardName, 'has name'); | ||
assert.ok(!isInEditor, 'not isInEditor'); | ||
assert.ok(!!onTeardown, 'has onTeardown'); | ||
assert.deepEqual(options, expectedOptions); | ||
assert.deepEqual(payload, expectedPayload); | ||
return document.createTextNode(payload.name); | ||
} | ||
@@ -172,56 +328,78 @@ }; | ||
[// sections | ||
[10, cardName, payload]]] | ||
[10, cardName, expectedPayload]]] | ||
}; | ||
var cards = _defineProperty({}, cardName, TitleCard); | ||
var element = document.createElement('div'); | ||
var rendered = renderer.render(mobiledoc, element, cards, options); | ||
renderer = new _mobiledocDomRenderer['default']({ cards: [TitleCard], cardOptions: expectedOptions }); | ||
var _renderer$render6 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render6.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
var sectionEl = rendered.childNodes[0]; | ||
assert.equal(sectionEl.innerHTML, payload.name); | ||
assert.equal(sectionEl.innerHTML, expectedPayload.name); | ||
}); | ||
test('renders a mobiledoc with card section and no payload', function (assert) { | ||
assert.expect(3); | ||
var cardName = 'title-card'; | ||
var TitleCard = { | ||
name: cardName, | ||
display: { | ||
setup: function setup(element, options, env, setupPayload) { | ||
assert.deepEqual({}, setupPayload); | ||
element.innerHTML = ''; | ||
} | ||
} | ||
}; | ||
test('renderResult.teardown removes rendered sections from dom', function (assert) { | ||
var mobiledoc = { | ||
version: MOBILEDOC_VERSION, | ||
sections: [[], // markers | ||
[// sections | ||
[10, cardName]]] | ||
sections: [[], [[1, 'p', [[[], 0, 'Hello world']]]]] | ||
}; | ||
var rendered = renderer.render(mobiledoc, document.createElement('div'), _defineProperty({}, cardName, TitleCard)); | ||
var _renderer$render7 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render7.result; | ||
var teardown = _renderer$render7.teardown; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
var sectionEl = rendered.childNodes[0]; | ||
assert.equal(sectionEl.innerHTML, ''); | ||
var fixture = document.getElementById('qunit-fixture'); | ||
fixture.appendChild(rendered); | ||
assert.ok(fixture.childNodes.length === 1, 'precond - result is appended'); | ||
teardown(); | ||
assert.ok(fixture.childNodes.length === 0, 'rendered result removed after teardown'); | ||
}); | ||
test('when passed an array of cards, throws', function (assert) { | ||
test('card can register teardown hook', function (assert) { | ||
var cardName = 'title-card'; | ||
var didTeardown = false; | ||
var card = { | ||
name: cardName, | ||
type: 'dom', | ||
render: function render(_ref3) { | ||
var env = _ref3.env; | ||
env.onTeardown(function () { | ||
return didTeardown = true; | ||
}); | ||
} | ||
}; | ||
var mobiledoc = { | ||
version: MOBILEDOC_VERSION, | ||
sections: [[], []] | ||
sections: [[], [[10, cardName]]] | ||
}; | ||
var cards = [{ name: 'test-card' }]; | ||
var element = document.createElement('div'); | ||
assert.throws(function () { | ||
renderer.render(mobiledoc, element, cards); | ||
}, /cards.*must be passed as an object/); | ||
renderer = new _mobiledocDomRenderer['default']({ cards: [card] }); | ||
var _renderer$render8 = renderer.render(mobiledoc); | ||
var teardown = _renderer$render8.teardown; | ||
assert.ok(!didTeardown, 'teardown not called'); | ||
teardown(); | ||
assert.ok(didTeardown, 'teardown called'); | ||
}); | ||
test('renders a mobiledoc with default image section', function (assert) { | ||
test('renders a mobiledoc with default image card', function (assert) { | ||
assert.expect(3); | ||
var cardName = 'image'; | ||
var payload = { | ||
src: 'http://example.org/foo.jpg' | ||
src: dataUri | ||
}; | ||
@@ -234,3 +412,7 @@ var mobiledoc = { | ||
}; | ||
var rendered = renderer.render(mobiledoc, document.createElement('div')); | ||
var _renderer$render9 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render9.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
@@ -240,3 +422,3 @@ var sectionEl = rendered.childNodes[0]; | ||
assert.equal(sectionEl.firstChild.tagName, 'IMG'); | ||
assert.equal(sectionEl.firstChild.src, 'http://example.org/foo.jpg'); | ||
assert.equal(sectionEl.firstChild.src, dataUri); | ||
}); | ||
@@ -249,3 +431,7 @@ | ||
}; | ||
var rendered = renderer.render(mobiledoc, document.createElement('div')); | ||
var _renderer$render10 = renderer.render(mobiledoc, document.createElement('div')); | ||
var rendered = _renderer$render10.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
@@ -284,5 +470,9 @@ | ||
var nbsp = ' '; | ||
var sn = ' ' + nbsp; | ||
var expectedText = [repeat(sn, 2), 'some', repeat(sn, 2), ' ', 'text', repeat(sn, 3)].join(''); | ||
var rendered = renderer.render(mobiledoc); | ||
var sn = space + nbsp; | ||
var expectedText = [repeat(sn, 2), 'some', repeat(sn, 2), space, 'text', repeat(sn, 3)].join(''); | ||
var _renderer$render11 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render11.result; | ||
var textNode = rendered.firstChild.firstChild; | ||
@@ -293,13 +483,15 @@ assert.equal(textNode.textContent, expectedText, 'renders the text'); | ||
test('rendering nested mobiledocs in cards', function (assert) { | ||
var renderer = new _mobiledocDomRenderer['default'](); | ||
var cards = [{ | ||
name: 'nested-card', | ||
type: 'dom', | ||
render: function render(_ref4) { | ||
var payload = _ref4.payload; | ||
var cards = { | ||
'nested-card': { | ||
display: { | ||
setup: function setup(element, options, env, payload) { | ||
renderer.render(payload.mobiledoc, element, cards); | ||
} | ||
} | ||
var _renderer$render12 = renderer.render(payload.mobiledoc); | ||
var rendered = _renderer$render12.result; | ||
return rendered; | ||
} | ||
}; | ||
}]; | ||
@@ -320,3 +512,8 @@ var innerMobiledoc = { | ||
var rendered = renderer.render(mobiledoc, document.createElement('div'), cards); | ||
var renderer = new _mobiledocDomRenderer['default']({ cards: cards }); | ||
var _renderer$render13 = renderer.render(mobiledoc); | ||
var rendered = _renderer$render13.result; | ||
assert.equal(rendered.childNodes.length, 1, 'renders 1 section'); | ||
@@ -323,0 +520,0 @@ var card = rendered.childNodes[0]; |
@@ -1,19 +0,12 @@ | ||
import { | ||
createElement, | ||
appendChild | ||
} from '../utils/dom'; | ||
import { createElement } from '../utils/dom'; | ||
import RENDER_TYPE from '../utils/render-type'; | ||
const ImageCard = { | ||
export default { | ||
name: 'image', | ||
display: { | ||
setup(element, options, env, payload) { | ||
if (payload.src) { | ||
let img = createElement('img'); | ||
img.src = payload.src; | ||
appendChild(element, img); | ||
} | ||
} | ||
type: RENDER_TYPE, | ||
render({payload}) { | ||
let img = createElement('img'); | ||
img.src = payload.src; | ||
return img; | ||
} | ||
}; | ||
export default ImageCard; |
@@ -1,7 +0,10 @@ | ||
import Renderer from './renderer'; | ||
import RendererFactory from './renderer-factory'; | ||
import RENDER_TYPE from './utils/render-type'; | ||
export { RENDER_TYPE }; | ||
export function registerGlobal(window) { | ||
window.MobiledocDOMRenderer = Renderer; | ||
window.MobiledocDOMRenderer = RendererFactory; | ||
} | ||
export default Renderer; | ||
export default RendererFactory; |
import { | ||
createElement, | ||
appendChild, | ||
removeChild, | ||
createTextNode, | ||
setAttribute | ||
setAttribute, | ||
createDocumentFragment | ||
} from './utils/dom'; | ||
import ImageCard from './cards/image'; | ||
import RENDER_TYPE from './utils/render-type'; | ||
@@ -14,10 +17,2 @@ const MARKUP_SECTION_TYPE = 1; | ||
/** | ||
* runtime DOM renderer | ||
* renders a mobiledoc to DOM | ||
* | ||
* input: mobiledoc | ||
* output: DOM | ||
*/ | ||
function createElementFromMarkerType([tagName, attributes]=['', []]){ | ||
@@ -35,116 +30,187 @@ let element = createElement(tagName); | ||
function renderMarkersOnElement(element, markers, renderState) { | ||
let elements = [element]; | ||
let currentElement = element; | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error(`Unexpected Mobiledoc version "${version}"`); | ||
} | ||
} | ||
for (let i=0, l=markers.length; i<l; i++) { | ||
let marker = markers[i]; | ||
let [openTypes, closeTypes, text] = marker; | ||
export default class Renderer { | ||
constructor(mobiledoc, state) { | ||
let { cards, cardOptions, atoms, unknownCardHandler } = state; | ||
let { version, sections: sectionData } = mobiledoc; | ||
validateVersion(version); | ||
for (let j=0, m=openTypes.length; j<m; j++) { | ||
let markerType = renderState.markerTypes[openTypes[j]]; | ||
let openedElement = createElementFromMarkerType(markerType); | ||
appendChild(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
const [markerTypes, sections] = sectionData; | ||
this.root = createDocumentFragment(); | ||
this.markerTypes = markerTypes; | ||
this.sections = sections; | ||
this.cards = cards; | ||
this.atoms = atoms; | ||
this.cardOptions = cardOptions; | ||
this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; | ||
this._teardownCallbacks = []; | ||
this._renderedChildNodes = []; | ||
} | ||
get _defaultUnknownCardHandler() { | ||
return ({env: {name}}) => { | ||
throw new Error(`Card "${name}" not found but no unknownCardHandler was registered`); | ||
}; | ||
} | ||
render() { | ||
this.sections.forEach(section => { | ||
appendChild(this.root, this.renderSection(section)); | ||
}); | ||
// maintain a reference to child nodes so they can be cleaned up later by teardown | ||
this._renderedChildNodes = Array.prototype.slice.call(this.root.childNodes); | ||
return { result: this.root, teardown: () => this.teardown() }; | ||
} | ||
teardown() { | ||
for (let i=0; i < this._teardownCallbacks.length; i++) { | ||
this._teardownCallbacks[i](); | ||
} | ||
for (let i=0; i < this._renderedChildNodes.length; i++) { | ||
let node = this._renderedChildNodes[i]; | ||
if (node.parentNode) { | ||
removeChild(node.parentNode, node); | ||
} | ||
} | ||
} | ||
appendChild(currentElement, createTextNode(text)); | ||
renderSection(section) { | ||
const [type] = section; | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return this.renderMarkupSection(section); | ||
case IMAGE_SECTION_TYPE: | ||
return this.renderImageSection(section); | ||
case LIST_SECTION_TYPE: | ||
return this.renderListSection(section); | ||
case CARD_SECTION_TYPE: | ||
return this.renderCardSection(section); | ||
default: | ||
throw new Error(`Cannot render mobiledoc section of type "${type}"`); | ||
} | ||
} | ||
for (let j=0, m=closeTypes; j<m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
renderMarkersOnElement(element, markers) { | ||
let elements = [element]; | ||
let currentElement = element; | ||
for (let i=0, l=markers.length; i<l; i++) { | ||
let marker = markers[i]; | ||
let [openTypes, closeTypes, text] = marker; | ||
for (let j=0, m=openTypes.length; j<m; j++) { | ||
let markerType = this.markerTypes[openTypes[j]]; | ||
let openedElement = createElementFromMarkerType(markerType); | ||
appendChild(currentElement, openedElement); | ||
elements.push(openedElement); | ||
currentElement = openedElement; | ||
} | ||
appendChild(currentElement, createTextNode(text)); | ||
for (let j=0, m=closeTypes; j<m; j++) { | ||
elements.pop(); | ||
currentElement = elements[elements.length - 1]; | ||
} | ||
} | ||
} | ||
} | ||
function renderListItem(markers, renderState) { | ||
const element = createElement('li'); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
renderListItem(markers) { | ||
const element = createElement('li'); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
function renderListSection([type, tagName, listItems], renderState) { | ||
const element = createElement(tagName); | ||
listItems.forEach(li => { | ||
appendChild(element, (renderListItem(li, renderState))); | ||
}); | ||
return element; | ||
} | ||
renderListSection([type, tagName, listItems]) { | ||
const element = createElement(tagName); | ||
listItems.forEach(li => { | ||
appendChild(element, (this.renderListItem(li))); | ||
}); | ||
return element; | ||
} | ||
function renderImageSection([type, src]) { | ||
let element = createElement('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
renderImageSection([type, src]) { | ||
let element = createElement('img'); | ||
element.src = src; | ||
return element; | ||
} | ||
function renderCardSection([type, name, payload], renderState) { | ||
let { cards } = renderState; | ||
let card = cards[name]; | ||
if (!card) { | ||
throw new Error(`Cannot render unknown card named ${name}`); | ||
findCard(name) { | ||
for (let i=0; i < this.cards.length; i++) { | ||
if (this.cards[i].name === name) { | ||
return this.cards[i]; | ||
} | ||
} | ||
if (name === ImageCard.name) { | ||
return ImageCard; | ||
} | ||
return this._createUnknownCard(name); | ||
} | ||
if (!payload) { | ||
payload = {}; | ||
_createUnknownCard(name) { | ||
return { | ||
name, | ||
type: RENDER_TYPE, | ||
render: this.unknownCardHandler | ||
}; | ||
} | ||
let element = createElement('div'); | ||
let cardOptions = renderState.options.cardOptions || {}; | ||
card.display.setup(element, cardOptions, {name}, payload); | ||
return element; | ||
} | ||
function renderMarkupSection([type, tagName, markers], renderState) { | ||
const element = createElement(tagName); | ||
renderMarkersOnElement(element, markers, renderState); | ||
return element; | ||
} | ||
_createCardArgument(card, payload={}) { | ||
let env = { | ||
name: card.name, | ||
isInEditor: false, | ||
onTeardown: (callback) => this._registerTeardownCallback(callback) | ||
}; | ||
function renderSection(section, renderState) { | ||
const [type] = section; | ||
switch (type) { | ||
case MARKUP_SECTION_TYPE: | ||
return renderMarkupSection(section, renderState); | ||
case IMAGE_SECTION_TYPE: | ||
return renderImageSection(section, renderState); | ||
case LIST_SECTION_TYPE: | ||
return renderListSection(section, renderState); | ||
case CARD_SECTION_TYPE: | ||
return renderCardSection(section, renderState); | ||
default: | ||
throw new Error('Unimplement renderer for type ' + type); | ||
let options = this.cardOptions; | ||
return { env, options, payload }; | ||
} | ||
} | ||
function validateVersion(version) { | ||
if (version !== '0.2.0') { | ||
throw new Error(`Unexpected Mobiledoc version "${version}"`); | ||
_registerTeardownCallback(callback) { | ||
this._teardownCallbacks.push(callback); | ||
} | ||
} | ||
export default class Renderer { | ||
/** | ||
* @param {Mobiledoc} mobiledoc | ||
* @param {DOMNode} [rootElement] defaults to an empty div | ||
* @param {Object} [cards] Each top-level property on the object is considered | ||
* to be a card's name, its value is an object with `setup` and (optional) `teardown` | ||
* properties | ||
* @return DOMNode | ||
*/ | ||
render({version, sections: sectionData}, root=createElement('div'), cards={}, options={}) { | ||
validateVersion(version); | ||
const [markerTypes, sections] = sectionData; | ||
cards.image = cards.image || ImageCard; | ||
const renderState = {root, markerTypes, cards, options}; | ||
renderCardSection([type, name, payload]) { | ||
let card = this.findCard(name); | ||
if (Array.isArray(cards)) { | ||
throw new Error('`cards` must be passed as an object, not an array.'); | ||
let cardWrapper = this._createCardElement(); | ||
let cardArg = this._createCardArgument(card, payload); | ||
let rendered = card.render(cardArg); | ||
this._validateCardRender(rendered, card.name); | ||
if (rendered) { | ||
appendChild(cardWrapper, rendered); | ||
} | ||
return cardWrapper; | ||
} | ||
sections.forEach(section => { | ||
let rendered = renderSection(section, renderState); | ||
appendChild(renderState.root, rendered); | ||
}); | ||
_createCardElement() { | ||
return createElement('div'); | ||
} | ||
return root; | ||
_validateCardRender(rendered, cardName) { | ||
if (!rendered) { | ||
return; | ||
} | ||
if (typeof rendered !== 'object') { | ||
throw new Error(`Card "${cardName}" must render ${RENDER_TYPE}, but result was "${rendered}"`); | ||
} | ||
} | ||
renderMarkupSection([type, tagName, markers]) { | ||
const element = createElement(tagName); | ||
this.renderMarkersOnElement(element, markers); | ||
return element; | ||
} | ||
} | ||
@@ -9,2 +9,6 @@ export function createElement(tagName) { | ||
export function removeChild(target, child) { | ||
target.removeChild(child); | ||
} | ||
function addHTMLSpaces(text) { | ||
@@ -22,1 +26,5 @@ let nbsp = '\u00A0'; | ||
} | ||
export function createDocumentFragment() { | ||
return document.createDocumentFragment(); | ||
} |
{ | ||
"name": "mobiledoc-dom-renderer", | ||
"version": "0.1.18", | ||
"version": "0.2.0", | ||
"description": "Renders Mobiledoc input to DOM output", | ||
@@ -5,0 +5,0 @@ "scripts": { |
## Mobiledoc DOM Renderer [![Build Status](https://travis-ci.org/bustlelabs/mobiledoc-dom-renderer.svg?branch=master)](https://travis-ci.org/bustlelabs/mobiledoc-dom-renderer) | ||
This is a DOM renderer for the [Mobiledoc](https://github.com/bustlelabs/content-kit-editor/blob/master/MOBILEDOC.md) format used | ||
This is a DOM renderer for the [Mobiledoc](https://github.com/bustlelabs/mobiledoc-kit/blob/master/MOBILEDOC.md) format used | ||
by [Mobiledoc-Kit](https://github.com/bustlelabs/mobiledoc-kit). | ||
@@ -12,3 +12,3 @@ | ||
var mobiledoc = { | ||
version: "0.1", | ||
version: "0.2.0", | ||
sections: [ | ||
@@ -30,6 +30,6 @@ [ // markers | ||
}; | ||
var renderer = new MobiledocDOMRenderer(); | ||
var cards = {}; | ||
var rendered = renderer.render(mobiledoc, document.createElement('div'), cards); | ||
document.getElementById('output').appendChild(rendered); | ||
var renderer = new MobiledocDOMRenderer({cards: []}); | ||
var rendered = renderer.render(mobiledoc); | ||
var result = rendered.result; | ||
document.getElementById('output').appendChild(result); | ||
// renders <div><p><b>hello world</b></b></div> | ||
@@ -39,2 +39,11 @@ // into 'output' element | ||
The Renderer constructor accepts a single object with the following optional properties: | ||
* `cards`: An array of card objects | ||
* `cardOptions`: Options to pass to cards when they are rendered | ||
* `unknownCardHandler`: A method that will be called when any unknown card is enountered | ||
The return value from `renderer.render(mobiledoc)` is an object with two properties: | ||
* `result` The rendered result. A DOMNode | ||
* `teardown` A teardown method that, when called, will tear down the rendered mobiledoc and call any teardown handlers that were registered by cards in the mobiledoc. | ||
### Tests | ||
@@ -41,0 +50,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
251554
25
5858
57