ciril
Version:
A javascript data binding library
1,527 lines (1,272 loc) • 455 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (global){
'use strict';
var _flowgraph = require('./core/flowgraph');
var _flowgraph2 = _interopRequireDefault(_flowgraph);
var _flownode = require('./core/flownode');
var _flownode2 = _interopRequireDefault(_flownode);
var _classes = require('./factory/classes');
var _classes2 = _interopRequireDefault(_classes);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
if (!global._babelPolyfill) {
require('babel-polyfill');
}
var Ciril = {
// core/flownode.js
FlowNode: _flownode2.default,
Transformer: _flownode.Transformer,
wrap: _flownode.wrap,
// FlowGraph API
// core/flowgraph.js
FlowGraph: _flowgraph2.default,
// fields for debugging
_nodes: _flowgraph2.default.nodes,
_bindings: _flowgraph2.default.bindings,
_inputs: _flowgraph2.default.inputs,
// public methods
register: _flowgraph2.default.register.bind(_flowgraph2.default),
isRegistered: _flowgraph2.default.isRegistered.bind(_flowgraph2.default),
remove: _flowgraph2.default.remove.bind(_flowgraph2.default),
removeAll: _flowgraph2.default.removeAll.bind(_flowgraph2.default),
bind: _flowgraph2.default.bind.bind(_flowgraph2.default),
bindAll: _flowgraph2.default.bindAll.bind(_flowgraph2.default),
bindInputs: _flowgraph2.default.bindInputs.bind(_flowgraph2.default),
bindAllInputs: _flowgraph2.default.bindAllInputs.bind(_flowgraph2.default),
unbind: _flowgraph2.default.unbind.bind(_flowgraph2.default),
unbindAll: _flowgraph2.default.unbindAll.bind(_flowgraph2.default),
synchronize: _flowgraph2.default.synchronize.bind(_flowgraph2.default),
synchronizeAll: _flowgraph2.default.synchronizeAll.bind(_flowgraph2.default),
desynchronize: _flowgraph2.default.desynchronize.bind(_flowgraph2.default),
desynchronizeAll: _flowgraph2.default.desynchronizeAll.bind(_flowgraph2.default),
nodeFromUuid: _flowgraph2.default.nodeFromUuid.bind(_flowgraph2.default),
getNodeState: _flowgraph2.default.getNodeState.bind(_flowgraph2.default),
update: _flowgraph2.default.update.bind(_flowgraph2.default),
updateAll: _flowgraph2.default.updateAll.bind(_flowgraph2.default),
updateSync: _flowgraph2.default.updateSync.bind(_flowgraph2.default),
updateAllSync: _flowgraph2.default.updateAllSync.bind(_flowgraph2.default),
flush: _flowgraph2.default.flush.bind(_flowgraph2.default),
clear: _flowgraph2.default.clear.bind(_flowgraph2.default),
// Factory methods
// factory/classes.js
createClass: _classes2.default,
createMixin: _classes.createMixin
};
module.exports = Ciril;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./core/flowgraph":2,"./core/flownode":3,"./factory/classes":4,"babel-polyfill":5}],2:[function(require,module,exports){
'use strict';
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; }; }();
Object.defineProperty(exports, "__esModule", {
value: true
});
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _remove = require('lodash/array/remove');
var _remove2 = _interopRequireDefault(_remove);
var _cloneDeep = require('lodash/lang/cloneDeep');
var _cloneDeep2 = _interopRequireDefault(_cloneDeep);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
// renaming
var hasProp = require('lodash/object/has');
/**
* Maintains bindings between various nodes, and
* manages all node updates. The nodes themselves
* are unaware of their bindings -- this information
* is kept within the FlowGraph.
*
* The FlowGraph structure is meant for internal
* use. The public API is exposed through the Ciril
* object.
*/
var _FlowGraph = function () {
function _FlowGraph() {
_classCallCheck(this, _FlowGraph);
// uuid --> node
this.nodes = new Map([]);
// uuid --> Set<uuid>
this.bindings = new Map([]);
// uuid --> [uuid]
this.inputs = new Map([]);
// Set<Promise>
this.pending = new Set([]);
}
/**
* Register the node with this FlowGraph.
* @param node
* the node
* @return
* true iff the node doesn't already
* exist in the store.
*/
_createClass(_FlowGraph, [{
key: 'register',
value: function register(node) {
if (!hasProp(node, 'uuid')) throw new Error('register(node): node must ' + 'have a uuid.');
var uuid = node.uuid;
if (this.nodes.has(uuid)) throw new Error('register(node): a node with ' + 'the given uuid is already registered, ' + 'try generating a new one.');
this.nodes.set(uuid, node);
this.bindings.set(uuid, new Set([]));
this.inputs.set(uuid, []);
}
/**
* Remove the given nodes from the FlowGraph.
* Connected edges are removed as well.
* @param nodes
* the nodes to remove
*/
}, {
key: 'remove',
value: function remove() {
for (var _len = arguments.length, nodes = Array(_len), _key = 0; _key < _len; _key++) {
nodes[_key] = arguments[_key];
}
this.removeAll(nodes);
}
/**
* Remove the given nodes from the FlowGraph.
* Connected edges are removed as well.
* @param nodes
* the nodes to remove
*/
}, {
key: 'removeAll',
value: function removeAll(nodes) {
var _this = this;
nodes.forEach(function (node) {
var uuid = node.uuid;
_this.inputs.get(uuid).map(function (id) {
return _this.nodeFromUuid(id);
}).forEach(function (inp) {
return _this.unbind(inp, _this);
});
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = _this.bindings.get(uuid)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var id = _step.value;
var dest = _this.nodeFromUuid(id);
_this.unbind(node, dest);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
node.onRemove();
_this.nodes.delete(uuid);
});
}
/**
* Create a one-way data binding between
* the source and each of the destinations.
* @param source
* the source node
* @param destinations
* the destination nodes
* @return
* true iff source and destinations
* are registered
*/
}, {
key: 'bind',
value: function bind(source) {
for (var _len2 = arguments.length, destinations = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
destinations[_key2 - 1] = arguments[_key2];
}
return this.bindAll(source, destinations);
}
/**
* Create a one-way data binding between
* the source and each of the destinations.
* @param source
* the source node
* @param destinations
* the destination nodes
* @return true iff source and destinations
* are registered
*/
}, {
key: 'bindAll',
value: function bindAll(source, destinations) {
if (!this.isRegistered(source)) throw new Error('bindAll(...): source not registered.');
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = destinations[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var node = _step2.value;
if (!this.isRegistered(node)) throw new Error('bindAll(...): destination not registered.');
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = destinations[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var node = _step3.value;
this.inputs.get(node.uuid).push(source.uuid);
this.bindings.get(source.uuid).add(node.uuid);
node.onBindInput(source);
source.onBind(node);
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
return true;
}
/**
* Bind the input nodes to the given node, in
* the given order.
* @param node
* the output node
* @param inputs
* the input nodes
*/
}, {
key: 'bindInputs',
value: function bindInputs(node) {
for (var _len3 = arguments.length, inputs = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
inputs[_key3 - 1] = arguments[_key3];
}
this.bindAllInputs(node, inputs);
}
/**
* Bind the input nodes to the given node, in
* the given order.
* @param node
* the output node
* @param inputs
* the input nodes
*/
}, {
key: 'bindAllInputs',
value: function bindAllInputs(node, inputs) {
var _this2 = this;
var uuid = node.uuid;
var _inputs = this.inputs.get(uuid);
if (_inputs.length > 0) {
console.warn("bindAllInputs(...): Overwriting existing inputs");
_inputs.map(function (id) {
return _this2.nodeFromUuid(id);
}).forEach(function (inp) {
return _this2.unbind(inp, node);
});
}
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = inputs[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var inp = _step4.value;
this.bind(inp, node);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
/**
* Remove bindings between the source
* and destinations.
* @param source
* the source node
* @param destinations
* the destination nodes
* @return true iff source and destinations
* are registered
*/
}, {
key: 'unbind',
value: function unbind(source) {
for (var _len4 = arguments.length, destinations = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
destinations[_key4 - 1] = arguments[_key4];
}
return this.unbindAll(source, destinations);
}
/**
* Remove bindings between the source
* and destinations.
* @param source
* the source node
* @param destinations
* the destination nodes
* @return true iff source and destinations
* are registered
*/
}, {
key: 'unbindAll',
value: function unbindAll(source, destinations) {
if (!this.nodes.has(source.uuid)) return false;
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = destinations[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var node = _step5.value;
if (!this.nodes.has(node.uuid)) {
console.warn("unbindAll(...): Attempting to unbind unregistered node.");
continue;
}
node.onUnbindInput(source);
source.onUnbind(node);
(0, _remove2.default)(this.inputs.get(node.uuid), function (id) {
return id === source.uuid;
});
this.bindings.get(source.uuid).delete(node.uuid);
// remove direct reference to input state objects.
node.state = (0, _cloneDeep2.default)(node.state);
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
return true;
}
/**
* Create a cycle of bindings within nodes.
* @param nodes
* the nodes to synchronize
*/
}, {
key: 'synchronize',
value: function synchronize() {
for (var _len5 = arguments.length, nodes = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
nodes[_key5] = arguments[_key5];
}
this.synchronizeAll(nodes);
}
/**
* Create a cycle of bindings within nodes.
* @param nodes
* a list of the nodes to synchronize
*/
}, {
key: 'synchronizeAll',
value: function synchronizeAll(nodes) {
var _this3 = this;
if (nodes.length <= 1) return false;
nodes.reduce(function (p, c, i, a) {
var next = (i + 1) % nodes.length;
return _this3.bind(c, a[next]);
});
}
/**
* Inverse of this.synchronize(...nodes).
* @param nodes
* the nodes to desynchronize
*/
}, {
key: 'desynchronize',
value: function desynchronize() {
for (var _len6 = arguments.length, nodes = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
nodes[_key6] = arguments[_key6];
}
this.desynchronizeAll(nodes);
}
/**
* Inverse of this.synchronizeAll(nodes).
* @param nodes
* a list of the nodes to desynchronize
*/
}, {
key: 'desynchronizeAll',
value: function desynchronizeAll(nodes) {
var _this4 = this;
if (nodes.length <= 1) return;
nodes.reduce(function (p, c, i, a) {
_this4.unbind(p, c);
return c;
}, nodes[nodes.length - 1]);
}
/**
* Get the node keyed by uuid.
* @param uuid
* the node's uuid
* @return
* the node keyed by uuid
*/
}, {
key: 'nodeFromUuid',
value: function nodeFromUuid(uuid) {
return this.nodes.has(uuid) ? this.nodes.get(uuid) : null;
}
/**
* Get the state of the node keyed
* by uuid.
* @param uuid
* the node's uuid
* @return
* the node's state
*/
}, {
key: 'getNodeState',
value: function getNodeState(uuid) {
return this.nodeFromUuid(uuid).getState();
}
/**
* Should be called when a node's data changes.
* Recursively updates bound nodes asynchronously.
* @param nodes
* the nodes to update
* @return
* a Promise for the update
*/
}, {
key: 'update',
value: function update() {
for (var _len7 = arguments.length, nodes = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
nodes[_key7] = arguments[_key7];
}
return this.updateAll(nodes);
}
/**
* Should be called when a node's data changes.
* Recursively updates bound nodes asynchronously.
* @param nodes
* the nodes to update
* @return
* a Promise for the update
*/
}, {
key: 'updateAll',
value: function updateAll(nodes) {
var _this5 = this;
var p = _bluebird2.default.map(nodes, function (node) {
var uuid = node.uuid;
return new _bluebird2.default(function (resolve, reject) {
resolve(_this5._terminalsFrom(uuid));
}).map(function (id) {
return _this5._update(id);
}).all();
}).all(); // TODO: test with inconsistent updates
this.pending.add(p);
return p.then(function (res) {
return _this5.pending.delete(p);
});
}
/**
* Synchronous version of update(node).
* Assumes setState() implementations on
* dependent nodes are synchronous.
* @param nodes
* the nodes to update
*/
}, {
key: 'updateSync',
value: function updateSync() {
for (var _len8 = arguments.length, nodes = Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
nodes[_key8] = arguments[_key8];
}
this.updateAllSync(nodes);
}
/**
* Synchronous version of update(node).
* Assumes setState() implementations on
* dependent nodes are synchronous.
* @param nodes
* the nodes to update
*/
}, {
key: 'updateAllSync',
value: function updateAllSync(nodes) {
var _this6 = this;
nodes.forEach(function (node) {
var uuid = node.uuid;
_this6._terminalsFrom(uuid).forEach(function (id) {
return _this6._updateSync(id);
});
});
}
/**
* Removes all nodes and bindings. Completes
* Pending updates first.
* @param safe
* if safe, pending updates will
* be completed before FlowGraph is
* cleared.
* @return
* a Promise for the clear, void if !safe
*/
}, {
key: 'clear',
value: function clear() {
var _this7 = this;
var safe = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
if (safe) {
// Finish remaining updates.
return this.flush().then(function (res) {
return _this7._clear();
}).caught(function (e) {
console.warn(e.stack);
_this7._clear();
});
} else {
this._clear();
}
}
/**
* Return a promise for the completion of all pending
* promises.
* @return
* a promise for the completion.
*/
}, {
key: 'flush',
value: function flush() {
return _bluebird2.default.all(Array.from(this.pending));
}
/**
* Clear this FlowGraph.
*/
}, {
key: '_clear',
value: function _clear() {
this.nodes.clear();
this.bindings.clear();
this.inputs.clear();
this.pending.clear();
}
/**
* Updates terminal node keyed by uuid, recursively
* updating dirty nodes when necessary.
* @param uuid
* the terminal uuid
* @return
* a Promise for the update
* @api private
*/
}, {
key: '_update',
value: function _update(uuid) {
var _this8 = this;
var node = this.nodeFromUuid(uuid);
if (!node.isDirty()) return _bluebird2.default.resolve(false);
node.markDirty(false);
return _bluebird2.default.map(this.inputs.get(uuid).filter(function (uuid) {
return _this8.nodeFromUuid(uuid).isDirty();
}), function (id) {
return _this8._update(id);
}).all()
// then update node
.then(function (res) {
var inputs = _this8.inputs.get(uuid);
var changed = inputs.length > 0 ? inputs.reduce(function (p, uuid, i, a) {
return p || _this8.nodeFromUuid(uuid).changed;
}) : false;
if (changed) {
var args = inputs.map(function (id) {
return _this8.getNodeState(id);
});
// supporting asynchronous setState() functions
return _bluebird2.default.resolve(node.setState.apply(node, args));
}
node.changed = false;
return _bluebird2.default.resolve(false);
});
}
/**
* Synchronous version of _updateSync(node).
* Assumes setState() implementations on
* dependent nodes are synchronous.
* @param node
* the node to update
* @api private
*/
}, {
key: '_updateSync',
value: function _updateSync(uuid) {
var _this9 = this;
var node = this.nodeFromUuid(uuid);
if (!node.isDirty()) return false;
node.markDirty(false);
var upstream = this.inputs.get(uuid).filter(function (id) {
return _this9.nodeFromUuid(id).isDirty();
}).map(function (id) {
return _this9._updateSync(id);
});
var inputs = this.inputs.get(uuid);
var changed = inputs.length > 0 ? inputs.reduce(function (p, uuid, i, a) {
return p || _this9.nodeFromUuid(uuid).changed;
}) : false;
if (changed) {
var args = inputs.map(function (id) {
return _this9.getNodeState(id);
});
return node.setState.apply(node, _toConsumableArray(args));
}
node.changed = false;
return false;
}
/**
* Get terminal nodes from node keyed by uuid
* @param uuid
* the node uuid
* @return
* the terminal nodes
* @api private
*/
}, {
key: '_terminalsFrom',
value: function _terminalsFrom(uuid) {
var visited = new Set();
var stack = [uuid];
var terminals = [];
// Depth-first search from uuid
while (stack.length > 0) {
var current = stack.pop();
var terminal = true;
visited.add(current);
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = this.bindings.get(current)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var child = _step6.value;
if (!visited.has(child)) {
terminal = false;
stack.push(child);
this.nodeFromUuid(child).markDirty(true);
}
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
if (terminal) terminals.push(current);
}
return terminals;
}
}, {
key: 'isRegistered',
value: function isRegistered(node) {
return this.nodes.has(node.uuid);
}
}]);
return _FlowGraph;
}();
var FlowGraph = new _FlowGraph();
exports.default = FlowGraph;
},{"bluebird":7,"lodash/array/remove":196,"lodash/lang/cloneDeep":238,"lodash/object/has":246}],3:[function(require,module,exports){
'use strict';
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; }; }();
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Transformer = undefined;
exports.wrap = wrap;
exports.NodeConstructor = NodeConstructor;
var _flowgraph = require('./flowgraph');
var _flowgraph2 = _interopRequireDefault(_flowgraph);
var _uuid = require('uuid');
var _uuid2 = _interopRequireDefault(_uuid);
var _isEqual = require('lodash/lang/isEqual');
var _isEqual2 = _interopRequireDefault(_isEqual);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function conservativeMerge(target) {
for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
sources[_key - 1] = arguments[_key];
}
sources.forEach(function (source) {
Object.getOwnPropertyNames(source).forEach(function (name) {
if (name === 'constructor' || target[name]) return;
Object.defineProperty(target, name, Object.getOwnPropertyDescriptor(source, name));
});
});
}
function createTransformer(fn) {
return new Transformer(fn);
}
function createSimpleWrapper(obj) {
var node = new FlowNode(obj);
return node;
}
function createWrapper(obj) {
if (!obj.hasOwnProperty('state')) return createSimpleWrapper(obj);
var node = new FlowNode(null, false);
// Add FlowNode fields
conservativeMerge(obj, node, FlowNode.prototype);
obj.register();
return obj;
}
/**
* Wrap the given object or function or value in a FlowNode.
* @param obj
* the object, function, or value to wrap
* @return
* the wrapper FlowNode
*/
function wrap(obj) {
if (obj instanceof FlowNode) return obj.register();
switch (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) {
case 'function':
return createTransformer(obj);
case 'object':
return createWrapper(obj);
default:
return createSimpleWrapper(obj);
}
}
/**
* Constructor for creating a FlowNode. Should be
* called if creating a mixin class with FlowNode.
*/
function NodeConstructor() {
var initialState = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
var register = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
Object.defineProperty(this, 'dirty', {
writable: true,
value: false
});
Object.defineProperty(this, 'uuid', {
value: _uuid2.default.v4()
});
Object.defineProperty(this, 'state', {
writable: true,
value: initialState
});
Object.defineProperty(this, 'changed', {
writable: true,
value: false
});
if (register) _flowgraph2.default.register(this);
}
/**
* A FlowNode stores some state and can have
* input and output nodes. When extending this class,
* the primary implementation details to consider
* are how input data is handled in setState(), and
* how object data is serialized in getState().
*
* The core subclasses of FlowNode are Transformer
* and DataNode.
*/
var FlowNode = function () {
/**
* On creation, a FlowNode registers itself
* with the FlowGraph.
*/
function FlowNode() {
var initialState = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
var register = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
_classCallCheck(this, FlowNode);
NodeConstructor.call(this, initialState, register);
}
/**
* Same as Ciril.register(this).
*/
_createClass(FlowNode, [{
key: 'register',
value: function register() {
_flowgraph2.default.register(this);
return this;
}
/**
* Same as Ciril.isRegistered(this).
*/
}, {
key: 'isRegistered',
value: function isRegistered() {
return _flowgraph2.default.isRegistered(this);
}
/**
* Bind this node to a transformer node.
* @param fn
* the transformer function
* @return
* the transformer node
*/
}, {
key: 'transform',
value: function transform(fn) {
var transformer = new Transformer(fn);
_flowgraph2.default.bind(this, transformer);
return transformer;
}
}, {
key: 'bind',
/**
* Same as Ciril.bind(this, ...destinations)
* @param destinations
* the destination nodes
* @return
* the last node in destinations
*/
value: function bind() {
for (var _len2 = arguments.length, destinations = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
destinations[_key2] = arguments[_key2];
}
return this.bindAll(destinations);
}
/**
* Same as Ciril.bindAll(this, ...destinations)
* @param destinations
* the destination nodes
* @return
* the last node in destinations
*/
}, {
key: 'bindAll',
value: function bindAll(destinations) {
var dests = destinations.map(function (e) {
return _flowgraph2.default.isRegistered(e) ? e : wrap(e);
});
_flowgraph2.default.bindAll(this, dests);
return dests[dests.length - 1];
}
/**
* Same as Ciril.synchronize(this, ..nodes).
* @param nodes
* the nodes to synchronize with
* @return
* this node
*/
}, {
key: 'synchronize',
value: function synchronize() {
for (var _len3 = arguments.length, nodes = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
nodes[_key3] = arguments[_key3];
}
nodes.reduce(function (p, c, i, a) {
return p.bind(c);
}, this);
return this;
}
/**
* Same as Ciril.synchronize(this, ..nodes).
* @param nodes
* the nodes to synchronize with
* @return
* this node
*/
}, {
key: 'synchronizeAll',
value: function synchronizeAll(nodes) {
nodes.reduce(function (p, c, i, a) {
return p.bind(c);
}, this);
return this;
}
/**
* Same as Ciril.unbind(this, ...destinations).
* @param destinations
* the destination nodes
*/
}, {
key: 'unbind',
value: function unbind() {
for (var _len4 = arguments.length, destinations = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
destinations[_key4] = arguments[_key4];
}
_flowgraph2.default.unbindAll(this, destinations);
}
/**
* Same as Ciril.unbindAll(this, destinations).
* @param destinations
*/
}, {
key: 'unbindAll',
value: function unbindAll(destinations) {
_flowgraph2.default.unbindAll(this, destinations);
}
/**
* Same as Ciril.update(this).
*/
}, {
key: 'update',
value: function update() {
return _flowgraph2.default.update(this);
}
/**
* Same as Ciril.updateSync(this).
*/
}, {
key: 'updateSync',
value: function updateSync() {
_flowgraph2.default.updateSync(this);
}
/**
* Ckecks whether this node has been
* marked dirty, which is if its state
* is out of date.
* @return
* true iff the node is dirty.
*/
}, {
key: 'isDirty',
value: function isDirty() {
return this.dirty;
}
/**
* Mark this node as dirty or clean.
* Be careful using this method, as it
* affects the update algorithm. It is
* meant to be used by Ciril for
* bookkeeping purposes.
* @param dirty
* true iff marking dirty
*/
}, {
key: 'markDirty',
value: function markDirty(dirty) {
this.dirty = dirty;
}
/**
* Same as cirl.bindInputs(this, ...inputs).
* @param inputs
* the input nodes
* @return
* this node
*/
}, {
key: 'bindInputs',
value: function bindInputs() {
for (var _len5 = arguments.length, inputs = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
inputs[_key5] = arguments[_key5];
}
_flowgraph2.default.bindAllInputs(this, inputs);
return this;
}
/**
* Same as Ciril.bindAllInputs(this, inputs).
* @param inputs
* the input nodes
* @return
* this node
*/
}, {
key: 'bindAllInputs',
value: function bindAllInputs(inputs) {
_flowgraph2.default.bindAllInputs(this, inputs);
return this;
}
/**
* Same as Ciril.remove(this).
*/
}, {
key: 'remove',
value: function remove() {
_flowgraph2.default.remove(this);
}
/**
* Get this node's state.
* @return
* the node's state
*/
}, {
key: 'getState',
value: function getState() {
return this.state;
}
/**
* Set this node's state.
* @param args
* the input state objects
* @return
* true iff state changed
*/
}, {
key: 'setState',
value: function setState() {
for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
args[_key6] = arguments[_key6];
}
if (!args.reduce(function (p, c, i, a) {
return p && (0, _isEqual2.default)(a[0], a[i]);
})) {
console.warn('setState(...): Inconsistent state ' + 'detected, make sure transforms are correct.\n ' + ('inputs: ' + args));
}
if (!(0, _isEqual2.default)(this.state, args[0])) {
this.state = args[0];
this.changed = true;
return true;
}
this.changed = false;
return false;
}
/**
* Called before node is removed.
* Should be overriden.
*/
}, {
key: 'onRemove',
value: function onRemove() {}
/**
* Called before an input is unbound.
* Should be overriden.
*/
}, {
key: 'onUnbindInput',
value: function onUnbindInput(input) {}
/**
* Called after an input is bound.
* Should be overriden.
*/
}, {
key: 'onBindInput',
value: function onBindInput(input) {}
/**
* Called before an output is unbound.
* Should be overriden.
*/
}, {
key: 'onUnbind',
value: function onUnbind(node) {}
/**
* Called after an output is bound.
* Should be overriden.
*/
}, {
key: 'onBind',
value: function onBind(node) {}
}]);
return FlowNode;
}();
/**
* A Transformer represents a functional transform
* from an input state to an output state. Its purpose
* in the FlowGraph is to compute values from input
* data.
*/
exports.default = FlowNode;
var Transformer = exports.Transformer = function (_FlowNode) {
_inherits(Transformer, _FlowNode);
function Transformer(fn) {
_classCallCheck(this, Transformer);
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(Transformer).call(this));
_this.fn = fn;
return _this;
}
/**
* Compute new state based on input state.
* @param args
* the input state objects
* @return
* true iff state changed
*/
_createClass(Transformer, [{
key: 'setState',
value: function setState() {
for (var _len7 = arguments.length, args = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
args[_key7] = arguments[_key7];
}
var state = this.fn.apply(this, args);
if (!(0, _isEqual2.default)(this.state, state)) {
this.state = state;
this.changed = true;
return true;
}
this.changed = false;
return false;
}
}]);
return Transformer;
}(FlowNode);
},{"./flowgraph":2,"lodash/lang/isEqual":241,"uuid":255}],4:[function(require,module,exports){
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createMixin = createMixin;
exports.default = createClass;
var _flownode = require('../core/flownode');
var _flownode2 = _interopRequireDefault(_flownode);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var assign = require('object-assign');
function conservativeMerge(target, source) {
Object.getOwnPropertyNames(source).forEach(function (name) {
if (name !== "constructor" && !target[name]) Object.defineProperty(target, name, Object.getOwnPropertyDescriptor(source, name));
});
}
/**
* Helper method to mix source class into target class. Existing
* properties are NOT overriden.
* @param target {function}
* @param source {function}
*/
function mixin(target) {
var proto = target.prototype;
for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
sources[_key - 1] = arguments[_key];
}
sources.forEach(function (source) {
source = source.prototype;
conservativeMerge(proto, source);
});
return target;
}
/**
* Create a FlowNode mixin class from a constructor.
* @param Constructor {function}
* @return
* A FlowNode mixin class
*/
function createMixin(Constructor) {
if (typeof Constructor !== 'function') throw new Error('createMixin(...): argument ' + ('should be function, got ' + (typeof Constructor === 'undefined' ? 'undefined' : _typeof(Constructor))));
var getInitialState = Constructor.prototype.getInitialState;
var initialState = typeof getInitialState === 'function' ? getInitialState.apply(this) : null;
var register = Constructor.prototype.registerOnCreate || true;
var Mixin = function (_Constructor) {
_inherits(Mixin, _Constructor);
function Mixin() {
_classCallCheck(this, Mixin);
return _possibleConstructorReturn(this, Object.getPrototypeOf(Mixin).apply(this, arguments));
}
return Mixin;
}(Constructor);
;
mixin(Mixin, _flownode2.default);
var proto = Mixin.prototype;
// override constructor
Mixin = function Mixin() {
_flownode.NodeConstructor.call(this, initialState, register);
Constructor.apply(this, arguments);
};
// copy back prototype
Mixin.prototype = proto;
return Mixin;
}
/**
* Create a FlowNode class from a specification.
* @param spec {object}
*/
function createClass(spec) {
if ((typeof spec === 'undefined' ? 'undefined' : _typeof(spec)) !== 'object') throw new Error('createClass(...): class ' + 'specification must be an object, ' + ('got ' + (typeof spec === 'undefined' ? 'undefined' : _typeof(spec))));
var NewClass = function (_FlowNode) {
_inherits(NewClass, _FlowNode);
function NewClass() {
_classCallCheck(this, NewClass);
return _possibleConstructorReturn(this, Object.getPrototypeOf(NewClass).apply(this, arguments));
}
return NewClass;
}(_flownode2.default);
;
assign(NewClass.prototype, spec);
return NewClass;
}
},{"../core/flownode":3,"object-assign":252}],5:[function(require,module,exports){
(function (global){
"use strict";
require("core-js/shim");
require("babel-regenerator-runtime");
if (global._babelPolyfill) {
throw new Error("only one instance of babel-polyfill is allowed");
}
global._babelPolyfill = true;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"babel-regenerator-runtime":6,"core-js/shim":194}],6:[function(require,module,exports){
(function (process,global){
/**
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
* additional grant of patent rights can be found in the PATENTS file in
* the same directory.
*/
!(function(global) {
"use strict";
var hasOwn = Object.prototype.hasOwnProperty;
var undefined; // More compressible than void 0.
var iteratorSymbol =
typeof Symbol === "function" && Symbol.iterator || "@@iterator";
var inModule = typeof module === "object";
var runtime = global.regeneratorRuntime;
if (runtime) {
if (inModule) {
// If regeneratorRuntime is defined globally and we're in a module,
// make the exports object identical to regeneratorRuntime.
module.exports = runtime;
}
// Don't bother evaluating the rest of this file if the runtime was
// already defined globally.
return;
}
// Define the runtime globally (as expected by generated code) as either
// module.exports (if we're in a module) or a new, empty object.
runtime = global.regeneratorRuntime = inModule ? module.exports : {};
function wrap(innerFn, outerFn, self, tryLocsList) {
// If outerFn provided, then outerFn.prototype instanceof Generator.
var generator = Object.create((outerFn || Generator).prototype);
var context = new Context(tryLocsList || []);
// The ._invoke method unifies the implementations of the .next,
// .throw, and .return methods.
generator._invoke = makeInvokeMethod(innerFn, self, context);
return generator;
}
runtime.wrap = wrap;
// Try/catch helper to minimize deoptimizations. Returns a completi