noflo
Version:
Flow-Based Programming environment for JavaScript
626 lines (584 loc) • 21.2 kB
JavaScript
(function() {
var EventEmitter, Journal, JournalStore, MemoryJournalStore, calculateMeta, clone, entryToPrettyString,
__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; },
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
EventEmitter = require('events').EventEmitter;
clone = require('./Utils').clone;
entryToPrettyString = function(entry) {
var a;
a = entry.args;
switch (entry.cmd) {
case 'addNode':
return "" + a.id + "(" + a.component + ")";
case 'removeNode':
return "DEL " + a.id + "(" + a.component + ")";
case 'renameNode':
return "RENAME " + a.oldId + " " + a.newId;
case 'changeNode':
return "META " + a.id;
case 'addEdge':
return "" + a.from.node + " " + a.from.port + " -> " + a.to.port + " " + a.to.node;
case 'removeEdge':
return "" + a.from.node + " " + a.from.port + " -X> " + a.to.port + " " + a.to.node;
case 'changeEdge':
return "META " + a.from.node + " " + a.from.port + " -> " + a.to.port + " " + a.to.node;
case 'addInitial':
return "'" + a.from.data + "' -> " + a.to.port + " " + a.to.node;
case 'removeInitial':
return "'" + a.from.data + "' -X> " + a.to.port + " " + a.to.node;
case 'startTransaction':
return ">>> " + entry.rev + ": " + a.id;
case 'endTransaction':
return "<<< " + entry.rev + ": " + a.id;
case 'changeProperties':
return "PROPERTIES";
case 'addGroup':
return "GROUP " + a.name;
case 'renameGroup':
return "RENAME GROUP " + a.oldName + " " + a.newName;
case 'removeGroup':
return "DEL GROUP " + a.name;
case 'changeGroup':
return "META GROUP " + a.name;
case 'addInport':
return "INPORT " + a.name;
case 'removeInport':
return "DEL INPORT " + a.name;
case 'renameInport':
return "RENAME INPORT " + a.oldId + " " + a.newId;
case 'changeInport':
return "META INPORT " + a.name;
case 'addOutport':
return "OUTPORT " + a.name;
case 'removeOutport':
return "DEL OUTPORT " + a.name;
case 'renameOutport':
return "RENAME OUTPORT " + a.oldId + " " + a.newId;
case 'changeOutport':
return "META OUTPORT " + a.name;
default:
throw new Error("Unknown journal entry: " + entry.cmd);
}
};
calculateMeta = function(oldMeta, newMeta) {
var k, setMeta, v;
setMeta = {};
for (k in oldMeta) {
v = oldMeta[k];
setMeta[k] = null;
}
for (k in newMeta) {
v = newMeta[k];
setMeta[k] = v;
}
return setMeta;
};
JournalStore = (function(_super) {
__extends(JournalStore, _super);
JournalStore.prototype.lastRevision = 0;
function JournalStore(graph) {
this.graph = graph;
this.lastRevision = 0;
}
JournalStore.prototype.putTransaction = function(revId, entries) {
if (revId > this.lastRevision) {
this.lastRevision = revId;
}
return this.emit('transaction', revId);
};
JournalStore.prototype.fetchTransaction = function(revId, entries) {};
return JournalStore;
})(EventEmitter);
MemoryJournalStore = (function(_super) {
__extends(MemoryJournalStore, _super);
function MemoryJournalStore(graph) {
MemoryJournalStore.__super__.constructor.call(this, graph);
this.transactions = [];
}
MemoryJournalStore.prototype.putTransaction = function(revId, entries) {
MemoryJournalStore.__super__.putTransaction.call(this, revId, entries);
return this.transactions[revId] = entries;
};
MemoryJournalStore.prototype.fetchTransaction = function(revId) {
return this.transactions[revId];
};
return MemoryJournalStore;
})(JournalStore);
Journal = (function(_super) {
__extends(Journal, _super);
Journal.prototype.graph = null;
Journal.prototype.entries = [];
Journal.prototype.subscribed = true;
function Journal(graph, metadata, store) {
this.endTransaction = __bind(this.endTransaction, this);
this.startTransaction = __bind(this.startTransaction, this);
var edge, group, iip, k, node, v, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _ref4, _ref5;
this.graph = graph;
this.entries = [];
this.subscribed = true;
this.store = store || new MemoryJournalStore(this.graph);
if (this.store.transactions.length === 0) {
this.currentRevision = -1;
this.startTransaction('initial', metadata);
_ref = this.graph.nodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
this.appendCommand('addNode', node);
}
_ref1 = this.graph.edges;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
edge = _ref1[_j];
this.appendCommand('addEdge', edge);
}
_ref2 = this.graph.initializers;
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
iip = _ref2[_k];
this.appendCommand('addInitial', iip);
}
if (Object.keys(this.graph.properties).length > 0) {
this.appendCommand('changeProperties', this.graph.properties, {});
}
_ref3 = this.graph.inports;
for (k in _ref3) {
v = _ref3[k];
this.appendCommand('addInport', {
name: k,
port: v
});
}
_ref4 = this.graph.outports;
for (k in _ref4) {
v = _ref4[k];
this.appendCommand('addOutport', {
name: k,
port: v
});
}
_ref5 = this.graph.groups;
for (_l = 0, _len3 = _ref5.length; _l < _len3; _l++) {
group = _ref5[_l];
this.appendCommand('addGroup', group);
}
this.endTransaction('initial', metadata);
} else {
this.currentRevision = this.store.lastRevision;
}
this.graph.on('addNode', (function(_this) {
return function(node) {
return _this.appendCommand('addNode', node);
};
})(this));
this.graph.on('removeNode', (function(_this) {
return function(node) {
return _this.appendCommand('removeNode', node);
};
})(this));
this.graph.on('renameNode', (function(_this) {
return function(oldId, newId) {
var args;
args = {
oldId: oldId,
newId: newId
};
return _this.appendCommand('renameNode', args);
};
})(this));
this.graph.on('changeNode', (function(_this) {
return function(node, oldMeta) {
return _this.appendCommand('changeNode', {
id: node.id,
"new": node.metadata,
old: oldMeta
});
};
})(this));
this.graph.on('addEdge', (function(_this) {
return function(edge) {
return _this.appendCommand('addEdge', edge);
};
})(this));
this.graph.on('removeEdge', (function(_this) {
return function(edge) {
return _this.appendCommand('removeEdge', edge);
};
})(this));
this.graph.on('changeEdge', (function(_this) {
return function(edge, oldMeta) {
return _this.appendCommand('changeEdge', {
from: edge.from,
to: edge.to,
"new": edge.metadata,
old: oldMeta
});
};
})(this));
this.graph.on('addInitial', (function(_this) {
return function(iip) {
return _this.appendCommand('addInitial', iip);
};
})(this));
this.graph.on('removeInitial', (function(_this) {
return function(iip) {
return _this.appendCommand('removeInitial', iip);
};
})(this));
this.graph.on('changeProperties', (function(_this) {
return function(newProps, oldProps) {
return _this.appendCommand('changeProperties', {
"new": newProps,
old: oldProps
});
};
})(this));
this.graph.on('addGroup', (function(_this) {
return function(group) {
return _this.appendCommand('addGroup', group);
};
})(this));
this.graph.on('renameGroup', (function(_this) {
return function(oldName, newName) {
return _this.appendCommand('renameGroup', {
oldName: oldName,
newName: newName
});
};
})(this));
this.graph.on('removeGroup', (function(_this) {
return function(group) {
return _this.appendCommand('removeGroup', group);
};
})(this));
this.graph.on('changeGroup', (function(_this) {
return function(group, oldMeta) {
return _this.appendCommand('changeGroup', {
name: group.name,
"new": group.metadata,
old: oldMeta
});
};
})(this));
this.graph.on('addExport', (function(_this) {
return function(exported) {
return _this.appendCommand('addExport', exported);
};
})(this));
this.graph.on('removeExport', (function(_this) {
return function(exported) {
return _this.appendCommand('removeExport', exported);
};
})(this));
this.graph.on('addInport', (function(_this) {
return function(name, port) {
return _this.appendCommand('addInport', {
name: name,
port: port
});
};
})(this));
this.graph.on('removeInport', (function(_this) {
return function(name, port) {
return _this.appendCommand('removeInport', {
name: name,
port: port
});
};
})(this));
this.graph.on('renameInport', (function(_this) {
return function(oldId, newId) {
return _this.appendCommand('renameInport', {
oldId: oldId,
newId: newId
});
};
})(this));
this.graph.on('changeInport', (function(_this) {
return function(name, port, oldMeta) {
return _this.appendCommand('changeInport', {
name: name,
"new": port.metadata,
old: oldMeta
});
};
})(this));
this.graph.on('addOutport', (function(_this) {
return function(name, port) {
return _this.appendCommand('addOutport', {
name: name,
port: port
});
};
})(this));
this.graph.on('removeOutport', (function(_this) {
return function(name, port) {
return _this.appendCommand('removeOutport', {
name: name,
port: port
});
};
})(this));
this.graph.on('renameOutport', (function(_this) {
return function(oldId, newId) {
return _this.appendCommand('renameOutport', {
oldId: oldId,
newId: newId
});
};
})(this));
this.graph.on('changeOutport', (function(_this) {
return function(name, port, oldMeta) {
return _this.appendCommand('changeOutport', {
name: name,
"new": port.metadata,
old: oldMeta
});
};
})(this));
this.graph.on('startTransaction', (function(_this) {
return function(id, meta) {
return _this.startTransaction(id, meta);
};
})(this));
this.graph.on('endTransaction', (function(_this) {
return function(id, meta) {
return _this.endTransaction(id, meta);
};
})(this));
}
Journal.prototype.startTransaction = function(id, meta) {
if (!this.subscribed) {
return;
}
if (this.entries.length > 0) {
throw Error("Inconsistent @entries");
}
this.currentRevision++;
return this.appendCommand('startTransaction', {
id: id,
metadata: meta
}, this.currentRevision);
};
Journal.prototype.endTransaction = function(id, meta) {
if (!this.subscribed) {
return;
}
this.appendCommand('endTransaction', {
id: id,
metadata: meta
}, this.currentRevision);
this.store.putTransaction(this.currentRevision, this.entries);
return this.entries = [];
};
Journal.prototype.appendCommand = function(cmd, args, rev) {
var entry;
if (!this.subscribed) {
return;
}
entry = {
cmd: cmd,
args: clone(args)
};
if (rev != null) {
entry.rev = rev;
}
return this.entries.push(entry);
};
Journal.prototype.executeEntry = function(entry) {
var a;
a = entry.args;
switch (entry.cmd) {
case 'addNode':
return this.graph.addNode(a.id, a.component);
case 'removeNode':
return this.graph.removeNode(a.id);
case 'renameNode':
return this.graph.renameNode(a.oldId, a.newId);
case 'changeNode':
return this.graph.setNodeMetadata(a.id, calculateMeta(a.old, a["new"]));
case 'addEdge':
return this.graph.addEdge(a.from.node, a.from.port, a.to.node, a.to.port);
case 'removeEdge':
return this.graph.removeEdge(a.from.node, a.from.port, a.to.node, a.to.port);
case 'changeEdge':
return this.graph.setEdgeMetadata(a.from.node, a.from.port, a.to.node, a.to.port, calculateMeta(a.old, a["new"]));
case 'addInitial':
return this.graph.addInitial(a.from.data, a.to.node, a.to.port);
case 'removeInitial':
return this.graph.removeInitial(a.to.node, a.to.port);
case 'startTransaction':
return null;
case 'endTransaction':
return null;
case 'changeProperties':
return this.graph.setProperties(a["new"]);
case 'addGroup':
return this.graph.addGroup(a.name, a.nodes, a.metadata);
case 'renameGroup':
return this.graph.renameGroup(a.oldName, a.newName);
case 'removeGroup':
return this.graph.removeGroup(a.name);
case 'changeGroup':
return this.graph.setGroupMetadata(a.name, calculateMeta(a.old, a["new"]));
case 'addInport':
return this.graph.addInport(a.name, a.port.process, a.port.port, a.port.metadata);
case 'removeInport':
return this.graph.removeInport(a.name);
case 'renameInport':
return this.graph.renameInport(a.oldId, a.newId);
case 'changeInport':
return this.graph.setInportMetadata(a.name, calculateMeta(a.old, a["new"]));
case 'addOutport':
return this.graph.addOutport(a.name, a.port.process, a.port.port, a.port.metadata(a.name));
case 'removeOutport':
return this.graph.removeOutport;
case 'renameOutport':
return this.graph.renameOutport(a.oldId, a.newId);
case 'changeOutport':
return this.graph.setOutportMetadata(a.name, calculateMeta(a.old, a["new"]));
default:
throw new Error("Unknown journal entry: " + entry.cmd);
}
};
Journal.prototype.executeEntryInversed = function(entry) {
var a;
a = entry.args;
switch (entry.cmd) {
case 'addNode':
return this.graph.removeNode(a.id);
case 'removeNode':
return this.graph.addNode(a.id, a.component);
case 'renameNode':
return this.graph.renameNode(a.newId, a.oldId);
case 'changeNode':
return this.graph.setNodeMetadata(a.id, calculateMeta(a["new"], a.old));
case 'addEdge':
return this.graph.removeEdge(a.from.node, a.from.port, a.to.node, a.to.port);
case 'removeEdge':
return this.graph.addEdge(a.from.node, a.from.port, a.to.node, a.to.port);
case 'changeEdge':
return this.graph.setEdgeMetadata(a.from.node, a.from.port, a.to.node, a.to.port, calculateMeta(a["new"], a.old));
case 'addInitial':
return this.graph.removeInitial(a.to.node, a.to.port);
case 'removeInitial':
return this.graph.addInitial(a.from.data, a.to.node, a.to.port);
case 'startTransaction':
return null;
case 'endTransaction':
return null;
case 'changeProperties':
return this.graph.setProperties(a.old);
case 'addGroup':
return this.graph.removeGroup(a.name);
case 'renameGroup':
return this.graph.renameGroup(a.newName, a.oldName);
case 'removeGroup':
return this.graph.addGroup(a.name, a.nodes, a.metadata);
case 'changeGroup':
return this.graph.setGroupMetadata(a.name, calculateMeta(a["new"], a.old));
case 'addInport':
return this.graph.removeInport(a.name);
case 'removeInport':
return this.graph.addInport(a.name, a.port.process, a.port.port, a.port.metadata);
case 'renameInport':
return this.graph.renameInport(a.newId, a.oldId);
case 'changeInport':
return this.graph.setInportMetadata(a.name, calculateMeta(a["new"], a.old));
case 'addOutport':
return this.graph.removeOutport(a.name);
case 'removeOutport':
return this.graph.addOutport(a.name, a.port.process, a.port.port, a.port.metadata);
case 'renameOutport':
return this.graph.renameOutport(a.newId, a.oldId);
case 'changeOutport':
return this.graph.setOutportMetadata(a.name, calculateMeta(a["new"], a.old));
default:
throw new Error("Unknown journal entry: " + entry.cmd);
}
};
Journal.prototype.moveToRevision = function(revId) {
var entries, entry, i, r, _i, _j, _k, _l, _len, _ref, _ref1, _ref2, _ref3, _ref4;
if (revId === this.currentRevision) {
return;
}
this.subscribed = false;
if (revId > this.currentRevision) {
for (r = _i = _ref = this.currentRevision + 1; _ref <= revId ? _i <= revId : _i >= revId; r = _ref <= revId ? ++_i : --_i) {
_ref1 = this.store.fetchTransaction(r);
for (_j = 0, _len = _ref1.length; _j < _len; _j++) {
entry = _ref1[_j];
this.executeEntry(entry);
}
}
} else {
for (r = _k = _ref2 = this.currentRevision, _ref3 = revId + 1; _k >= _ref3; r = _k += -1) {
entries = this.store.fetchTransaction(r);
for (i = _l = _ref4 = entries.length - 1; _l >= 0; i = _l += -1) {
this.executeEntryInversed(entries[i]);
}
}
}
this.currentRevision = revId;
return this.subscribed = true;
};
Journal.prototype.undo = function() {
if (!this.canUndo()) {
return;
}
return this.moveToRevision(this.currentRevision - 1);
};
Journal.prototype.canUndo = function() {
return this.currentRevision > 0;
};
Journal.prototype.redo = function() {
if (!this.canRedo()) {
return;
}
return this.moveToRevision(this.currentRevision + 1);
};
Journal.prototype.canRedo = function() {
return this.currentRevision < this.store.lastRevision;
};
Journal.prototype.toPrettyString = function(startRev, endRev) {
var e, entry, lines, r, _i, _j, _len;
startRev |= 0;
endRev |= this.store.lastRevision;
lines = [];
for (r = _i = startRev; startRev <= endRev ? _i < endRev : _i > endRev; r = startRev <= endRev ? ++_i : --_i) {
e = this.store.fetchTransaction(r);
for (_j = 0, _len = e.length; _j < _len; _j++) {
entry = e[_j];
lines.push(entryToPrettyString(entry));
}
}
return lines.join('\n');
};
Journal.prototype.toJSON = function(startRev, endRev) {
var entries, entry, r, _i, _j, _len, _ref;
startRev |= 0;
endRev |= this.store.lastRevision;
entries = [];
for (r = _i = startRev; _i < endRev; r = _i += 1) {
_ref = this.store.fetchTransaction(r);
for (_j = 0, _len = _ref.length; _j < _len; _j++) {
entry = _ref[_j];
entries.push(entryToPrettyString(entry));
}
}
return entries;
};
Journal.prototype.save = function(file, success) {
var json;
json = JSON.stringify(this.toJSON(), null, 4);
return require('fs').writeFile("" + file + ".json", json, "utf-8", function(err, data) {
if (err) {
throw err;
}
return success(file);
});
};
return Journal;
})(EventEmitter);
exports.Journal = Journal;
exports.JournalStore = JournalStore;
exports.MemoryJournalStore = MemoryJournalStore;
}).call(this);