diff --git a/.gitignore b/.gitignore index 72a38cea..43956891 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ bower_components/ node_modules/ /.idea /build +/dist npm-debug.log /spec/*.js diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..48dabdee --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +/build +/bower_components +*.tgz diff --git a/CHANGES.md b/CHANGES.md index 062a71e4..499a16db 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,14 @@ ## dev +## 0.7.0 (unreleased) + +Breaking changes + +* Polymer elements no longer automatically include the neccesary JS files. +Instead users must include `dist/the-graph.js`, which bundles the needed JavaScript and provides API under `window.TheGraph`. +The file is included in `the-graph` NPM packages. +This is preparation for removing the Polymer dependency, instead providing JS APIs and React components. + ## 0.6.0 (2017 January 5) * Add all dependencies besides Polymer to NPM. diff --git a/Gruntfile.js b/Gruntfile.js index d6595c0d..5977db51 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -7,7 +7,7 @@ "* Copyright (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>; Licensed <%= _.pluck(pkg.licenses, 'type').join(', ') %> */\n"; var sources = { - scripts: ['Gruntfile.js', 'the-*/*.js', 'the-*/*.html'], + scripts: ['Gruntfile.js', 'the-*/*.js', 'the-*/*.html', 'index.js'], // elements: ['the-*/*.html'], stylus: ['themes/*/*.styl'], css: ['themes/*.css'], @@ -51,20 +51,20 @@ browserify: { libs: { files: { - 'build/the-graph.js': ['index.js'], + 'dist/the-graph.js': ['index.js'], }, options: { - transform: ['coffeeify'] - }, - browserifyOptions: { - require: 'noflo' + transform: ['coffeeify'], + browserifyOptions: { + standalone: 'TheGraph' + } } } }, jshint: { options: { extract: 'auto', - strict: true, + strict: false, newcap: false, "globals": { "Polymer": true } }, @@ -88,7 +88,7 @@ watch: { scripts: { files: sources.scripts, - tasks: ['jshint:force'], + tasks: ['jshint:force', 'browserify:libs'], options: { livereload: true } diff --git a/examples/demo-full.html b/examples/demo-full.html index 82d560df..83566861 100644 --- a/examples/demo-full.html +++ b/examples/demo-full.html @@ -14,7 +14,7 @@ - + diff --git a/examples/demo-simple.html b/examples/demo-simple.html index 22c20644..26aeff19 100644 --- a/examples/demo-simple.html +++ b/examples/demo-simple.html @@ -14,7 +14,7 @@ - + diff --git a/examples/demo-thumbnail.html b/examples/demo-thumbnail.html index 0d8d5541..c7eb36c2 100644 --- a/examples/demo-thumbnail.html +++ b/examples/demo-thumbnail.html @@ -4,9 +4,11 @@ the-graph-thumb documentation - + - + + + diff --git a/index.js b/index.js index 78ff79c3..162b4b7e 100644 --- a/index.js +++ b/index.js @@ -1,2 +1,28 @@ // Build required libs fbpGraph = require('fbp-graph'); + +var g = { TheGraph: {} }; + +require("./the-graph/the-graph.js").register(g); +require("./the-graph/the-graph-app.js").register(g); +require("./the-graph/the-graph-graph.js").register(g); +require("./the-graph/the-graph-node.js").register(g); +require("./the-graph/the-graph-node-menu.js").register(g); +require("./the-graph/the-graph-node-menu-port.js").register(g); +require("./the-graph/the-graph-node-menu-ports.js").register(g); +require("./the-graph/the-graph-port.js").register(g); +require("./the-graph/the-graph-edge.js").register(g); +require("./the-graph/the-graph-iip.js").register(g); +require("./the-graph/the-graph-group.js").register(g); +require("./the-graph/the-graph-tooltip.js").register(g); +require("./the-graph/the-graph-menu.js").register(g); +require("./the-graph/the-graph-clipboard.js").register(g); +require("./the-graph/font-awesome-unicode-map.js").register(g); + +g.TheGraph.thumb = require('./the-graph-thumb/the-graph-thumb.js'); +g.TheGraph.nav = require('./the-graph-nav/the-graph-nav.js'); +g.TheGraph.autolayout = require('./the-graph/the-graph-autolayout.js'); +g.TheGraph.library = require('./the-graph/the-graph-library.js'); +g.TheGraph.editor = require('./the-graph-editor/the-graph-editor.js'); + +module.exports = g.TheGraph; diff --git a/package.json b/package.json index be66975d..fd596ad0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "flow-based programming graph editing", "author": "Forrest Oliphant, the Grid", "license": "MIT", - "main": "the-graph-editor/index.html", + "main": "index.js", "dependencies": { "fbp-graph": "^0.1.0", "font-awesome": "^4.6.3", diff --git a/scripts/build-font-awesome-javascript.js b/scripts/build-font-awesome-javascript.js index f2157477..da5906d4 100644 --- a/scripts/build-font-awesome-javascript.js +++ b/scripts/build-font-awesome-javascript.js @@ -26,10 +26,10 @@ var generateFile = function (err, data) { }); var output = "/*\n this file is generated via `grunt build` \n*/\n\n"+ - "(function (context) {\n"+ - "\"use strict\";\n\n"+ + "module.exports.register = function (context) {\n"+ + "\n"+ "context.TheGraph.FONT_AWESOME = "+JSON.stringify(icons, null, 2)+";\n\n"+ - "})(this);"; + "};"; fs.writeFile(__dirname+'/../the-graph/font-awesome-unicode-map.js', output, function (err) { if (err) { @@ -39,4 +39,4 @@ var generateFile = function (err, data) { }); }; -fs.readFile( __dirname+'/../node_modules/font-awesome/less/variables.less', 'utf8', generateFile ); +fs.readFile( __dirname+'/../node_modules/font-awesome/less/variables.less', 'utf8', generateFile); diff --git a/spec/runner.html b/spec/runner.html index c1165210..92a935f9 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -15,7 +15,7 @@ - + diff --git a/the-graph-editor/the-graph-editor.html b/the-graph-editor/the-graph-editor.html index 64b71365..2e61ff22 100644 --- a/the-graph-editor/the-graph-editor.html +++ b/the-graph-editor/the-graph-editor.html @@ -44,169 +44,7 @@ forceSelection: false, created: function () { this.pan = [0,0]; - var pasteAction = function (graph, itemKey, item) { - var pasted = TheGraph.Clipboard.paste(graph); - this.selectedNodes = pasted.nodes; - this.selectedEdges = []; - }.bind(this); - var pasteMenu = { - icon: "paste", - iconLabel: "paste", - action: pasteAction - }; - // Default context menu defs - - var nodeActions = { - delete: function (graph, itemKey, item) { - graph.removeNode(itemKey); - // Remove selection - var newSelection = []; - for (var i=0, len=this.selectedNodes.length; ithis.width-x && h>this.height-y) { - // Hide map - this.hide = true; - return; - } else { - // Show map - this.hide = false; - } - - // Clip to bounds - // Left - if (x < 0) { - w += x; - x = 0; - this.$.viewrect.style.borderLeftColor = this.viewBoxBorder2; - } else { - this.$.viewrect.style.borderLeftColor = this.viewBoxBorder; - context.fillRect(0, 0, x, this.height); - } - // Top - if (y < 0) { - h += y; - y = 0; - this.$.viewrect.style.borderTopColor = this.viewBoxBorder2; - } else { - this.$.viewrect.style.borderTopColor = this.viewBoxBorder; - context.fillRect(x, 0, w, y); - } - // Right - if (w > this.width-x) { - w = this.width-x; - this.$.viewrect.style.borderRightColor = this.viewBoxBorder2; - } else { - this.$.viewrect.style.borderRightColor = this.viewBoxBorder; - context.fillRect(x+w, 0, this.width-(x+w), this.height); - } - // Bottom - if (h > this.height-y) { - h = this.height-y; - this.$.viewrect.style.borderBottomColor = this.viewBoxBorder2; - } else { - this.$.viewrect.style.borderBottomColor = this.viewBoxBorder; - context.fillRect(x, y+h, w, this.height-(y+h)); - } - // Size and translate rect - this.$.viewrect.style.left = x+"px"; - this.$.viewrect.style.top = y+"px"; - this.$.viewrect.style.width = w+"px"; - this.$.viewrect.style.height = h+"px"; + var properties = { + width: this.width, + height: this.height, + scale: this.scale, + thumbscale: this.thumbscale, + thumbrectangle: this.thumbrectangle, + viewrectangle: this.viewrectangle, + viewBoxBorder: this.viewBoxBorder, + viewBoxBorder2: this.viewBoxBorder2, + outsideFill: this.outsideFill, + }; + var nav = TheGraph.nav.render(context, this.$.viewrect, properties); + this.hide = nav.hide; // this.scaledviewrectangle = [x, y, w, h]; }, hideChanged: function () { diff --git a/the-graph-nav/the-graph-nav.js b/the-graph-nav/the-graph-nav.js new file mode 100644 index 00000000..7c3e0a25 --- /dev/null +++ b/the-graph-nav/the-graph-nav.js @@ -0,0 +1,92 @@ + +function calculateStyleFromTheme(theme) { + var style = {}; + if (theme === "dark") { + style.viewBoxBorder = "hsla(190, 100%, 80%, 0.4)"; + style.viewBoxBorder2 = "hsla( 10, 60%, 32%, 0.3)"; + style.outsideFill = "hsla(0, 0%, 0%, 0.4)"; + style.backgroundColor = "hsla(0, 0%, 0%, 0.9)"; + } else { + style.viewBoxBorder = "hsla(190, 100%, 20%, 0.8)"; + style.viewBoxBorder2 = "hsla( 10, 60%, 80%, 0.8)"; + style.outsideFill = "hsla(0, 0%, 100%, 0.4)"; + style.backgroundColor = "hsla(0, 0%, 100%, 0.9)"; + } + return style; +} + +function renderViewRectangle(context, viewrect, props) { + + context.clearRect(0, 0, props.width, props.height); + context.fillStyle = props.outsideFill; + + // Scaled view rectangle + var x = Math.round( (props.viewrectangle[0]/props.scale - props.thumbrectangle[0]) * props.thumbscale ); + var y = Math.round( (props.viewrectangle[1]/props.scale - props.thumbrectangle[1]) * props.thumbscale ); + var w = Math.round( props.viewrectangle[2] * props.thumbscale / props.scale ); + var h = Math.round( props.viewrectangle[3] * props.thumbscale / props.scale ); + + var hide = false; + if (x<0 && y<0 && w>props.width-x && h>props.height-y) { + // Hide map + hide = true; + return { + hide: hide + }; + } else { + // Show map + hide = false; + } + + // Clip to bounds + // Left + if (x < 0) { + w += x; + x = 0; + viewrect.style.borderLeftColor = props.viewBoxBorder2; + } else { + viewrect.style.borderLeftColor = props.viewBoxBorder; + context.fillRect(0, 0, x, props.height); + } + // Top + if (y < 0) { + h += y; + y = 0; + viewrect.style.borderTopColor = props.viewBoxBorder2; + } else { + viewrect.style.borderTopColor = props.viewBoxBorder; + context.fillRect(x, 0, w, y); + } + // Right + if (w > props.width-x) { + w = props.width-x; + viewrect.style.borderRightColor = props.viewBoxBorder2; + } else { + viewrect.style.borderRightColor = props.viewBoxBorder; + context.fillRect(x+w, 0, props.width-(x+w), props.height); + } + // Bottom + if (h > props.height-y) { + h = props.height-y; + viewrect.style.borderBottomColor = props.viewBoxBorder2; + } else { + viewrect.style.borderBottomColor = props.viewBoxBorder; + context.fillRect(x, y+h, w, props.height-(y+h)); + } + + // Size and translate rect + viewrect.style.left = x+"px"; + viewrect.style.top = y+"px"; + viewrect.style.width = w+"px"; + viewrect.style.height = h+"px"; + + return { + hide: hide + }; + +} + +module.exports = { + render: renderViewRectangle, + calculateStyleFromTheme: calculateStyleFromTheme, +}; diff --git a/the-graph-thumb/the-graph-thumb.html b/the-graph-thumb/the-graph-thumb.html index c02a967e..3623a2bd 100644 --- a/the-graph-thumb/the-graph-thumb.html +++ b/the-graph-thumb/the-graph-thumb.html @@ -46,64 +46,13 @@ this.themeChanged(); }, themeChanged: function () { - if (this.theme === "dark") { - this.fillStyle = "hsl(184, 8%, 10%)"; - this.strokeStyle = "hsl(180, 11%, 70%)"; - this.edgeColors = [ - "white", - "hsl( 0, 100%, 46%)", - "hsl( 35, 100%, 46%)", - "hsl( 60, 100%, 46%)", - "hsl(135, 100%, 46%)", - "hsl(160, 100%, 46%)", - "hsl(185, 100%, 46%)", - "hsl(210, 100%, 46%)", - "hsl(285, 100%, 46%)", - "hsl(310, 100%, 46%)", - "hsl(335, 100%, 46%)" - ]; - - } else { - // Light - this.fillStyle = "hsl(184, 8%, 75%)"; - this.strokeStyle = "hsl(180, 11%, 20%)"; - // Tweaked to make thin lines more visible - this.edgeColors = [ - "hsl( 0, 0%, 50%)", - "hsl( 0, 100%, 40%)", - "hsl( 29, 100%, 40%)", - "hsl( 47, 100%, 40%)", - "hsl(138, 100%, 40%)", - "hsl(160, 73%, 50%)", - "hsl(181, 100%, 40%)", - "hsl(216, 100%, 40%)", - "hsl(260, 100%, 40%)", - "hsl(348, 100%, 50%)", - "hsl(328, 100%, 40%)" - ]; - } + var style = TheGraph.thumb.styleFromTheme(this.theme); + this.edgeColors = style.edgeColors; + this.fillStyle = style.fill; + this.strokeStyle = style.stroke; // Redraw this.redrawGraph(); }, - drawEdge: function (context, scale, source, target, route) { - // Draw path - try { - context.strokeStyle = this.edgeColors[0]; - if (route) { - // Color if route defined - context.strokeStyle = this.edgeColors[route]; - } - var fromX = Math.round(source.metadata.x*scale)-0.5; - var fromY = Math.round(source.metadata.y*scale)-0.5; - var toX = Math.round(target.metadata.x*scale)-0.5; - var toY = Math.round(target.metadata.y*scale)-0.5; - context.beginPath(); - context.moveTo(fromX, fromY); - context.lineTo(toX, toY); - context.stroke(); - } catch (error) { - } - }, redrawGraph: function () { if (!this.graph) { return; @@ -117,149 +66,19 @@ // Need the actual context, not polymer-wrapped one context = unwrap(context); - // Reset origin - context.setTransform(1,0,0,1,0,0); - // Clear - context.clearRect(0, 0, this.width, this.height); - context.lineWidth = this.lineWidth; - // Find dimensions - var toDraw = []; - var minX = Infinity; - var minY = Infinity; - var maxX = -Infinity; - var maxY = -Infinity; - var nodes = {}; - - // Process nodes - this.graph.nodes.forEach(function(process){ - if ( process.metadata && !isNaN(process.metadata.x) && !isNaN(process.metadata.y) ) { - toDraw.push(process); - nodes[process.id] = process; - minX = Math.min(minX, process.metadata.x); - minY = Math.min(minY, process.metadata.y); - maxX = Math.max(maxX, process.metadata.x); - maxY = Math.max(maxY, process.metadata.y); - } - }.bind(this)); - - // Process exported ports - if (this.graph.inports) { - Object.keys(this.graph.inports).forEach(function(key){ - var exp = this.graph.inports[key]; - if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { - toDraw.push(exp); - minX = Math.min(minX, exp.metadata.x); - minY = Math.min(minY, exp.metadata.y); - maxX = Math.max(maxX, exp.metadata.x); - maxY = Math.max(maxY, exp.metadata.y); - } - }.bind(this)); - } - if (this.graph.outports) { - Object.keys(this.graph.outports).forEach(function(key){ - var exp = this.graph.outports[key]; - if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { - toDraw.push(exp); - minX = Math.min(minX, exp.metadata.x); - minY = Math.min(minY, exp.metadata.y); - maxX = Math.max(maxX, exp.metadata.x); - maxY = Math.max(maxY, exp.metadata.y); - } - }.bind(this)); - } - - // Sanity check graph size - if (!isFinite(minX) || !isFinite(minY) || !isFinite(maxX) || !isFinite(maxY) ) { - return; - } - - minX -= this.nodeSize; - minY -= this.nodeSize; - maxX += this.nodeSize*2; - maxY += this.nodeSize*2; - var w = maxX - minX; - var h = maxY - minY; - // For the-graph-nav to bind - this.thumbrectangle[0] = minX; - this.thumbrectangle[1] = minY; - this.thumbrectangle[2] = w; - this.thumbrectangle[3] = h; - // Scale dimensions - var scale = (w > h) ? this.width/w : this.height/h; - this.thumbscale = scale; - var size = Math.round(this.nodeSize * scale); - var sizeHalf = size / 2; - // Translate origin to match - context.setTransform(1,0,0,1,0-minX*scale,0-minY*scale); - - // Draw connection from inports to nodes - if (this.graph.inports) { - Object.keys(this.graph.inports).forEach(function(key){ - var exp = this.graph.inports[key]; - if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { - var target = nodes[exp.process]; - if (!target) { - return; - } - this.drawEdge(context, scale, exp, target, 2); - } - }.bind(this)); - } - // Draw connection from nodes to outports - if (this.graph.outports) { - Object.keys(this.graph.outports).forEach(function(key){ - var exp = this.graph.outports[key]; - if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { - var source = nodes[exp.process]; - if (!source) { - return; - } - this.drawEdge(context, scale, source, exp, 5); - } - }.bind(this)); - } - - // Draw edges - this.graph.edges.forEach(function (connection){ - var source = nodes[connection.from.node]; - var target = nodes[connection.to.node]; - if (!source || !target) { - return; - } - this.drawEdge(context, scale, source, target, connection.metadata.route); - }.bind(this)); - - // Draw nodes - toDraw.forEach(function (node){ - var x = Math.round(node.metadata.x * scale); - var y = Math.round(node.metadata.y * scale); - - // Outer circle - context.strokeStyle = this.strokeStyle; - context.fillStyle = this.fillStyle; - context.beginPath(); - if (node.process && !node.component) { - context.arc(x, y, sizeHalf / 2, 0, 2*Math.PI, false); - } else { - context.arc(x, y, sizeHalf, 0, 2*Math.PI, false); - } - context.fill(); - context.stroke(); - - // Inner circle - context.beginPath(); - var smallRadius = Math.max(sizeHalf-1.5, 1); - if (node.process && !node.component) { - // Exported port - context.arc(x, y, smallRadius / 2, 0, 2*Math.PI, false); - } else { - // Regular node - context.arc(x, y, smallRadius, 0, 2*Math.PI, false); - } - context.fill(); - - }.bind(this)); - + var properties = { + width: this.width, + height: this.height, + edgeColors: this.edgeColors, + nodeSize: this.nodeSize, + strokeStyle: this.strokeStyle, + fillStyle: this.fillStyle, + lineWidth: this.lineWidth, + }; + var thumb = TheGraph.thumb.render(context, this.graph, properties); + + this.thumbrectangle = thumb.rectangle; + this.thumbscale = thumb.scale; }, listener: null, graphChanged: function (oldGraph, newGraph) { diff --git a/the-graph-thumb/the-graph-thumb.js b/the-graph-thumb/the-graph-thumb.js new file mode 100644 index 00000000..5487596c --- /dev/null +++ b/the-graph-thumb/the-graph-thumb.js @@ -0,0 +1,220 @@ + +function drawEdge(context, scale, source, target, route, properties) { + // Draw path + try { + context.strokeStyle = properties.edgeColors[0]; + if (route) { + // Color if route defined + context.strokeStyle = properties.edgeColors[route]; + } + var fromX = Math.round(source.metadata.x*scale)-0.5; + var fromY = Math.round(source.metadata.y*scale)-0.5; + var toX = Math.round(target.metadata.x*scale)-0.5; + var toY = Math.round(target.metadata.y*scale)-0.5; + context.beginPath(); + context.moveTo(fromX, fromY); + context.lineTo(toX, toY); + context.stroke(); + } catch (error) { + // FIXME: handle? + } +} + +function styleFromTheme(theme) { + var style = {}; + if (theme === "dark") { + style.fill = "hsl(184, 8%, 10%)"; + style.stroke = "hsl(180, 11%, 70%)"; + style.edgeColors = [ + "white", + "hsl( 0, 100%, 46%)", + "hsl( 35, 100%, 46%)", + "hsl( 60, 100%, 46%)", + "hsl(135, 100%, 46%)", + "hsl(160, 100%, 46%)", + "hsl(185, 100%, 46%)", + "hsl(210, 100%, 46%)", + "hsl(285, 100%, 46%)", + "hsl(310, 100%, 46%)", + "hsl(335, 100%, 46%)" + ]; + + } else { + // Light + style.fill = "hsl(184, 8%, 75%)"; + style.stroke = "hsl(180, 11%, 20%)"; + // Tweaked to make thin lines more visible + style.edgeColors = [ + "hsl( 0, 0%, 50%)", + "hsl( 0, 100%, 40%)", + "hsl( 29, 100%, 40%)", + "hsl( 47, 100%, 40%)", + "hsl(138, 100%, 40%)", + "hsl(160, 73%, 50%)", + "hsl(181, 100%, 40%)", + "hsl(216, 100%, 40%)", + "hsl(260, 100%, 40%)", + "hsl(348, 100%, 50%)", + "hsl(328, 100%, 40%)" + ]; + } + return style; +} + +function renderThumbnail(context, graph, properties) { + + // Reset origin + context.setTransform(1,0,0,1,0,0); + // Clear + context.clearRect(0, 0, properties.width, properties.height); + context.lineWidth = properties.lineWidth; + + // Find dimensions + var toDraw = []; + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + var nodes = {}; + + // Process nodes + graph.nodes.forEach(function(process){ + if ( process.metadata && !isNaN(process.metadata.x) && !isNaN(process.metadata.y) ) { + toDraw.push(process); + nodes[process.id] = process; + minX = Math.min(minX, process.metadata.x); + minY = Math.min(minY, process.metadata.y); + maxX = Math.max(maxX, process.metadata.x); + maxY = Math.max(maxY, process.metadata.y); + } + }.bind(this)); + + // Process exported ports + if (graph.inports) { + Object.keys(graph.inports).forEach(function(key){ + var exp = graph.inports[key]; + if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { + toDraw.push(exp); + minX = Math.min(minX, exp.metadata.x); + minY = Math.min(minY, exp.metadata.y); + maxX = Math.max(maxX, exp.metadata.x); + maxY = Math.max(maxY, exp.metadata.y); + } + }.bind(this)); + } + if (graph.outports) { + Object.keys(graph.outports).forEach(function(key){ + var exp = graph.outports[key]; + if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { + toDraw.push(exp); + minX = Math.min(minX, exp.metadata.x); + minY = Math.min(minY, exp.metadata.y); + maxX = Math.max(maxX, exp.metadata.x); + maxY = Math.max(maxY, exp.metadata.y); + } + }.bind(this)); + } + + // Sanity check graph size + if (!isFinite(minX) || !isFinite(minY) || !isFinite(maxX) || !isFinite(maxY) ) { + return; + } + + minX -= properties.nodeSize; + minY -= properties.nodeSize; + maxX += properties.nodeSize*2; + maxY += properties.nodeSize*2; + var w = maxX - minX; + var h = maxY - minY; + // For the-graph-nav to bind + var thumbrectangle = []; + thumbrectangle[0] = minX; + thumbrectangle[1] = minY; + thumbrectangle[2] = w; + thumbrectangle[3] = h; + // Scale dimensions + var scale = (w > h) ? properties.width/w : properties.height/h; + var thumbscale = scale; + var size = Math.round(properties.nodeSize * scale); + var sizeHalf = size / 2; + // Translate origin to match + context.setTransform(1,0,0,1,0-minX*scale,0-minY*scale); + + // Draw connection from inports to nodes + if (graph.inports) { + Object.keys(graph.inports).forEach(function(key){ + var exp = graph.inports[key]; + if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { + var target = nodes[exp.process]; + if (!target) { + return; + } + drawEdge(context, scale, exp, target, 2, properties); + } + }.bind(this)); + } + // Draw connection from nodes to outports + if (graph.outports) { + Object.keys(graph.outports).forEach(function(key){ + var exp = graph.outports[key]; + if ( exp.metadata && !isNaN(exp.metadata.x) && !isNaN(exp.metadata.y) ) { + var source = nodes[exp.process]; + if (!source) { + return; + } + drawEdge(context, scale, source, exp, 5, properties); + } + }.bind(this)); + } + + // Draw edges + graph.edges.forEach(function (connection){ + var source = nodes[connection.from.node]; + var target = nodes[connection.to.node]; + if (!source || !target) { + return; + } + drawEdge(context, scale, source, target, connection.metadata.route, properties); + }.bind(this)); + + // Draw nodes + toDraw.forEach(function (node){ + var x = Math.round(node.metadata.x * scale); + var y = Math.round(node.metadata.y * scale); + + // Outer circle + context.strokeStyle = properties.strokeStyle; + context.fillStyle = properties.fillStyle; + context.beginPath(); + if (node.process && !node.component) { + context.arc(x, y, sizeHalf / 2, 0, 2*Math.PI, false); + } else { + context.arc(x, y, sizeHalf, 0, 2*Math.PI, false); + } + context.fill(); + context.stroke(); + + // Inner circle + context.beginPath(); + var smallRadius = Math.max(sizeHalf-1.5, 1); + if (node.process && !node.component) { + // Exported port + context.arc(x, y, smallRadius / 2, 0, 2*Math.PI, false); + } else { + // Regular node + context.arc(x, y, smallRadius, 0, 2*Math.PI, false); + } + context.fill(); + + }.bind(this)); + + return { + rectangle: thumbrectangle, + scale: thumbscale + }; +} + +module.exports = { + render: renderThumbnail, + styleFromTheme: styleFromTheme, +}; diff --git a/the-graph/font-awesome-unicode-map.js b/the-graph/font-awesome-unicode-map.js index 586aa99d..5dd8e5c3 100644 --- a/the-graph/font-awesome-unicode-map.js +++ b/the-graph/font-awesome-unicode-map.js @@ -2,8 +2,7 @@ this file is generated via `grunt build` */ -(function (context) { -"use strict"; +module.exports.register = function (context) { context.TheGraph.FONT_AWESOME = { "500px": "", @@ -738,4 +737,4 @@ context.TheGraph.FONT_AWESOME = { "youtube-square": "" }; -})(this); \ No newline at end of file +}; \ No newline at end of file diff --git a/the-graph/the-graph-app.js b/the-graph/the-graph-app.js index 9e8a7c9d..653a56c1 100644 --- a/the-graph/the-graph-app.js +++ b/the-graph/the-graph-app.js @@ -1,5 +1,4 @@ -(function (context) { - "use strict"; +module.exports.register = function (context) { var TheGraph = context.TheGraph; @@ -625,4 +624,4 @@ })); -})(this); +}; diff --git a/the-graph/the-graph-autolayout.js b/the-graph/the-graph-autolayout.js new file mode 100644 index 00000000..c1aeeddc --- /dev/null +++ b/the-graph/the-graph-autolayout.js @@ -0,0 +1,56 @@ + +// NOTE: caller should wrap in a graph transaction, to group all changes made to @graph +function applyAutolayout(graph, keilerGraph, props) { + + // Update original graph nodes with the new coordinates from KIELER graph + var children = keilerGraph.children.slice(); + + var i, len; + for (i=0, len = children.length; i - - - - - - - - - - - - - - - -