UNPKG

ciril

Version:
507 lines (418 loc) 14.6 kB
'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);