UNPKG

rpd

Version:

RPD is a minimal framework for building Node-Based User Interfaces, powered by Reactive Programming

119 lines (91 loc) 4.45 kB
;(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));