├── .gitignore
├── LICENSE
├── README.md
├── index.js
├── package-lock.json
├── package.json
├── public
├── graphs
│ ├── blocks_margin-convention.json
│ ├── blocks_responding-to-resize.json
│ ├── graph-data-structure_ab-nodes.json
│ ├── graph-data-structure_ab.json
│ ├── graph-data-structure_abc.json
│ ├── graph-data-structure_cycle.json
│ ├── graph-data-structure_cycles.json
│ ├── graph-data-structure_getting-dressed.json
│ ├── graph-data-structure_tricky-case.json
│ ├── reactive-function_ab.json
│ ├── reactive-function_abc.json
│ ├── reactive-function_abcd.json
│ ├── reactive-function_any-number.json
│ ├── reactive-function_data-binding.json
│ ├── reactive-function_full-name.json
│ ├── reactive-function_ohms-law.json
│ ├── reactive-function_tricky-case.json
│ ├── reactive-model_ab.json
│ ├── reactive-model_abc.json
│ ├── reactive-model_full-name.json
│ ├── reactive-model_not-too-tricky.json
│ ├── reactive-model_side-effect-ab.json
│ ├── reactive-model_side-effect.json
│ ├── reactive-model_trickier-case.json
│ ├── reactive-model_tricky-case.json
│ ├── reactive-vis_margin-tweaked.json
│ ├── reactive-vis_margin.json
│ ├── reactive-vis_svg.json
│ └── streamGraphExplorer.json
├── index.html
└── reactiveFlowGraph
│ ├── README.md
│ ├── barChartFlow.json
│ ├── forceDirectedGraph.js
│ ├── index.html
│ ├── main.js
│ ├── model.js
│ └── styles.css
└── server.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Datavis Tech
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # graph-diagrams
2 | A tool for visualizing directed graphs. [Try it!](http://bl.ocks.org/curran/9b73eb564c1c8a3d8f3ab207de364bf4)
3 |
4 | This tool has been used to create the graph diagrams found in
5 |
6 | * reactive-model
7 | * reactive-function
8 | * graph-data-structure
9 |
10 | To use, clone this repository then run `npm start`. To open a graph file from the `/public/graphs` directory, add the name of the file to the URL hash (e.g. [http://localhost:3000/#full-name](http://localhost:3000/#full-name)) then refresh the page. This is all the tool does currently. There are plans to save the coordinates for fixed nodes in the graph files.
11 |
12 | Draws from [Reactive Flow Diagram](http://bl.ocks.org/curran/5905182da50a4667dc00).
13 |
14 | ## Diagrams
15 |
16 |
17 |
18 |
19 | reactive-model - AB
20 |
21 |
22 |
23 |
24 |
25 | reactive-model - ABC
26 |
27 |
28 |
29 |
30 |
31 | reactive-model - CDE
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | reactive-function - Ohm's Law
58 |
59 |
60 |
61 |
62 |
63 |
64 | bl.ocks.org - Responding to Resize with ReactiveModel
65 |
66 |
67 |
68 |
69 |
70 |
71 | bl.ocks.org - Margin Convention with ReactiveModel
72 |
73 |
74 |
75 |
76 |
77 | reactive-vis - SVG
78 |
79 |
80 |
81 |
82 |
83 | reactive-vis - Margin
84 |
85 |
86 |
87 |
88 |
89 |
90 | graph-data-structure - Getting Dressed
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // This module is a utility for writing graph files
2 | // from within the unit tests of other projects.
3 | var fs = require("fs");
4 | module.exports = function (options){
5 | return function (serialized, name){
6 | if(options.outputGraphs){
7 | var filename = [
8 | "../graph-diagrams/public/graphs/",
9 | options.project,
10 | "_",
11 | name,
12 | ".json"
13 | ].join("");
14 | var data = JSON.stringify(serialized, null, 2);
15 | fs.writeFile(filename, data);
16 | }
17 | };
18 | };
19 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "graph-diagrams",
3 | "version": "0.5.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.3",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
10 | "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
11 | "dev": true,
12 | "requires": {
13 | "mime-types": "2.1.16",
14 | "negotiator": "0.6.1"
15 | }
16 | },
17 | "array-flatten": {
18 | "version": "1.1.1",
19 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
20 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
21 | "dev": true
22 | },
23 | "content-disposition": {
24 | "version": "0.5.2",
25 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
26 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
27 | "dev": true
28 | },
29 | "content-type": {
30 | "version": "1.0.2",
31 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
32 | "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=",
33 | "dev": true
34 | },
35 | "cookie": {
36 | "version": "0.3.1",
37 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
38 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
39 | "dev": true
40 | },
41 | "cookie-signature": {
42 | "version": "1.0.6",
43 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
44 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
45 | "dev": true
46 | },
47 | "debug": {
48 | "version": "2.6.8",
49 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
50 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
51 | "dev": true,
52 | "requires": {
53 | "ms": "2.0.0"
54 | }
55 | },
56 | "depd": {
57 | "version": "1.1.1",
58 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
59 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=",
60 | "dev": true
61 | },
62 | "destroy": {
63 | "version": "1.0.4",
64 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
65 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
66 | "dev": true
67 | },
68 | "ee-first": {
69 | "version": "1.1.1",
70 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
71 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
72 | "dev": true
73 | },
74 | "encodeurl": {
75 | "version": "1.0.1",
76 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
77 | "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=",
78 | "dev": true
79 | },
80 | "escape-html": {
81 | "version": "1.0.3",
82 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
83 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
84 | "dev": true
85 | },
86 | "etag": {
87 | "version": "1.8.0",
88 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz",
89 | "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=",
90 | "dev": true
91 | },
92 | "express": {
93 | "version": "4.15.4",
94 | "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz",
95 | "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=",
96 | "dev": true,
97 | "requires": {
98 | "accepts": "1.3.3",
99 | "array-flatten": "1.1.1",
100 | "content-disposition": "0.5.2",
101 | "content-type": "1.0.2",
102 | "cookie": "0.3.1",
103 | "cookie-signature": "1.0.6",
104 | "debug": "2.6.8",
105 | "depd": "1.1.1",
106 | "encodeurl": "1.0.1",
107 | "escape-html": "1.0.3",
108 | "etag": "1.8.0",
109 | "finalhandler": "1.0.4",
110 | "fresh": "0.5.0",
111 | "merge-descriptors": "1.0.1",
112 | "methods": "1.1.2",
113 | "on-finished": "2.3.0",
114 | "parseurl": "1.3.1",
115 | "path-to-regexp": "0.1.7",
116 | "proxy-addr": "1.1.5",
117 | "qs": "6.5.0",
118 | "range-parser": "1.2.0",
119 | "send": "0.15.4",
120 | "serve-static": "1.12.4",
121 | "setprototypeof": "1.0.3",
122 | "statuses": "1.3.1",
123 | "type-is": "1.6.15",
124 | "utils-merge": "1.0.0",
125 | "vary": "1.1.1"
126 | }
127 | },
128 | "finalhandler": {
129 | "version": "1.0.4",
130 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz",
131 | "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==",
132 | "dev": true,
133 | "requires": {
134 | "debug": "2.6.8",
135 | "encodeurl": "1.0.1",
136 | "escape-html": "1.0.3",
137 | "on-finished": "2.3.0",
138 | "parseurl": "1.3.1",
139 | "statuses": "1.3.1",
140 | "unpipe": "1.0.0"
141 | }
142 | },
143 | "forwarded": {
144 | "version": "0.1.0",
145 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz",
146 | "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=",
147 | "dev": true
148 | },
149 | "fresh": {
150 | "version": "0.5.0",
151 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz",
152 | "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=",
153 | "dev": true
154 | },
155 | "http-errors": {
156 | "version": "1.6.2",
157 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
158 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
159 | "dev": true,
160 | "requires": {
161 | "depd": "1.1.1",
162 | "inherits": "2.0.3",
163 | "setprototypeof": "1.0.3",
164 | "statuses": "1.3.1"
165 | }
166 | },
167 | "inherits": {
168 | "version": "2.0.3",
169 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
170 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
171 | "dev": true
172 | },
173 | "ipaddr.js": {
174 | "version": "1.4.0",
175 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz",
176 | "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=",
177 | "dev": true
178 | },
179 | "media-typer": {
180 | "version": "0.3.0",
181 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
182 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
183 | "dev": true
184 | },
185 | "merge-descriptors": {
186 | "version": "1.0.1",
187 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
188 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
189 | "dev": true
190 | },
191 | "methods": {
192 | "version": "1.1.2",
193 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
194 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
195 | "dev": true
196 | },
197 | "mime": {
198 | "version": "1.3.4",
199 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
200 | "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=",
201 | "dev": true
202 | },
203 | "mime-db": {
204 | "version": "1.29.0",
205 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz",
206 | "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=",
207 | "dev": true
208 | },
209 | "mime-types": {
210 | "version": "2.1.16",
211 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz",
212 | "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=",
213 | "dev": true,
214 | "requires": {
215 | "mime-db": "1.29.0"
216 | }
217 | },
218 | "ms": {
219 | "version": "2.0.0",
220 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
221 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
222 | "dev": true
223 | },
224 | "negotiator": {
225 | "version": "0.6.1",
226 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
227 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
228 | "dev": true
229 | },
230 | "on-finished": {
231 | "version": "2.3.0",
232 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
233 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
234 | "dev": true,
235 | "requires": {
236 | "ee-first": "1.1.1"
237 | }
238 | },
239 | "parseurl": {
240 | "version": "1.3.1",
241 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
242 | "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=",
243 | "dev": true
244 | },
245 | "path-to-regexp": {
246 | "version": "0.1.7",
247 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
248 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
249 | "dev": true
250 | },
251 | "proxy-addr": {
252 | "version": "1.1.5",
253 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz",
254 | "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=",
255 | "dev": true,
256 | "requires": {
257 | "forwarded": "0.1.0",
258 | "ipaddr.js": "1.4.0"
259 | }
260 | },
261 | "qs": {
262 | "version": "6.5.0",
263 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz",
264 | "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==",
265 | "dev": true
266 | },
267 | "range-parser": {
268 | "version": "1.2.0",
269 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
270 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
271 | "dev": true
272 | },
273 | "send": {
274 | "version": "0.15.4",
275 | "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz",
276 | "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=",
277 | "dev": true,
278 | "requires": {
279 | "debug": "2.6.8",
280 | "depd": "1.1.1",
281 | "destroy": "1.0.4",
282 | "encodeurl": "1.0.1",
283 | "escape-html": "1.0.3",
284 | "etag": "1.8.0",
285 | "fresh": "0.5.0",
286 | "http-errors": "1.6.2",
287 | "mime": "1.3.4",
288 | "ms": "2.0.0",
289 | "on-finished": "2.3.0",
290 | "range-parser": "1.2.0",
291 | "statuses": "1.3.1"
292 | }
293 | },
294 | "serve-static": {
295 | "version": "1.12.4",
296 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz",
297 | "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=",
298 | "dev": true,
299 | "requires": {
300 | "encodeurl": "1.0.1",
301 | "escape-html": "1.0.3",
302 | "parseurl": "1.3.1",
303 | "send": "0.15.4"
304 | }
305 | },
306 | "setprototypeof": {
307 | "version": "1.0.3",
308 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
309 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=",
310 | "dev": true
311 | },
312 | "statuses": {
313 | "version": "1.3.1",
314 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
315 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
316 | "dev": true
317 | },
318 | "type-is": {
319 | "version": "1.6.15",
320 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
321 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
322 | "dev": true,
323 | "requires": {
324 | "media-typer": "0.3.0",
325 | "mime-types": "2.1.16"
326 | }
327 | },
328 | "unpipe": {
329 | "version": "1.0.0",
330 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
331 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
332 | "dev": true
333 | },
334 | "utils-merge": {
335 | "version": "1.0.0",
336 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
337 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=",
338 | "dev": true
339 | },
340 | "vary": {
341 | "version": "1.1.1",
342 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz",
343 | "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=",
344 | "dev": true
345 | }
346 | }
347 | }
348 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "graph-diagrams",
3 | "version": "0.5.0",
4 | "description": "A tool for visualizing directed graphs.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node server.js",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "files": [
11 | "index.js"
12 | ],
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/datavis-tech/graph-diagrams.git"
16 | },
17 | "keywords": [
18 | "graph",
19 | "visualization"
20 | ],
21 | "author": "Curran Kelleher",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/datavis-tech/graph-diagrams/issues"
25 | },
26 | "homepage": "https://github.com/datavis-tech/graph-diagrams#readme",
27 | "devDependencies": {
28 | "express": "^4.13.4"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/public/graphs/blocks_margin-convention.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "1",
5 | "propertyName": "svg-width"
6 | },
7 | {
8 | "id": "2",
9 | "propertyName": "svg"
10 | },
11 | {
12 | "id": "3",
13 | "propertyName": "width"
14 | },
15 | {
16 | "id": "4",
17 | "propertyName": "svg-height"
18 | },
19 | {
20 | "id": "5",
21 | "propertyName": "height"
22 | },
23 | {
24 | "id": "6",
25 | "propertyName": "innerWidth"
26 | },
27 | {
28 | "id": "7",
29 | "propertyName": "marginLeft"
30 | },
31 | {
32 | "id": "8",
33 | "propertyName": "marginRight"
34 | },
35 | {
36 | "id": "9",
37 | "propertyName": "innerHeight"
38 | },
39 | {
40 | "id": "10",
41 | "propertyName": "marginTop"
42 | },
43 | {
44 | "id": "11",
45 | "propertyName": "marginBottom"
46 | },
47 | {
48 | "id": "12",
49 | "propertyName": "g"
50 | },
51 | {
52 | "id": "13",
53 | "propertyName": "g-transform"
54 | },
55 | {
56 | "id": "14",
57 | "propertyName": "rect"
58 | },
59 | {
60 | "id": "15",
61 | "propertyName": "rect-width"
62 | },
63 | {
64 | "id": "16",
65 | "propertyName": "rect-height"
66 | }
67 | ],
68 | "links": [
69 | {
70 | "source": "2",
71 | "target": "1"
72 | },
73 | {
74 | "source": "2",
75 | "target": "4"
76 | },
77 | {
78 | "source": "2",
79 | "target": "12"
80 | },
81 | {
82 | "source": "3",
83 | "target": "1"
84 | },
85 | {
86 | "source": "3",
87 | "target": "6"
88 | },
89 | {
90 | "source": "5",
91 | "target": "4"
92 | },
93 | {
94 | "source": "5",
95 | "target": "9"
96 | },
97 | {
98 | "source": "6",
99 | "target": "15"
100 | },
101 | {
102 | "source": "7",
103 | "target": "6"
104 | },
105 | {
106 | "source": "7",
107 | "target": "13"
108 | },
109 | {
110 | "source": "8",
111 | "target": "6"
112 | },
113 | {
114 | "source": "9",
115 | "target": "16"
116 | },
117 | {
118 | "source": "10",
119 | "target": "9"
120 | },
121 | {
122 | "source": "10",
123 | "target": "13"
124 | },
125 | {
126 | "source": "11",
127 | "target": "9"
128 | },
129 | {
130 | "source": "12",
131 | "target": "13"
132 | },
133 | {
134 | "source": "12",
135 | "target": "14"
136 | },
137 | {
138 | "source": "14",
139 | "target": "15"
140 | },
141 | {
142 | "source": "14",
143 | "target": "16"
144 | }
145 | ]
146 | }
--------------------------------------------------------------------------------
/public/graphs/blocks_responding-to-resize.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "1",
5 | "propertyName": ""
6 | },
7 | {
8 | "id": "2",
9 | "propertyName": "svg"
10 | },
11 | {
12 | "id": "3",
13 | "propertyName": "width"
14 | },
15 | {
16 | "id": "4",
17 | "propertyName": "height"
18 | }
19 | ],
20 | "links": [
21 | {
22 | "source": "2",
23 | "target": "1"
24 | },
25 | {
26 | "source": "3",
27 | "target": "1"
28 | },
29 | {
30 | "source": "4",
31 | "target": "1"
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/public/graphs/graph-data-structure_ab-nodes.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "a"
5 | },
6 | {
7 | "id": "b"
8 | }
9 | ],
10 | "links": []
11 | }
--------------------------------------------------------------------------------
/public/graphs/graph-data-structure_ab.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "a"
5 | },
6 | {
7 | "id": "b"
8 | }
9 | ],
10 | "links": [
11 | {
12 | "source": "a",
13 | "target": "b"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/public/graphs/graph-data-structure_abc.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "a"
5 | },
6 | {
7 | "id": "b"
8 | },
9 | {
10 | "id": "c"
11 | }
12 | ],
13 | "links": [
14 | {
15 | "source": "a",
16 | "target": "b"
17 | },
18 | {
19 | "source": "b",
20 | "target": "c"
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/public/graphs/graph-data-structure_cycle.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "a"
5 | },
6 | {
7 | "id": "b"
8 | },
9 | {
10 | "id": "c"
11 | }
12 | ],
13 | "links": [
14 | {
15 | "source": "a",
16 | "target": "b"
17 | },
18 | {
19 | "source": "b",
20 | "target": "c"
21 | },
22 | {
23 | "source": "c",
24 | "target": "a"
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/public/graphs/graph-data-structure_cycles.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "a"
5 | },
6 | {
7 | "id": "b"
8 | },
9 | {
10 | "id": "c"
11 | }
12 | ],
13 | "links": [
14 | {
15 | "source": "a",
16 | "target": "b"
17 | },
18 | {
19 | "source": "a",
20 | "target": "c"
21 | },
22 | {
23 | "source": "b",
24 | "target": "a"
25 | },
26 | {
27 | "source": "b",
28 | "target": "c"
29 | },
30 | {
31 | "source": "c",
32 | "target": "b"
33 | },
34 | {
35 | "source": "c",
36 | "target": "a"
37 | }
38 | ]
39 | }
--------------------------------------------------------------------------------
/public/graphs/graph-data-structure_getting-dressed.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "socks"
5 | },
6 | {
7 | "id": "shoes"
8 | },
9 | {
10 | "id": "shirt"
11 | },
12 | {
13 | "id": "belt"
14 | },
15 | {
16 | "id": "tie"
17 | },
18 | {
19 | "id": "jacket"
20 | },
21 | {
22 | "id": "pants"
23 | },
24 | {
25 | "id": "underpants"
26 | }
27 | ],
28 | "links": [
29 | {
30 | "source": "socks",
31 | "target": "shoes"
32 | },
33 | {
34 | "source": "shirt",
35 | "target": "belt"
36 | },
37 | {
38 | "source": "shirt",
39 | "target": "tie"
40 | },
41 | {
42 | "source": "belt",
43 | "target": "jacket"
44 | },
45 | {
46 | "source": "tie",
47 | "target": "jacket"
48 | },
49 | {
50 | "source": "pants",
51 | "target": "shoes"
52 | },
53 | {
54 | "source": "pants",
55 | "target": "belt"
56 | },
57 | {
58 | "source": "underpants",
59 | "target": "pants"
60 | }
61 | ]
62 | }
--------------------------------------------------------------------------------
/public/graphs/graph-data-structure_tricky-case.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "a"
5 | },
6 | {
7 | "id": "b"
8 | },
9 | {
10 | "id": "d"
11 | },
12 | {
13 | "id": "c"
14 | },
15 | {
16 | "id": "e"
17 | }
18 | ],
19 | "links": [
20 | {
21 | "source": "a",
22 | "target": "b"
23 | },
24 | {
25 | "source": "a",
26 | "target": "d"
27 | },
28 | {
29 | "source": "b",
30 | "target": "c"
31 | },
32 | {
33 | "source": "d",
34 | "target": "e"
35 | },
36 | {
37 | "source": "c",
38 | "target": "e"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_ab.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "40",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "41",
9 | "propertyName": "a"
10 | }
11 | ],
12 | "links": [
13 | {
14 | "source": "41",
15 | "target": "40"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_abc.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "9",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "10",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "11",
13 | "propertyName": "c"
14 | }
15 | ],
16 | "links": [
17 | {
18 | "source": "9",
19 | "target": "11"
20 | },
21 | {
22 | "source": "10",
23 | "target": "9"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_abcd.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "12",
5 | "propertyName": "c"
6 | },
7 | {
8 | "id": "13",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "14",
13 | "propertyName": "d"
14 | },
15 | {
16 | "id": "15",
17 | "propertyName": "b"
18 | }
19 | ],
20 | "links": [
21 | {
22 | "source": "12",
23 | "target": "14"
24 | },
25 | {
26 | "source": "13",
27 | "target": "12"
28 | },
29 | {
30 | "source": "15",
31 | "target": "14"
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_any-number.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "4",
5 | "propertyName": "d"
6 | },
7 | {
8 | "id": "5",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "6",
13 | "propertyName": "e"
14 | },
15 | {
16 | "id": "7",
17 | "propertyName": "b"
18 | },
19 | {
20 | "id": "8",
21 | "propertyName": "c"
22 | }
23 | ],
24 | "links": [
25 | {
26 | "source": "5",
27 | "target": "4"
28 | },
29 | {
30 | "source": "5",
31 | "target": "6"
32 | },
33 | {
34 | "source": "7",
35 | "target": "6"
36 | },
37 | {
38 | "source": "8",
39 | "target": "6"
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_data-binding.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "42",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "43",
9 | "propertyName": "a"
10 | }
11 | ],
12 | "links": [
13 | {
14 | "source": "42",
15 | "target": "43"
16 | },
17 | {
18 | "source": "43",
19 | "target": "42"
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_full-name.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "1",
5 | "propertyName": "fullName"
6 | },
7 | {
8 | "id": "2",
9 | "propertyName": "firstName"
10 | },
11 | {
12 | "id": "3",
13 | "propertyName": "lastName"
14 | }
15 | ],
16 | "links": [
17 | {
18 | "source": "2",
19 | "target": "1"
20 | },
21 | {
22 | "source": "3",
23 | "target": "1"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_ohms-law.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "44",
5 | "propertyName": "I"
6 | },
7 | {
8 | "id": "45",
9 | "propertyName": "V"
10 | },
11 | {
12 | "id": "46",
13 | "propertyName": "R"
14 | }
15 | ],
16 | "links": [
17 | {
18 | "source": "44",
19 | "target": "45"
20 | },
21 | {
22 | "source": "44",
23 | "target": "46"
24 | },
25 | {
26 | "source": "45",
27 | "target": "44"
28 | },
29 | {
30 | "source": "45",
31 | "target": "46"
32 | },
33 | {
34 | "source": "46",
35 | "target": "44"
36 | },
37 | {
38 | "source": "46",
39 | "target": "45"
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-function_tricky-case.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "16",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "17",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "18",
13 | "propertyName": "c"
14 | },
15 | {
16 | "id": "19",
17 | "propertyName": "d"
18 | },
19 | {
20 | "id": "20",
21 | "propertyName": "e"
22 | }
23 | ],
24 | "links": [
25 | {
26 | "source": "16",
27 | "target": "18"
28 | },
29 | {
30 | "source": "17",
31 | "target": "16"
32 | },
33 | {
34 | "source": "17",
35 | "target": "19"
36 | },
37 | {
38 | "source": "18",
39 | "target": "20"
40 | },
41 | {
42 | "source": "19",
43 | "target": "20"
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_ab.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "34",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "35",
9 | "propertyName": "a"
10 | }
11 | ],
12 | "links": [
13 | {
14 | "source": "35",
15 | "target": "34"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_abc.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "46",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "47",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "48",
13 | "propertyName": "c"
14 | }
15 | ],
16 | "links": [
17 | {
18 | "source": "46",
19 | "target": "48"
20 | },
21 | {
22 | "source": "47",
23 | "target": "46"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_full-name.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "40",
5 | "propertyName": "fullName"
6 | },
7 | {
8 | "id": "41",
9 | "propertyName": "firstName"
10 | },
11 | {
12 | "id": "42",
13 | "propertyName": "lastName"
14 | }
15 | ],
16 | "links": [
17 | {
18 | "source": "41",
19 | "target": "40"
20 | },
21 | {
22 | "source": "42",
23 | "target": "40"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_not-too-tricky.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "49",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "50",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "51",
13 | "propertyName": "d"
14 | },
15 | {
16 | "id": "52",
17 | "propertyName": "c"
18 | },
19 | {
20 | "id": "53",
21 | "propertyName": "e"
22 | }
23 | ],
24 | "links": [
25 | {
26 | "source": "49",
27 | "target": "53"
28 | },
29 | {
30 | "source": "50",
31 | "target": "49"
32 | },
33 | {
34 | "source": "51",
35 | "target": "53"
36 | },
37 | {
38 | "source": "52",
39 | "target": "51"
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_side-effect-ab.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "89",
5 | "propertyName": ""
6 | },
7 | {
8 | "id": "90",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "91",
13 | "propertyName": "b"
14 | }
15 | ],
16 | "links": [
17 | {
18 | "source": "90",
19 | "target": "89"
20 | },
21 | {
22 | "source": "91",
23 | "target": "89"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_side-effect.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "87",
5 | "propertyName": ""
6 | },
7 | {
8 | "id": "88",
9 | "propertyName": "a"
10 | }
11 | ],
12 | "links": [
13 | {
14 | "source": "88",
15 | "target": "87"
16 | }
17 | ]
18 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_trickier-case.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "59",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "60",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "61",
13 | "propertyName": "c"
14 | },
15 | {
16 | "id": "62",
17 | "propertyName": "d"
18 | },
19 | {
20 | "id": "63",
21 | "propertyName": "e"
22 | },
23 | {
24 | "id": "64",
25 | "propertyName": "f"
26 | },
27 | {
28 | "id": "65",
29 | "propertyName": "g"
30 | },
31 | {
32 | "id": "66",
33 | "propertyName": "h"
34 | }
35 | ],
36 | "links": [
37 | {
38 | "source": "59",
39 | "target": "61"
40 | },
41 | {
42 | "source": "60",
43 | "target": "59"
44 | },
45 | {
46 | "source": "60",
47 | "target": "63"
48 | },
49 | {
50 | "source": "60",
51 | "target": "65"
52 | },
53 | {
54 | "source": "61",
55 | "target": "62"
56 | },
57 | {
58 | "source": "62",
59 | "target": "66"
60 | },
61 | {
62 | "source": "63",
63 | "target": "64"
64 | },
65 | {
66 | "source": "64",
67 | "target": "66"
68 | },
69 | {
70 | "source": "65",
71 | "target": "66"
72 | }
73 | ]
74 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-model_tricky-case.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "54",
5 | "propertyName": "b"
6 | },
7 | {
8 | "id": "55",
9 | "propertyName": "a"
10 | },
11 | {
12 | "id": "56",
13 | "propertyName": "c"
14 | },
15 | {
16 | "id": "57",
17 | "propertyName": "d"
18 | },
19 | {
20 | "id": "58",
21 | "propertyName": "e"
22 | }
23 | ],
24 | "links": [
25 | {
26 | "source": "54",
27 | "target": "56"
28 | },
29 | {
30 | "source": "54",
31 | "target": "58"
32 | },
33 | {
34 | "source": "55",
35 | "target": "54"
36 | },
37 | {
38 | "source": "55",
39 | "target": "57"
40 | },
41 | {
42 | "source": "57",
43 | "target": "58"
44 | }
45 | ]
46 | }
--------------------------------------------------------------------------------
/public/graphs/reactive-vis_margin-tweaked.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "7",
5 | "propertyName": "svg",
6 | "faded": true
7 | },
8 | {
9 | "id": "8",
10 | "propertyName": "width",
11 | "faded": true
12 | },
13 | {
14 | "id": "10",
15 | "propertyName": "height",
16 | "faded": true
17 | },
18 | {
19 | "id": "11",
20 | "propertyName": "innerWidth"
21 | },
22 | {
23 | "id": "12",
24 | "propertyName": "marginLeft"
25 | },
26 | {
27 | "id": "13",
28 | "propertyName": "marginRight"
29 | },
30 | {
31 | "id": "14",
32 | "propertyName": "innerHeight"
33 | },
34 | {
35 | "id": "15",
36 | "propertyName": "marginTop"
37 | },
38 | {
39 | "id": "16",
40 | "propertyName": "marginBottom"
41 | },
42 | {
43 | "id": "17",
44 | "propertyName": "g"
45 | },
46 | {
47 | "id": "18",
48 | "propertyName": "g-transform"
49 | }
50 | ],
51 | "links": [
52 | {
53 | "source": "7",
54 | "target": "17"
55 | },
56 | {
57 | "source": "8",
58 | "target": "11"
59 | },
60 | {
61 | "source": "10",
62 | "target": "14"
63 | },
64 | {
65 | "source": "12",
66 | "target": "11"
67 | },
68 | {
69 | "source": "12",
70 | "target": "18"
71 | },
72 | {
73 | "source": "13",
74 | "target": "11"
75 | },
76 | {
77 | "source": "15",
78 | "target": "14"
79 | },
80 | {
81 | "source": "15",
82 | "target": "18"
83 | },
84 | {
85 | "source": "16",
86 | "target": "14"
87 | },
88 | {
89 | "source": "17",
90 | "target": "18"
91 | }
92 | ]
93 | }
94 |
--------------------------------------------------------------------------------
/public/graphs/reactive-vis_margin.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "7",
5 | "propertyName": "svg",
6 | "faded": true
7 | },
8 | {
9 | "id": "8",
10 | "propertyName": "width",
11 | "faded": true
12 | },
13 | {
14 | "id": "10",
15 | "propertyName": "height",
16 | "faded": true
17 | },
18 | {
19 | "id": "11",
20 | "propertyName": "innerWidth"
21 | },
22 | {
23 | "id": "12",
24 | "propertyName": "marginLeft"
25 | },
26 | {
27 | "id": "13",
28 | "propertyName": "marginRight"
29 | },
30 | {
31 | "id": "14",
32 | "propertyName": "innerHeight"
33 | },
34 | {
35 | "id": "15",
36 | "propertyName": "marginTop"
37 | },
38 | {
39 | "id": "16",
40 | "propertyName": "marginBottom"
41 | },
42 | {
43 | "id": "17",
44 | "propertyName": "g"
45 | },
46 | {
47 | "id": "18",
48 | "propertyName": "g-transform"
49 | }
50 | ],
51 | "links": [
52 | {
53 | "source": "7",
54 | "target": "17"
55 | },
56 | {
57 | "source": "8",
58 | "target": "11"
59 | },
60 | {
61 | "source": "10",
62 | "target": "14"
63 | },
64 | {
65 | "source": "12",
66 | "target": "11"
67 | },
68 | {
69 | "source": "12",
70 | "target": "18"
71 | },
72 | {
73 | "source": "13",
74 | "target": "11"
75 | },
76 | {
77 | "source": "15",
78 | "target": "14"
79 | },
80 | {
81 | "source": "15",
82 | "target": "18"
83 | },
84 | {
85 | "source": "16",
86 | "target": "14"
87 | },
88 | {
89 | "source": "17",
90 | "target": "18"
91 | }
92 | ]
93 | }
94 |
--------------------------------------------------------------------------------
/public/graphs/reactive-vis_svg.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "1",
5 | "propertyName": "svg-width"
6 | },
7 | {
8 | "id": "2",
9 | "propertyName": "svg"
10 | },
11 | {
12 | "id": "3",
13 | "propertyName": "width"
14 | },
15 | {
16 | "id": "4",
17 | "propertyName": "svg-height"
18 | },
19 | {
20 | "id": "5",
21 | "propertyName": "height"
22 | }
23 | ],
24 | "links": [
25 | {
26 | "source": "2",
27 | "target": "1"
28 | },
29 | {
30 | "source": "2",
31 | "target": "4"
32 | },
33 | {
34 | "source": "3",
35 | "target": "1"
36 | },
37 | {
38 | "source": "5",
39 | "target": "4"
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/public/graphs/streamGraphExplorer.json:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": [
3 | {
4 | "id": "1",
5 | "propertyName": "data"
6 | },
7 | {
8 | "id": "2",
9 | "propertyName": "packedData"
10 | },
11 | {
12 | "id": "3",
13 | "propertyName": "dataFiltered"
14 | },
15 | {
16 | "id": "4",
17 | "propertyName": "src"
18 | },
19 | {
20 | "id": "5",
21 | "propertyName": "dest"
22 | },
23 | {
24 | "id": "6",
25 | "propertyName": "dataBySrc"
26 | },
27 | {
28 | "id": "7",
29 | "propertyName": "dataByDest"
30 | },
31 | {
32 | "id": "8",
33 | "propertyName": "dataByYear"
34 | },
35 | {
36 | "id": "9",
37 | "propertyName": "contextStreamData"
38 | },
39 | {
40 | "id": "10",
41 | "propertyName": "allYears"
42 | },
43 | {
44 | "id": "11",
45 | "propertyName": "srcKeys"
46 | },
47 | {
48 | "id": "12",
49 | "propertyName": "maxStreamLayers"
50 | },
51 | {
52 | "id": "13",
53 | "propertyName": "minStreamMax"
54 | },
55 | {
56 | "id": "14",
57 | "propertyName": "destKeys"
58 | },
59 | {
60 | "id": "15",
61 | "propertyName": "srcStreamDataAllYears"
62 | },
63 | {
64 | "id": "16",
65 | "propertyName": "destStreamDataAllYears"
66 | },
67 | {
68 | "id": "17",
69 | "propertyName": "srcStreamData"
70 | },
71 | {
72 | "id": "18",
73 | "propertyName": "zoomExtent"
74 | },
75 | {
76 | "id": "19",
77 | "propertyName": "destStreamData"
78 | },
79 | {
80 | "id": "20",
81 | "propertyName": "hashRouting"
82 | },
83 | {
84 | "id": "21",
85 | "propertyName": "svgDimensions"
86 | },
87 | {
88 | "id": "22",
89 | "propertyName": "containerBox"
90 | },
91 | {
92 | "id": "23",
93 | "propertyName": "srcStream"
94 | },
95 | {
96 | "id": "24",
97 | "propertyName": "srcStreamBox"
98 | },
99 | {
100 | "id": "25",
101 | "propertyName": "streamsMargin"
102 | },
103 | {
104 | "id": "26",
105 | "propertyName": "showStreamLabels"
106 | },
107 | {
108 | "id": "27",
109 | "propertyName": "colorScale"
110 | },
111 | {
112 | "id": "28",
113 | "propertyName": "destStream"
114 | },
115 | {
116 | "id": "29",
117 | "propertyName": "destStreamBox"
118 | },
119 | {
120 | "id": "30",
121 | "propertyName": "timeTicksYExtent"
122 | },
123 | {
124 | "id": "31",
125 | "propertyName": "timePanel"
126 | },
127 | {
128 | "id": "32",
129 | "propertyName": "timePanelBox"
130 | },
131 | {
132 | "id": "33",
133 | "propertyName": "contextStream"
134 | },
135 | {
136 | "id": "34",
137 | "propertyName": "contextStreamBox"
138 | },
139 | {
140 | "id": "35",
141 | "propertyName": "timeExtent"
142 | }
143 | ],
144 | "links": [
145 | {
146 | "source": "1",
147 | "target": "3"
148 | },
149 | {
150 | "source": "2",
151 | "target": "1"
152 | },
153 | {
154 | "source": "3",
155 | "target": "6"
156 | },
157 | {
158 | "source": "3",
159 | "target": "7"
160 | },
161 | {
162 | "source": "3",
163 | "target": "8"
164 | },
165 | {
166 | "source": "4",
167 | "target": "3"
168 | },
169 | {
170 | "source": "4",
171 | "target": "20"
172 | },
173 | {
174 | "source": "5",
175 | "target": "3"
176 | },
177 | {
178 | "source": "5",
179 | "target": "20"
180 | },
181 | {
182 | "source": "6",
183 | "target": "11"
184 | },
185 | {
186 | "source": "6",
187 | "target": "15"
188 | },
189 | {
190 | "source": "7",
191 | "target": "14"
192 | },
193 | {
194 | "source": "7",
195 | "target": "16"
196 | },
197 | {
198 | "source": "8",
199 | "target": "9"
200 | },
201 | {
202 | "source": "9",
203 | "target": "33"
204 | },
205 | {
206 | "source": "10",
207 | "target": "9"
208 | },
209 | {
210 | "source": "10",
211 | "target": "15"
212 | },
213 | {
214 | "source": "10",
215 | "target": "16"
216 | },
217 | {
218 | "source": "11",
219 | "target": "23"
220 | },
221 | {
222 | "source": "12",
223 | "target": "11"
224 | },
225 | {
226 | "source": "12",
227 | "target": "14"
228 | },
229 | {
230 | "source": "13",
231 | "target": "11"
232 | },
233 | {
234 | "source": "13",
235 | "target": "14"
236 | },
237 | {
238 | "source": "14",
239 | "target": "28"
240 | },
241 | {
242 | "source": "15",
243 | "target": "17"
244 | },
245 | {
246 | "source": "16",
247 | "target": "19"
248 | },
249 | {
250 | "source": "17",
251 | "target": "23"
252 | },
253 | {
254 | "source": "18",
255 | "target": "17"
256 | },
257 | {
258 | "source": "18",
259 | "target": "19"
260 | },
261 | {
262 | "source": "18",
263 | "target": "20"
264 | },
265 | {
266 | "source": "18",
267 | "target": "23"
268 | },
269 | {
270 | "source": "18",
271 | "target": "28"
272 | },
273 | {
274 | "source": "18",
275 | "target": "31"
276 | },
277 | {
278 | "source": "19",
279 | "target": "28"
280 | },
281 | {
282 | "source": "22",
283 | "target": "21"
284 | },
285 | {
286 | "source": "24",
287 | "target": "23"
288 | },
289 | {
290 | "source": "24",
291 | "target": "30"
292 | },
293 | {
294 | "source": "25",
295 | "target": "23"
296 | },
297 | {
298 | "source": "25",
299 | "target": "28"
300 | },
301 | {
302 | "source": "25",
303 | "target": "31"
304 | },
305 | {
306 | "source": "26",
307 | "target": "23"
308 | },
309 | {
310 | "source": "26",
311 | "target": "28"
312 | },
313 | {
314 | "source": "27",
315 | "target": "23"
316 | },
317 | {
318 | "source": "27",
319 | "target": "28"
320 | },
321 | {
322 | "source": "29",
323 | "target": "28"
324 | },
325 | {
326 | "source": "29",
327 | "target": "30"
328 | },
329 | {
330 | "source": "30",
331 | "target": "31"
332 | },
333 | {
334 | "source": "32",
335 | "target": "31"
336 | },
337 | {
338 | "source": "34",
339 | "target": "33"
340 | },
341 | {
342 | "source": "35",
343 | "target": "33"
344 | }
345 | ]
346 | }
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
19 |
20 |
21 | Graph Editor
22 |
23 |
24 |
56 |
57 |
58 |
265 |
266 |
267 |
--------------------------------------------------------------------------------
/public/reactiveFlowGraph/README.md:
--------------------------------------------------------------------------------
1 | This program renders a network diagram for a [ModelJS](https://github.com/curran/model) reactive flow.
2 |
3 | The input data is generated by an [experimental ModelJS branch](https://github.com/curran/model/tree/flow-graph-detection) that computes the reactive flow graph at runtime.
4 |
5 | Based on a [previous implementation from July 2014](https://github.com/curran/model/tree/5a30cc7647d970e452bbe10ef458c40a25d825c6/examples/dataFlowDiagram)
6 |
--------------------------------------------------------------------------------
/public/reactiveFlowGraph/barChartFlow.json:
--------------------------------------------------------------------------------
1 | {"nodes":[{"type":"lambda","fixed":1,"x":-108,"y":308},{"type":"property","property":"container","fixed":1,"x":-233,"y":306},{"type":"property","property":"svg","fixed":1,"x":-5,"y":309},{"type":"lambda","fixed":1,"x":214,"y":176},{"type":"property","property":"box","fixed":1,"x":-200,"y":257},{"type":"lambda","fixed":1,"x":80,"y":311},{"type":"property","property":"g","fixed":1,"x":165,"y":311},{"type":"lambda","fixed":1,"x":212,"y":264},{"type":"property","property":"margin","fixed":1,"x":-222,"y":202},{"type":"lambda","fixed":1,"x":231,"y":369},{"type":"property","property":"titleText","fixed":1,"x":388,"y":347},{"type":"lambda","fixed":1,"x":578,"y":391},{"type":"property","property":"titleOffset","fixed":1,"x":430,"y":401},{"type":"lambda","fixed":1,"x":214,"y":216},{"type":"property","property":"width","fixed":1,"x":497,"y":63},{"type":"property","property":"height","fixed":1,"x":485,"y":555},{"type":"lambda","fixed":1,"x":653,"y":123},{"type":"property","property":"xAxisG","fixed":1,"x":782,"y":65},{"type":"property","property":"xAxisText","fixed":1,"x":785,"y":119},{"type":"lambda","fixed":1,"x":963,"y":159},{"type":"property","property":"xAxisLabelOffset","fixed":1,"x":-292,"y":153},{"type":"lambda","fixed":1,"x":955,"y":53},{"type":"lambda","fixed":1,"x":963,"y":107},{"type":"lambda","fixed":1,"x":963,"y":221},{"type":"property","property":"xAxisLabel","fixed":1,"x":-257,"y":104},{"type":"lambda","fixed":1,"x":598,"y":332},{"type":"property","property":"yAxisG","fixed":1,"x":811,"y":442},{"type":"property","property":"yAxisText","fixed":1,"x":790,"y":390},{"type":"lambda","fixed":1,"x":946,"y":335},{"type":"property","property":"yAxisLabelOffset","fixed":1,"x":-286,"y":418},{"type":"lambda","fixed":1,"x":946,"y":463},{"type":"lambda","fixed":1,"x":944,"y":407},{"type":"property","property":"yAxisLabel","fixed":1,"x":-248,"y":466},{"type":"lambda","fixed":1,"x":674,"y":271},{"type":"property","property":"barsG","fixed":1,"x":1133,"y":312},{"type":"lambda","fixed":1,"x":589,"y":235},{"type":"lambda","fixed":true,"x":-87,"y":137},{"type":"property","property":"data","fixed":1,"x":-203,"y":365},{"type":"property","property":"xAttribute","fixed":1,"x":-242,"y":54},{"type":"property","property":"getX","fixed":1,"x":147,"y":92},{"type":"lambda","fixed":1,"x":17,"y":26},{"type":"property","property":"sortField","fixed":1,"x":-234,"y":2},{"type":"property","property":"sortOrder","fixed":1,"x":-235,"y":-51},{"type":"property","property":"sortedData","fixed":1,"x":157,"y":17},{"type":"lambda","fixed":1,"x":-70,"y":499},{"type":"property","property":"yAttribute","fixed":1,"x":-233,"y":521},{"type":"property","property":"getY","fixed":1,"x":61,"y":525},{"type":"lambda","fixed":1,"x":281,"y":585},{"type":"property","property":"yDomainMin","fixed":1,"x":-252,"y":573},{"type":"property","property":"yDomainMax","fixed":1,"x":-255,"y":625},{"type":"property","property":"yDomain","fixed":1,"x":475,"y":614},{"type":"lambda","fixed":1,"x":678,"y":566},{"type":"property","property":"yScale","fixed":1,"x":815,"y":565},{"type":"lambda","fixed":1,"x":1033,"y":516},{"type":"property","property":"getYScaled","fixed":1,"x":1243,"y":482},{"type":"lambda","fixed":1,"x":326,"y":4},{"type":"property","property":"xDomain","fixed":1,"x":498,"y":-11},{"type":"lambda","fixed":1,"x":952,"y":573},{"type":"lambda","fixed":1,"x":668,"y":-27},{"type":"property","property":"barPadding","fixed":1,"x":-248,"y":-101},{"type":"property","property":"xScale","fixed":1,"x":787,"y":0},{"type":"lambda","fixed":1,"x":1092,"y":96},{"type":"property","property":"getXScaled","fixed":1,"x":1233,"y":131},{"type":"lambda","fixed":1,"x":955,"y":-2},{"type":"lambda","fixed":1,"x":1378,"y":318}],"links":[{"source":1,"target":0},{"source":0,"target":2},{"source":2,"target":3},{"source":4,"target":3},{"source":2,"target":5},{"source":5,"target":6},{"source":6,"target":7},{"source":8,"target":7},{"source":6,"target":9},{"source":9,"target":10},{"source":10,"target":11},{"source":12,"target":11},{"source":4,"target":13},{"source":8,"target":13},{"source":13,"target":14},{"source":13,"target":15},{"source":6,"target":16},{"source":16,"target":17},{"source":16,"target":18},{"source":18,"target":19},{"source":20,"target":19},{"source":17,"target":21},{"source":15,"target":21},{"source":18,"target":22},{"source":14,"target":22},{"source":18,"target":23},{"source":24,"target":23},{"source":6,"target":25},{"source":25,"target":26},{"source":25,"target":27},{"source":27,"target":28},{"source":29,"target":28},{"source":27,"target":30},{"source":15,"target":30},{"source":27,"target":31},{"source":32,"target":31},{"source":6,"target":33},{"source":33,"target":34},{"source":10,"target":35},{"source":14,"target":35},{"source":37,"target":36},{"source":38,"target":36},{"source":36,"target":39},{"source":41,"target":40},{"source":42,"target":40},{"source":37,"target":40},{"source":40,"target":43},{"source":37,"target":44},{"source":45,"target":44},{"source":44,"target":46},{"source":37,"target":47},{"source":46,"target":47},{"source":48,"target":47},{"source":49,"target":47},{"source":47,"target":50},{"source":37,"target":51},{"source":50,"target":51},{"source":15,"target":51},{"source":51,"target":52},{"source":37,"target":53},{"source":52,"target":53},{"source":46,"target":53},{"source":53,"target":54},{"source":43,"target":55},{"source":39,"target":55},{"source":55,"target":56},{"source":26,"target":57},{"source":52,"target":57},{"source":56,"target":58},{"source":14,"target":58},{"source":59,"target":58},{"source":58,"target":60},{"source":37,"target":61},{"source":60,"target":61},{"source":39,"target":61},{"source":61,"target":62},{"source":17,"target":63},{"source":60,"target":63},{"source":34,"target":64},{"source":43,"target":64},{"source":62,"target":64},{"source":54,"target":64},{"source":60,"target":64},{"source":15,"target":64}],"scale":0.5332125839901604,"translate":[373.3250529749264,143.7733216449567]}
2 |
--------------------------------------------------------------------------------
/public/reactiveFlowGraph/forceDirectedGraph.js:
--------------------------------------------------------------------------------
1 | // A force directed graph visualization module.
2 | define(["d3", "model", "lodash"], function (d3, Model, _) {
3 |
4 | // The constructor function, accepting default values.
5 | return function ForceDirectedGraph(defaults) {
6 |
7 | // Create a Model.
8 | // This will serve as the public API for the visualization.
9 | var model = Model({
10 |
11 | // Force directed layout parameters.
12 | charge: -200,
13 | linkDistance: 140,
14 | gravity: 0.03,
15 |
16 | // The color scale.
17 | color: d3.scale.ordinal()
18 | .domain(["property", "lambda"])
19 | .range(["#FFD1B5", "white"])
20 | }),
21 | force = d3.layout.force(),
22 | zoom = d3.behavior.zoom(),
23 |
24 | // The size of nodes and arrows
25 | nodeSize = 20,
26 | arrowWidth = 8;
27 |
28 | // Respond to zoom interactions.
29 | zoom.on("zoom", function (){
30 | model.scale = zoom.scale();
31 | model.translate = zoom.translate();
32 | });
33 |
34 | // Call onTick each frame of the force directed layout.
35 | force.on("tick", function(e) { onTick(e); })
36 |
37 | // This function gets reassigned later, each time new data loads.
38 | function onTick(){}
39 |
40 | // Stop propagation of drag events here so that both dragging nodes and panning are possible.
41 | // Draws from http://stackoverflow.com/questions/17953106/why-does-d3-js-v3-break-my-force-graph-when-implementing-zooming-when-v2-doesnt/17976205#17976205
42 | force.drag().on("dragstart", function () {
43 | d3.event.sourceEvent.stopPropagation();
44 | });
45 |
46 | // Fix node positions after the first time the user clicks and drags a node.
47 | force.drag().on("dragend", function (d) {
48 |
49 | // Stop the dragged node from moving.
50 | d.fixed = true;
51 |
52 | // Communicate this change to the outside world.
53 | serializeState();
54 | });
55 |
56 |
57 | // Create the SVG element from the container DOM element.
58 | model.when("container", function (container) {
59 | model.svg = d3.select(container).append("svg").call(zoom);
60 | });
61 |
62 | // Adjust the size of the SVG based on the `box` property.
63 | model.when(["svg", "box"], function (svg, box) {
64 | svg.attr("width", box.width).attr("height", box.height);
65 | force.size([box.width, box.height]);
66 | });
67 |
68 | // Create the SVG group that will contain the visualization.
69 | model.when("svg", function (svg) {
70 | model.g = svg.append("g");
71 |
72 | // Arrowhead setup.
73 | // Draws from Mobile Patent Suits example:
74 | // http://bl.ocks.org/mbostock/1153292
75 | svg.append("defs")
76 | .append("marker")
77 | .attr("id", "arrow")
78 | .attr("orient", "auto")
79 | .attr("preserveAspectRatio", "none")
80 | // See also http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
81 | //.attr("viewBox", "0 -" + arrowWidth + " 10 " + (2 * arrowWidth))
82 | .attr("viewBox", "0 -5 10 10")
83 | // See also http://www.w3.org/TR/SVG/painting.html#MarkerElementRefXAttribute
84 | .attr("refX", 10)
85 | .attr("refY", 0)
86 | .attr("markerWidth", 10)
87 | .attr("markerHeight", arrowWidth)
88 | .append("path")
89 | .attr("d", "M0,-5L10,0L0,5");
90 | });
91 |
92 | // These 3 groups exist for control of Z-ordering.
93 | model.when("g", function (g) {
94 | model.nodeG = g.append("g");
95 | model.linkG = g.append("g");
96 | model.arrowG = g.append("g");
97 | });
98 |
99 | // Update the force layout with configured properties.
100 | model.when(["charge"], force.charge, force);
101 | model.when(["linkDistance"], force.linkDistance, force);
102 | model.when(["gravity"], force.gravity, force);
103 |
104 | // Update zoom scale and translation.
105 | model.when(["scale", "translate", "g"], function (scale, translate, g) {
106 |
107 | // In the case the scale and translate were set externally,
108 | if(zoom.scale() !== scale){
109 |
110 | // update the internal D3 zoom state.
111 | zoom.scale(scale);
112 | zoom.translate(translate);
113 | }
114 |
115 | // Transform the SVG group.
116 | g.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
117 | });
118 |
119 | // "state" represents the serialized state of the graph.
120 | model.when("state", function(state){
121 |
122 | // Extract the scale and translate.
123 | if(state.scale && model.scale !== state.scale){
124 | model.scale = state.scale;
125 | }
126 | if(state.translate && model.translate !== state.translate){
127 | model.translate = state.translate;
128 | }
129 |
130 | // Set the node and link data.
131 | var newData = _.cloneDeep(state);
132 | force.nodes(newData.nodes).links(newData.links).start();
133 | model.data = newData;
134 | });
135 |
136 | // Update the serialized state.
137 | model.when(["scale", "translate"], _.throttle(function(scale, translate){
138 | serializeState();
139 | }, 1000));
140 |
141 | // Sets model.state to expose the serialized state.
142 | function serializeState(){
143 | var data = model.data,
144 | scale = model.scale,
145 | translate = model.translate;
146 | model.state = {
147 | nodes: data.nodes.map(function(node){
148 | return {
149 | type: node.type,
150 | property: node.property,
151 | fixed: node.fixed,
152 |
153 | // Keep size of JSON small, so it fits in a URL.
154 | x: Math.round(node.x),
155 | y: Math.round(node.y)
156 | };
157 | }),
158 | links: data.links.map(function(link){
159 | // Replaced link object references with indices for serialization.
160 | return {
161 | source: link.source.index,
162 | target: link.target.index
163 | };
164 | }),
165 | scale: scale,
166 | translate: translate
167 | };
168 | }
169 |
170 | model.when(["data", "color", "nodeG", "linkG", "arrowG"],
171 | function(data, color, nodeG, linkG, arrowG){
172 | var node = nodeG.selectAll("g").data(data.nodes),
173 | nodeEnter = node.enter().append("g").call(force.drag);
174 |
175 | nodeEnter.append("rect").attr("class", "node")
176 | .attr("y", -nodeSize)
177 | .attr("height", nodeSize * 2)
178 | .attr("rx", nodeSize)
179 | .attr("ry", nodeSize);
180 |
181 | nodeEnter.append("text").attr("class", "nodeLabel");
182 |
183 | node.select("g text")
184 |
185 | // Use the property name for property nodes, and λ for lambda nodes.
186 | .text(function(d) {
187 | return (d.type === "property" ? d.property : "λ");
188 | })
189 |
190 | //Center text vertically.
191 | .attr("dy", function(d) {
192 | if(d.type === "lambda"){
193 | return "0.35em";
194 | } else {
195 | return "0.3em";
196 | }
197 | })
198 |
199 | // Compute rectancle sizes based on text labels.
200 | .each(function (d) {
201 | var circleWidth = nodeSize * 2,
202 | textLength = this.getComputedTextLength(),
203 | textWidth = textLength + nodeSize;
204 |
205 | if(circleWidth > textWidth) {
206 | d.isCircle = true;
207 | d.rectX = -nodeSize;
208 | d.rectWidth = circleWidth;
209 | } else {
210 | d.isCircle = false;
211 | d.rectX = -(textLength + nodeSize) / 2;
212 | d.rectWidth = textWidth;
213 | d.textLength = textLength;
214 | }
215 | });
216 |
217 | node.select("g rect")
218 | .attr("x", function(d) { return d.rectX; })
219 | .style("foo", function(d) { return "test"; })
220 | .attr("width", function(d) { return d.rectWidth; })
221 | .style("fill", function(d) { return color(d.type); });
222 | node.exit().remove();
223 |
224 | var link = linkG.selectAll(".link").data(data.links);
225 | link.enter().append("line").attr("class", "link")
226 | link.exit().remove();
227 |
228 | var arrow = arrowG.selectAll(".arrow").data(data.links);
229 | arrow.enter().append("line")
230 | .attr("class", "arrow")
231 | .attr("marker-end", function(d) { return "url(#arrow)" });
232 | arrow.exit().remove();
233 |
234 | // Run a modified version of force directed layout
235 | // to account for link direction going from left to right.
236 | onTick = function(e) {
237 |
238 | // Execute left-right constraints
239 | var k = 1 * e.alpha;
240 | force.links().forEach(function (link) {
241 | var a = link.source,
242 | b = link.target,
243 | dx = b.x - a.x,
244 | dy = b.y - a.y,
245 | d = Math.sqrt(dx * dx + dy * dy),
246 | x = (a.x + b.x) / 2;
247 | if(!a.fixed){
248 | a.x += k * (x - d / 2 - a.x);
249 | }
250 | if(!b.fixed){
251 | b.x += k * (x + d / 2 - b.x);
252 | }
253 | });
254 | force.nodes().forEach(function (d) {
255 | if(d.isCircle){
256 | d.leftX = d.rightX = d.x;
257 | } else {
258 | d.leftX = d.x - d.textLength / 2 + nodeSize / 2;
259 | d.rightX = d.x + d.textLength / 2 - nodeSize / 2;
260 | }
261 | });
262 |
263 | link.call(edge);
264 | arrow.call(edge);
265 |
266 | node.attr("transform", function(d) {
267 | return "translate(" + d.x + "," + d.y + ")";
268 | });
269 | };
270 | });
271 |
272 | // Sets the (x1, y1, x2, y2) line properties for graph edges.
273 | function edge(selection){
274 | selection
275 | .each(function (d) {
276 | var sourceX, targetX, dy, dy, angle;
277 |
278 | if( d.source.rightX < d.target.leftX ){
279 | sourceX = d.source.rightX;
280 | targetX = d.target.leftX;
281 | } else if( d.target.rightX < d.source.leftX ){
282 | targetX = d.target.rightX;
283 | sourceX = d.source.leftX;
284 | } else if (d.target.isCircle) {
285 | targetX = sourceX = d.target.x;
286 | } else if (d.source.isCircle) {
287 | targetX = sourceX = d.source.x;
288 | } else {
289 | targetX = sourceX = (d.source.x + d.target.x) / 2;
290 | }
291 |
292 | dx = targetX - sourceX;
293 | dy = d.target.y - d.source.y;
294 | angle = Math.atan2(dx, dy);
295 |
296 | d.sourceX = sourceX + Math.sin(angle) * nodeSize;
297 | d.targetX = targetX - Math.sin(angle) * nodeSize;
298 | d.sourceY = d.source.y + Math.cos(angle) * nodeSize;
299 | d.targetY = d.target.y - Math.cos(angle) * nodeSize;
300 | })
301 | .attr("x1", function(d) { return d.sourceX; })
302 | .attr("y1", function(d) { return d.sourceY; })
303 | .attr("x2", function(d) { return d.targetX; })
304 | .attr("y2", function(d) { return d.targetY; });
305 | }
306 |
307 | model.set(defaults);
308 |
309 | return model;
310 | };
311 | });
312 |
--------------------------------------------------------------------------------
/public/reactiveFlowGraph/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
19 |
20 |
21 |
22 |
23 | Data Flow Diagram
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/public/reactiveFlowGraph/main.js:
--------------------------------------------------------------------------------
1 | require(["d3", "forceDirectedGraph", "lodash"], function (d3, ForceDirectedGraph, lodash) {
2 |
3 | // Initialize the force directed graph.
4 | var container = d3.select("#container").node(),
5 | forceDirectedGraph = ForceDirectedGraph({ container: container });
6 |
7 | // Initialize zoom based on client size.
8 | var scale = container.clientWidth * 1 / 800;
9 | forceDirectedGraph.scale = scale;
10 | forceDirectedGraph.translate = [
11 | container.clientWidth / 2 * (1 - scale),
12 | container.clientHeight / 2 * (1 - scale)
13 | ];
14 |
15 |
16 | // Set up default data.
17 | if(!location.hash){
18 | location.hash = '{"nodes":[{"type":"lambda","fixed":0,"x":442,"y":250},{"type":"property","property":"firstName","fixed":1,"x":290,"y":212},{"type":"property","property":"lastName","fixed":1,"x":293,"y":294},{"type":"property","property":"fullName","fixed":0,"x":581,"y":247}],"links":[{"source":1,"target":0},{"source":2,"target":0},{"source":0,"target":3}],"scale":1.938287710980903,"translate":[-360.71751731834274,-241.583180104211]}';
19 | }
20 |
21 | // Update the fragment identifier in response to user interactions.
22 | forceDirectedGraph.when(["state"], function(state){
23 | location.hash = JSON.stringify(state);
24 | console.log(JSON.stringify(state));
25 | });
26 |
27 | // Sets the data on the graph visualization from the fragment identifier.
28 | // See https://github.com/curran/screencasts/blob/gh-pages/navigation/examples/code/snapshot11/main.js
29 | function navigate(){
30 | if(location.hash){
31 | var newState = JSON.parse(location.hash.substr(1));
32 | if(JSON.stringify(newState) !== JSON.stringify(forceDirectedGraph.state)){
33 | forceDirectedGraph.state = newState;
34 | }
35 | }
36 | }
37 |
38 | // Navigate once to the initial hash value.
39 | navigate();
40 |
41 | // Navigate whenever the fragment identifier value changes.
42 | window.addEventListener("hashchange", navigate);
43 |
44 | // Sets the `box` model property
45 | // based on the size of the container,
46 | function computeBox(){
47 | forceDirectedGraph.box = {
48 | width: container.clientWidth,
49 | height: container.clientHeight
50 | };
51 | }
52 |
53 | // once to initialize `model.box`, and
54 | computeBox();
55 |
56 | // whenever the browser window resizes in the future.
57 | window.addEventListener("resize", computeBox);
58 |
59 | });
60 |
--------------------------------------------------------------------------------
/public/reactiveFlowGraph/model.js:
--------------------------------------------------------------------------------
1 | // Implements key-value models with a functional reactive `when` operator.
2 | // See also https://github.com/curran/model
3 | define([], function (){
4 |
5 | // The constructor function, accepting default values.
6 | return function Model(defaults){
7 |
8 | // The returned public API object.
9 | var model = {},
10 |
11 | // The internal stored values for tracked properties. { property -> value }
12 | values = {},
13 |
14 | // The listeners for each tracked property. { property -> [callback] }
15 | listeners = {},
16 |
17 | // The set of tracked properties. { property -> true }
18 | trackedProperties = {};
19 |
20 | // The functional reactive "when" operator.
21 | //
22 | // * `properties` An array of property names (can also be a single property string).
23 | // * `callback` A callback function that is called:
24 | // * with property values as arguments, ordered corresponding to the properties array,
25 | // * only if all specified properties have values,
26 | // * once for initialization,
27 | // * whenever one or more specified properties change,
28 | // * on the next tick of the JavaScript event loop after properties change,
29 | // * only once as a result of one or more synchronous changes to dependency properties.
30 | function when(properties, callback){
31 |
32 | // This function will trigger the callback to be invoked.
33 | var triggerCallback = debounce(function (){
34 | var args = properties.map(function(property){
35 | return values[property];
36 | });
37 | if(allAreDefined(args)){
38 | callback.apply(null, args);
39 | }
40 | });
41 |
42 | // Handle either an array or a single string.
43 | properties = (properties instanceof Array) ? properties : [properties];
44 |
45 | // Trigger the callback once for initialization.
46 | triggerCallback();
47 |
48 | // Trigger the callback whenever specified properties change.
49 | properties.forEach(function(property){
50 | on(property, triggerCallback);
51 | });
52 | }
53 |
54 | // Returns a debounced version of the given function.
55 | // See http://underscorejs.org/#debounce
56 | function debounce(callback){
57 | var queued = false;
58 | return function () {
59 | if(!queued){
60 | queued = true;
61 | setTimeout(function () {
62 | queued = false;
63 | callback();
64 | }, 0);
65 | }
66 | };
67 | }
68 |
69 | // Returns true if all elements of the given array are defined, false otherwise.
70 | function allAreDefined(arr){
71 | return !arr.some(function (d) {
72 | return typeof d === 'undefined' || d === null;
73 | });
74 | }
75 |
76 | // Adds a change listener for a given property with Backbone-like behavior.
77 | // See http://backbonejs.org/#Events-on
78 | function on(property, callback){
79 | getListeners(property).push(callback);
80 | track(property);
81 | };
82 |
83 | // Gets or creates the array of listener functions for a given property.
84 | function getListeners(property){
85 | return listeners[property] || (listeners[property] = []);
86 | }
87 |
88 | // Tracks a property if it is not already tracked.
89 | function track(property){
90 | if(!(property in trackedProperties)){
91 | trackedProperties[property] = true;
92 | values[property] = model[property];
93 | Object.defineProperty(model, property, {
94 | get: function () { return values[property]; },
95 | set: function(value) {
96 | values[property] = value;
97 | getListeners(property).forEach(function(callback){
98 | callback(value);
99 | });
100 | }
101 | });
102 | }
103 | }
104 |
105 | // Sets all of the given values on the model.
106 | // Values is an object { property -> value }.
107 | function set(values){
108 | for(property in values){
109 | model[property] = values[property];
110 | }
111 | }
112 |
113 | // Transfer defaults passed into the constructor to the model.
114 | set(defaults);
115 |
116 | // Expose the public API.
117 | model.when = when;
118 | model.on = on;
119 | model.set = set
120 | return model;
121 | }
122 | });
123 |
--------------------------------------------------------------------------------
/public/reactiveFlowGraph/styles.css:
--------------------------------------------------------------------------------
1 | /* Make the visualization container fill the page. */
2 | #container {
3 | position: fixed;
4 | left: 0px;
5 | right: 0px;
6 | top: 0px;
7 | bottom: 0px;
8 | }
9 |
10 | /* Style the nodes of the graph. */
11 | .node {
12 | stroke: black;
13 | stroke-width: 1.5;
14 | }
15 | .nodeLabel {
16 | font-size: 2em;
17 | /* Center text horizontally */
18 | text-anchor: middle;
19 | }
20 |
21 | /* Style the links of the graph. */
22 | .link {
23 | stroke: black;
24 | }
25 |
26 | /* Set the arrowhead size. */
27 | .arrow {
28 | stroke-width: 1.5px;
29 | }
30 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var express = require("express");
2 | var app = express();
3 |
4 | app.use(express.static("public"));
5 |
6 | // Writes the metadata for the graph with the given name.
7 | app.post("/write/:graphName", function (req, res) {
8 |
9 | // Write the data to public/graphs/{{graphName}}-meta.json
10 | // * pan
11 | // * zoom
12 | // * fixedNodes: id ->
13 | // * x: int
14 | // * y: int
15 |
16 | res.send("Success!");
17 | });
18 |
19 | app.listen(3000, function () {
20 | console.log("Example app listening on port 3000!");
21 | });
22 |
--------------------------------------------------------------------------------