rpd
Version:
RPD is a minimal framework for building Node-Based User Interfaces, powered by Reactive Programming
173 lines (136 loc) • 5.79 kB
JavaScript
;(function(global) {
"use strict";
var Rpd = global.Rpd;
if (typeof Rpd === "undefined" && typeof require !== "undefined") {
Rpd = require('rpd');
}
Rpd.style('webpd', 'svg', (function() {
var socketPadding = 10; // distance between inlets/outlets in SVG units
var nodeHeight = 18;
var connectorWidth = 7;
function _createSvgElement(name) {
return document.createElementNS(d3.namespaces.svg, name);
}
function getPos(elm) { var bounds = elm.getBoundingClientRect();
return { x: bounds.left, y: bounds.top } };
return function(config) {
var lastCanvas;
var nodeToProcessElm = {};
var nodeToInlets = {},
nodeToOutlets = {};
var inletToElm = {},
outletToElm = {};
function redistributeInlets(node) {
var inlets = nodeToInlets[node.id];
var inlet_count = inlets.length;
if (inlet_count <= 1) return;
var bodyWidth = nodeToProcessElm[node.id].node().getBBox().width;
inlets.forEach(function(inlet, idx) {
var xPos = idx * (bodyWidth / (inlet_count - 1))
- idx * (connectorWidth / (inlet_count - 1));
inletToElm[inlet.id].attr('transform', 'translate(' + xPos + ',0)');
});
}
function redistributeOutlets(node) {
var outlets = nodeToOutlets[node.id];
var outlet_count = outlets.length;
if (outlet_count <= 1) return;
var bodyWidth = nodeToProcessElm[node.id].node().getBBox().width;
outlets.forEach(function(outlet, idx) {
var xPos = idx * (bodyWidth / (outlet_count - 1))
- idx * (connectorWidth / (outlet_count - 1));
outletToElm[outlet.id].attr('transform', 'translate(' + xPos + ',0)');
});
}
return {
createCanvas: function(patch, parent) {
return { element: d3.select(_createSvgElement('g'))
.classed('rpd-patch', true).node() };
},
createNode: function(node, render, description, icon) {
var size = render.size ? { width: render.size.width || 50,
height: render.size.height || 18 }
: { width: 18, height: 18 };
var nodeElm = d3.select(_createSvgElement('g'))
.attr('class', 'rpd-node');
nodeElm.classed('rpd-'+node.type.slice(0, node.type.indexOf('/'))+'-toolkit-node', true)
.classed('rpd-'+node.type.replace('/','-'), true);
var isPdNode = (node.type.indexOf('wpd/') === 0);
if (!isPdNode) {
nodeElm.append('rect').classed('rpd-background').classed('rpd-drag-handle', true)
.attr('width', size.width).attr('height', size.height);
}
nodeElm.append('g').classed('rpd-process', true).classed('rpd-drag-handle', isPdNode);
nodeElm.append('g').classed('rpd-inlets', true);
nodeElm.append('g').classed('rpd-outlets', true)
.attr('transform', 'translate(0,' + (nodeHeight - 2) + ')');
nodeToProcessElm[node.id] = nodeElm.select('.rpd-process');
nodeToInlets[node.id] = [];
nodeToOutlets[node.id] = [];
return {
element: nodeElm.node(),
size: size
};
},
createInlet: function(inlet, render) {
var inletElm = d3.select(_createSvgElement('g')).attr('class', 'rpd-inlet');
inletElm.append('g').attr('class', 'rpd-connector')
.append('rect').attr('width', 7).attr('height', 2);
inletToElm[inlet.id] = inletElm;
nodeToInlets[inlet.node.id].push(inlet);
redistributeInlets(inlet.node);
return { element: inletElm.node() };
},
createOutlet: function(outlet, render) {
var outletElm = d3.select(_createSvgElement('g')).attr('class', 'rpd-outlet');
outletElm.append('g').attr('class', 'rpd-connector')
.append('rect').attr('width', 7).attr('height', 2);
outletToElm[outlet.id] = outletElm;
nodeToOutlets[outlet.node.id].push(outlet);
redistributeOutlets(outlet.node);
return { element: outletElm.node() };
},
createLink: function(link) {
var linkElm = d3.select(_createSvgElement('line'))
.attr('class', 'rpd-link');
return { element: linkElm.node(),
rotate: function(x0, y0, x1, y1) {
linkElm.attr('x1', x0).attr('y1', y0)
.attr('x2', x1).attr('y2', y1);
},
noPointerEvents: function() {
linkElm.style('pointer-events', 'none');
} };
},
getInletPos: function(inlet) {
var connectorPos = getPos(inletToElm[inlet.id].select('.rpd-connector').node());
return { x: connectorPos.x + 3, y: connectorPos.y + 1 };
},
getOutletPos: function(outlet) {
var connectorPos = getPos(outletToElm[outlet.id].select('.rpd-connector').node());
return { x: connectorPos.x + 3, y: connectorPos.y + 1 };
},
getLocalPos: function(pos) {
if (!lastCanvas) return pos;
// calculate once on patch switch?
var canvasPos = getPos(lastCanvas.node());
return { x: pos.x - canvasPos.x, y: pos.y - canvasPos.y };
},
onPatchSwitch: function(patch, canvas) {
lastCanvas = d3.select(canvas);
},
onInletRemove: function(inlet) {
var nodeId = inlet.node.id;
nodeToInlets[nodeId] = nodeToInlets[nodeId].filter(function(other) {
return other.id !== inlet.id;
});
},
onOutletRemove: function(outlet) {
var nodeId = outlet.node.id;
nodeToOutlets[nodeId] = nodeToOutlets[nodeId].filter(function(other) {
return other.id !== outlet.id;
});
}
};
} })());
})(this);