Skip to content

Commit

Permalink
Fix custom elements conform with DOMUser #73
Browse files Browse the repository at this point in the history
  • Loading branch information
Andre Medeiros committed Feb 11, 2015
1 parent f4dae2c commit df6a5db
Show file tree
Hide file tree
Showing 9 changed files with 14,621 additions and 20,655 deletions.
34,912 changes: 14,471 additions & 20,441 deletions rx-run/dist/cycle.js

Large diffs are not rendered by default.

24 changes: 10 additions & 14 deletions rx-run/dist/cycle.min.js

Large diffs are not rendered by default.

99 changes: 34 additions & 65 deletions rx-run/src/custom-elements.js
Original file line number Diff line number Diff line change
@@ -1,90 +1,59 @@
'use strict';
var InputProxy = require('./input-proxy');
var DataFlowNode = require('./data-flow-node');
var Utils = require('./utils');

function getEventsOrigDestMap(vtree) {
var map = {};
for (var key in vtree.properties) {
if (vtree.properties.hasOwnProperty(key) &&
typeof key === 'string' && key.search(/^on[a-z]+/) === 0)
function makeDispatchFunction(element, eventName) {
return function dispatchCustomEvent(evData) {
var event;
try {
event = new Event(eventName);
} catch (err) {
event = document.createEvent('Event');
event.initEvent(eventName, true, true);
}
event.data = evData;
element.dispatchEvent(event);
};
}

function subscribeDispatchers(element, eventStreams) {
if (!eventStreams || eventStreams === null || typeof eventStreams !== 'object') {
return;
}
for (var streamName in eventStreams) {
if (eventStreams.hasOwnProperty(streamName) &&
Utils.endsWithDolarSign(streamName) &&
typeof eventStreams[streamName].subscribe === 'function')
{
var originStreamName = key.replace(/^on/, '').concat('$');
var destinationStream = vtree.properties[key];
map[originStreamName] = destinationStream;
var eventName = streamName.slice(0, -1);
eventStreams[streamName].subscribe(makeDispatchFunction(element, eventName));
}
}
return map;
}

function createContainerElement(tagName) {
function createContainerElement(tagName, vtreeProperties) {
var elem = document.createElement('div');
elem.className = 'cycleCustomElement-' + tagName.toUpperCase();
elem.className = vtreeProperties.className || '';
elem.id = vtreeProperties.id || '';
elem.className += ' cycleCustomElement-' + tagName.toUpperCase();
elem.cycleCustomElementProperties = new InputProxy();
return elem;
}

function getOriginEventStreams(dataFlowNode) {
if (!(dataFlowNode instanceof DataFlowNode) ||
!Array.isArray(dataFlowNode.outputStreams))
{
return {};
}
return dataFlowNode.outputStreams
.filter(function (streamName) {
return Utils.endsWithDolarSign(streamName) && streamName !== 'vtree$';
})
.reduce(function (events, streamName) {
events[streamName] = dataFlowNode.get(streamName);
return events;
}, {});
}

function subscribeAndForward(origin$, destination$) {
origin$.subscribe(
function onNextWidgetEvent(x) { destination$.onNext(x); },
function onErrorWidgetEvent(e) { destination$.onError(e); },
function onCompletedWidgetEvent() { destination$.onCompleted(); }
);
}

function forwardOriginEventsToDestinations(events, origDestMap) {
for (var originStreamName in events) {
if (events.hasOwnProperty(originStreamName) &&
origDestMap.hasOwnProperty(originStreamName))
{
subscribeAndForward(events[originStreamName], origDestMap[originStreamName]);
}
}
}

function makeConstructor() {
return function customElementConstructor(vtree) {
this.type = 'Widget';
this.properties = vtree.properties;
this.eventsOrigDestMap = getEventsOrigDestMap(vtree);
};
}

// TODO Figure out the API for grabbing custom element events just like we have now
// events being grabbed from normal DOM using domUser.event$(selector, eventName).
// Maybe with custom DOM events emitted from the actual container element?

// TODO Remove the DOMUser from this since it will be specified inside the dataFlowNode

function makeInit(tagName, dataFlowNode) {
var DOMUser = require('./dom-user');
function makeInit(tagName, definitionFn) {
return function initCustomElement() {
var dfn = dataFlowNode.clone();
var elem = createContainerElement(tagName);
var user = new DOMUser(elem, false);
var events = getOriginEventStreams(dfn);
forwardOriginEventsToDestinations(events, this.eventsOrigDestMap);
user.inject(dfn);
dfn._inCustomElement = true;
dfn.inject(elem.cycleCustomElementProperties);
this.update(null, elem);
return elem;
var element = createContainerElement(tagName, this.properties);
var eventStreams = definitionFn(element, element.cycleCustomElementProperties);
subscribeDispatchers(element, eventStreams);
this.update(null, element);
return element;
};
}

Expand Down
6 changes: 3 additions & 3 deletions rx-run/src/cycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ var Cycle = {
* will be injected automatically into `foo$`.
*
* @param {String} tagName a name for identifying the custom element.
* @param {DataFlowNode} dataFlowNode the implementation of the custom element.
* @param {Function} definitionFn the implementation for the custom element.
* @function registerCustomElement
*/
registerCustomElement: function registerCustomElement(tagName, dataFlowNode) {
DOMUser.registerCustomElement(tagName, dataFlowNode);
registerCustomElement: function registerCustomElement(tagName, definitionFn) {
DOMUser.registerCustomElement(tagName, definitionFn);
},

/**
Expand Down
2 changes: 1 addition & 1 deletion rx-run/src/data-flow-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function DataFlowNode(definitionFn) {
if (wasInjected) {
console.warn('DataFlowNode has already been injected an input.');
}
if (!this._inCustomElement && definitionFn.length !== arguments.length) {
if (definitionFn.length !== arguments.length) {
console.warn('The call to inject() should provide the inputs that this ' +
'DataFlowNode expects according to its definition function.');
}
Expand Down
24 changes: 8 additions & 16 deletions rx-run/src/dom-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ function isElement(o) {
);
}

function DOMUser(container, useInternalContainer) {
if (typeof useInternalContainer === 'undefined') {
useInternalContainer = true;
}
this._useInternalContainer = useInternalContainer;
function DOMUser(container) {
// Find and prepare the container
this._domContainer = (typeof container === 'string') ?
document.querySelector(container) :
Expand All @@ -47,12 +43,12 @@ DOMUser.prototype = Object.create(DataFlowNode.prototype);
DOMUser.prototype._renderEvery = function renderEvery(vtree$) {
var self = this;
var rootNode;
if (self._useInternalContainer) {
if (self._domContainer.cycleCustomElementProperties) {
rootNode = self._domContainer;
} else {
rootNode = document.createElement('div');
self._domContainer.innerHTML = '';
self._domContainer.appendChild(rootNode);
} else {
rootNode = this._domContainer;
}
self._domContainer$.onNext(this._domContainer);
return vtree$.startWith(VDOM.h())
Expand Down Expand Up @@ -115,22 +111,18 @@ DOMUser.prototype.event$ = function event$(selector, eventName) {
});
};

DOMUser.registerCustomElement = function registerCustomElement(tagName, dataFlowNode) {
if (typeof tagName !== 'string' || typeof dataFlowNode !== 'object') {
DOMUser.registerCustomElement = function registerCustomElement(tagName, definitionFn) {
if (typeof tagName !== 'string' || typeof definitionFn !== 'function') {
throw new Error('registerCustomElement requires parameters `tagName` and ' +
'`dataFlowNode`.');
}
if (!dataFlowNode.get('vtree$')) {
throw new Error('The dataFlowNode for a custom element must export ' +
'`vtree$`.');
'`definitionFn`.');
}
tagName = tagName.toUpperCase();
if (DOMUser._customElements && DOMUser._customElements.hasOwnProperty(tagName)) {
throw new Error('Cannot register custom element `' + tagName + '` ' +
'for the DOMUser because that tagName is already registered.');
}
var WidgetClass = CustomElements.makeConstructor();
WidgetClass.prototype.init = CustomElements.makeInit(tagName, dataFlowNode);
WidgetClass.prototype.init = CustomElements.makeInit(tagName, definitionFn);
WidgetClass.prototype.update = CustomElements.makeUpdate();
DOMUser._customElements = DOMUser._customElements || {};
DOMUser._customElements[tagName] = WidgetClass;
Expand Down

0 comments on commit df6a5db

Please sign in to comment.