noflo
Version:
Flow-Based Programming environment for JavaScript
509 lines (466 loc) • 15.4 kB
JavaScript
(function() {
var Component, EventEmitter, IP, ProcessInput, ProcessOutput, ports,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
EventEmitter = require('events').EventEmitter;
ports = require('./Ports');
IP = require('./IP');
Component = (function(_super) {
__extends(Component, _super);
Component.prototype.description = '';
Component.prototype.icon = null;
function Component(options) {
this.error = __bind(this.error, this);
if (!options) {
options = {};
}
if (!options.inPorts) {
options.inPorts = {};
}
if (options.inPorts instanceof ports.InPorts) {
this.inPorts = options.inPorts;
} else {
this.inPorts = new ports.InPorts(options.inPorts);
}
if (!options.outPorts) {
options.outPorts = {};
}
if (options.outPorts instanceof ports.OutPorts) {
this.outPorts = options.outPorts;
} else {
this.outPorts = new ports.OutPorts(options.outPorts);
}
if (options.icon) {
this.icon = options.icon;
}
if (options.description) {
this.description = options.description;
}
this.started = false;
this.load = 0;
this.ordered = false;
this.autoOrdering = false;
this.outputQ = [];
this.activateOnInput = true;
this.forwardBrackets = {
"in": ['out', 'error']
};
this.bracketCounter = {};
this.dropEmptyBrackets = ['error'];
if ('ordered' in options) {
this.ordered = options.ordered;
}
if ('activateOnInput' in options) {
this.activateOnInput = options.activateOnInput;
}
if ('forwardBrackets' in options) {
this.forwardBrackets = options.forwardBrackets;
}
if ('dropEmptyBrackets' in options) {
this.dropEmptyBrackets = options.dropEmptyBrackets;
}
if (typeof options.process === 'function') {
this.process(options.process);
}
}
Component.prototype.getDescription = function() {
return this.description;
};
Component.prototype.isReady = function() {
return true;
};
Component.prototype.isSubgraph = function() {
return false;
};
Component.prototype.setIcon = function(icon) {
this.icon = icon;
return this.emit('icon', this.icon);
};
Component.prototype.getIcon = function() {
return this.icon;
};
Component.prototype.error = function(e, groups, errorPort) {
var group, _i, _j, _len, _len1;
if (groups == null) {
groups = [];
}
if (errorPort == null) {
errorPort = 'error';
}
if (this.outPorts[errorPort] && (this.outPorts[errorPort].isAttached() || !this.outPorts[errorPort].isRequired())) {
for (_i = 0, _len = groups.length; _i < _len; _i++) {
group = groups[_i];
this.outPorts[errorPort].beginGroup(group);
}
this.outPorts[errorPort].send(e);
for (_j = 0, _len1 = groups.length; _j < _len1; _j++) {
group = groups[_j];
this.outPorts[errorPort].endGroup();
}
this.outPorts[errorPort].disconnect();
return;
}
throw e;
};
Component.prototype.shutdown = function() {
return this.started = false;
};
Component.prototype.start = function() {
this.started = true;
return this.started;
};
Component.prototype.isStarted = function() {
return this.started;
};
Component.prototype.prepareForwarding = function() {
var inPort, outPort, outPorts, tmp, _i, _j, _len, _len1, _ref, _ref1;
_ref = this.forwardBrackets;
for (inPort in _ref) {
outPorts = _ref[inPort];
if (!(inPort in this.inPorts.ports)) {
delete this.forwardBrackets[inPort];
continue;
}
tmp = [];
for (_i = 0, _len = outPorts.length; _i < _len; _i++) {
outPort = outPorts[_i];
if (outPort in this.outPorts.ports) {
tmp.push(outPort);
}
}
if (tmp.length === 0) {
delete this.forwardBrackets[inPort];
} else {
this.forwardBrackets[inPort] = tmp;
this.bracketCounter[inPort] = 0;
}
}
tmp = [];
_ref1 = this.dropEmptyBrackets;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
outPort = _ref1[_j];
if (outPort in this.outPorts.ports) {
tmp.push(outPort);
}
}
return this.dropEmptyBrackets = tmp;
};
Component.prototype.process = function(handle) {
var name, port, _fn, _ref;
if (typeof handle !== 'function') {
throw new Error("Process handler must be a function");
}
if (!this.inPorts) {
throw new Error("Component ports must be defined before process function");
}
this.prepareForwarding();
this.handle = handle;
_ref = this.inPorts.ports;
_fn = (function(_this) {
return function(name, port) {
if (!port.name) {
port.name = name;
}
return port.on('ip', function(ip) {
return _this.handleIP(ip, port);
});
};
})(this);
for (name in _ref) {
port = _ref[name];
_fn(name, port);
}
return this;
};
Component.prototype.handleIP = function(ip, port) {
var entry, haveData, i, input, ips, outPort, output, outputEntry, result, _i, _ip, _j, _k, _len, _len1, _ref, _ref1;
if (ip.type === 'openBracket') {
if (!this.autoOrdering) {
this.autoOrdering = true;
}
this.bracketCounter[port.name]++;
}
if (port.name in this.forwardBrackets && (ip.type === 'openBracket' || ip.type === 'closeBracket')) {
outputEntry = {
__resolved: ip.type === 'closeBracket' || !this.dropEmptyBrackets.length,
__forwarded: true,
__type: ip.type,
__scope: ip.scope
};
_ref = this.forwardBrackets[port.name];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
outPort = _ref[_i];
if (!(outPort in outputEntry)) {
outputEntry[outPort] = [];
}
outputEntry[outPort].push(ip);
}
port.buffer.pop();
if (ip.type === 'closeBracket' && this.dropEmptyBrackets.length) {
haveData = [];
for (i = _j = _ref1 = this.outputQ.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; i = _ref1 <= 0 ? ++_j : --_j) {
entry = this.outputQ[i];
if ('__forwarded' in entry) {
if (entry.__type === 'openBracket' && !entry.__resolved && entry.__scope === ip.scope) {
for (port in entry) {
ips = entry[port];
if (haveData.indexOf(port) === -1 && this.dropEmptyBrackets.indexOf(port) !== -1) {
delete entry[port];
delete outputEntry[port];
}
}
entry.__resolved = true;
break;
}
} else {
for (port in entry) {
ips = entry[port];
if (port.indexOf('__') === 0 || haveData.indexOf(port) >= 0) {
continue;
}
for (_k = 0, _len1 = ips.length; _k < _len1; _k++) {
_ip = ips[_k];
if (_ip.scope === ip.scope) {
haveData.push(port);
break;
}
}
}
}
}
}
this.outputQ.push(outputEntry);
this.processOutputQueue();
return;
}
if (!port.options.triggering) {
return;
}
result = {};
input = new ProcessInput(this.inPorts, ip, this, port, result);
output = new ProcessOutput(this.outPorts, ip, this, result);
this.load++;
return this.handle(input, output, function() {
return output.done();
});
};
Component.prototype.processOutputQueue = function() {
var bracketsClosed, ip, ips, name, port, result, _i, _len, _ref;
while (this.outputQ.length > 0) {
result = this.outputQ[0];
if (!result.__resolved) {
break;
}
for (port in result) {
ips = result[port];
if (port.indexOf('__') === 0) {
continue;
}
for (_i = 0, _len = ips.length; _i < _len; _i++) {
ip = ips[_i];
if (ip.type === 'closeBracket') {
this.bracketCounter[port]--;
}
this.outPorts[port].sendIP(ip);
}
}
this.outputQ.shift();
}
bracketsClosed = true;
_ref = this.outPorts.ports;
for (name in _ref) {
port = _ref[name];
if (this.bracketCounter[port] !== 0) {
bracketsClosed = false;
break;
}
}
if (bracketsClosed) {
return this.autoOrdering = false;
}
};
return Component;
})(EventEmitter);
exports.Component = Component;
ProcessInput = (function() {
function ProcessInput(ports, ip, nodeInstance, port, result) {
this.ports = ports;
this.ip = ip;
this.nodeInstance = nodeInstance;
this.port = port;
this.result = result;
this.scope = this.ip.scope;
}
ProcessInput.prototype.activate = function() {
this.result.__resolved = false;
if (this.nodeInstance.ordered || this.nodeInstance.autoOrdering) {
return this.nodeInstance.outputQ.push(this.result);
}
};
ProcessInput.prototype.has = function(port) {
var args, res, _i, _len;
if (port == null) {
port = 'in';
}
args = arguments.length === 0 ? [port] : arguments;
res = true;
for (_i = 0, _len = args.length; _i < _len; _i++) {
port = args[_i];
res && (res = this.ports[port].ready(this.scope));
}
return res;
};
ProcessInput.prototype.get = function(port) {
var args, res;
if (port == null) {
port = 'in';
}
args = arguments.length === 0 ? [port] : arguments;
if ((this.nodeInstance.ordered || this.nodeInstance.autoOrdering) && this.nodeInstance.activateOnInput && !('__resolved' in this.result)) {
this.activate();
}
res = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = args.length; _i < _len; _i++) {
port = args[_i];
_results.push(this.ports[port].get(this.scope));
}
return _results;
}).call(this);
if (args.length === 1) {
return res[0];
} else {
return res;
}
};
ProcessInput.prototype.getData = function(port) {
var args, ip, ips, _i, _len, _ref, _ref1, _results;
if (port == null) {
port = 'in';
}
args = arguments.length === 0 ? [port] : arguments;
ips = this.get.apply(this, args);
if (args.length === 1) {
return (_ref = ips != null ? ips.data : void 0) != null ? _ref : void 0;
}
_results = [];
for (_i = 0, _len = ips.length; _i < _len; _i++) {
ip = ips[_i];
_results.push((_ref1 = ip != null ? ip.data : void 0) != null ? _ref1 : void 0);
}
return _results;
};
return ProcessInput;
})();
ProcessOutput = (function() {
function ProcessOutput(ports, ip, nodeInstance, result) {
this.ports = ports;
this.ip = ip;
this.nodeInstance = nodeInstance;
this.result = result;
this.scope = this.ip.scope;
}
ProcessOutput.prototype.activate = function() {
this.result.__resolved = false;
if (this.nodeInstance.ordered || this.nodeInstance.autoOrdering) {
return this.nodeInstance.outputQ.push(this.result);
}
};
ProcessOutput.prototype.isError = function(err) {
return err instanceof Error || Array.isArray(err) && err.length > 0 && err[0] instanceof Error;
};
ProcessOutput.prototype.error = function(err) {
var e, multiple, _i, _j, _len, _len1, _results;
multiple = Array.isArray(err);
if (!multiple) {
err = [err];
}
if ('error' in this.ports && (this.ports.error.isAttached() || !this.ports.error.isRequired())) {
if (multiple) {
this.sendIP('error', new IP('openBracket'));
}
for (_i = 0, _len = err.length; _i < _len; _i++) {
e = err[_i];
this.sendIP('error', e);
}
if (multiple) {
return this.sendIP('error', new IP('closeBracket'));
}
} else {
_results = [];
for (_j = 0, _len1 = err.length; _j < _len1; _j++) {
e = err[_j];
throw e;
}
return _results;
}
};
ProcessOutput.prototype.sendIP = function(port, packet) {
var ip;
if (typeof packet !== 'object' || IP.types.indexOf(packet.type) === -1) {
ip = new IP('data', packet);
} else {
ip = packet;
}
if (this.scope !== null && ip.scope === null) {
ip.scope = this.scope;
}
if (this.nodeInstance.ordered || this.nodeInstance.autoOrdering) {
if (!(port in this.result)) {
this.result[port] = [];
}
return this.result[port].push(ip);
} else {
return this.nodeInstance.outPorts[port].sendIP(ip);
}
};
ProcessOutput.prototype.send = function(outputMap) {
var packet, port, _results;
if ((this.nodeInstance.ordered || this.nodeInstance.autoOrdering) && !('__resolved' in this.result)) {
this.activate();
}
if (this.isError(outputMap)) {
return this.error(outputMap);
}
_results = [];
for (port in outputMap) {
packet = outputMap[port];
_results.push(this.sendIP(port, packet));
}
return _results;
};
ProcessOutput.prototype.sendDone = function(outputMap) {
this.send(outputMap);
return this.done();
};
ProcessOutput.prototype.pass = function(data, options) {
var key, val;
if (options == null) {
options = {};
}
if (!('out' in this.ports)) {
throw new Error('output.pass() requires port "out" to be present');
}
for (key in options) {
val = options[key];
this.ip[key] = val;
}
this.ip.data = data;
this.sendIP('out', this.ip);
return this.done();
};
ProcessOutput.prototype.done = function(error) {
if (error) {
this.error(error);
}
if (this.nodeInstance.ordered || this.nodeInstance.autoOrdering) {
this.result.__resolved = true;
this.nodeInstance.processOutputQueue();
}
return this.nodeInstance.load--;
};
return ProcessOutput;
})();
}).call(this);