rpd
Version:
RPD is a minimal framework for building Node-Based User Interfaces, powered by Reactive Programming
119 lines (91 loc) • 4.45 kB
JavaScript
;(function(global) {
"use strict";
var Rpd = global.Rpd;
if (typeof Rpd === "undefined" && typeof require !== "undefined") {
Rpd = require('rpd');
}
// ================================= EXPORT =================================
Rpd.export.pd = function(name) {
var spec = exportSpec;
var commands = [];
var lines = [];
var moves = {};
Rpd.events.onValue(function() {
});
return function() {
return lines.join('\r\n');
};
}
// ================================= IMPORT =================================
Rpd.import.pd = function(lines) {
var WebPd = global.Pd || null;
if (!WebPd) throw new Error('WebPd is required to import PD files');
var patchData = WebPd.parsePatch(lines);
var webPdPatch = WebPd.loadPatch(patchData);
var nodes = [];
var rootPatch = Rpd.addPatch('PD');
var model = new PdModel(rootPatch, webPdPatch); // it is wrong to do it here as well
// since PdModel is defined for toolkit,
// not the import
rootPatch.model = model;
var nodeToInlets = {},
nodeToOutlets = {},
nodeToProto = {};
function pushInlet(update) {
if (update.inlet.def.hidden) return;
if (!nodeToInlets[update.node.id]) nodeToInlets[update.node.id] = [];
nodeToInlets[update.node.id].push(update.inlet);
}
function pushOutlet(update) {
if (!nodeToOutlets[update.node.id]) nodeToOutlets[update.node.id] = [];
nodeToOutlets[update.node.id].push(update.outlet);
}
function popInlet(update) { nodeToInlets[update.node.id].pop(); }
function popOutlet(update) { nodeToOutlets[update.node.id].pop(); }
function eventIs(type) { return function(event) { return event.type === type; } };
var addInletStream = rootPatch.events.filter(eventIs('node/add-inlet'))
.filter(function(event) { return !event.inlet.hidden; });
var addOutletStream = rootPatch.events.filter(eventIs('node/add-outlet'));
var removeInletStream = rootPatch.events.filter(eventIs('node/remove-inlet'));
var removeOutletStream = rootPatch.events.filter(eventIs('node/remove-outlet'));
addInletStream.onValue(pushInlet); addOutletStream.onValue(pushOutlet);
removeInletStream.onValue(popInlet); removeOutletStream.onValue(popOutlet);
var cmdToType = PdModel.COMMAND_TO_TYPE;
patchData.nodes.forEach(function(pdNode, idx) {
var proto = pdNode.proto,
nodeType = cmdToType[proto];
var node = rootPatch.addNode(nodeType || 'wpd/object');
nodeToProto[node.id] = proto;
node.move(pdNode.layout.x, pdNode.layout.y);
// node.webPdObject = webPdPatch.objects[idx];
model.markResolvedAndApply(node, proto, pdNode.args, webPdPatch.objects[idx]);
nodes.push(node);
});
patchData.connections.forEach(function(connection) {
var fromNodeIdx = connection.source.id,
toNodeIdx = connection.sink.id;
var outletIdx = connection.source.port,
inletIdx = connection.sink.port;
var fromNode = nodes[fromNodeIdx],
toNode = nodes[toNodeIdx];
if (nodeToOutlets[fromNode.id] && nodeToInlets[toNode.id]) {
var outlet = nodeToOutlets[fromNode.id][outletIdx],
inlet = nodeToInlets[toNode.id][inletIdx];
if (!inlet.allows(outlet)) {
// FIXME: actually, a dirty hack
console.warn('Different types of channels were encountered while connecting',
'from', nodeToProto[fromNode.id], 'outlet', outletIdx, outlet.type,
'to', nodeToProto[toNode.id], 'inlet', inletIdx, inlet.type,
', rewriting the outlet type');
outlet.type = inlet.type;
}
if (inlet && outlet) { outlet.connect(inlet); }
else { console.error('Failed to connect object ' + fromNodeIdx + ' to object ' + toNodeIdx); };
} else { console.error('Failed to connect object ' + fromNodeIdx + ' to object ' + toNodeIdx); };
});
addInletStream.offValue(pushInlet); addOutletStream.offValue(pushOutlet);
removeInletStream.offValue(popInlet); removeOutletStream.offValue(popOutlet);
model.listenForNewNodes();
return rootPatch;
}
}(this));