├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bower.json ├── demo ├── test-all.html ├── test-cna.html ├── test-color.html ├── test-dendrogram.html ├── test-dispatch.html ├── test-graph.html ├── test-heatmap.html ├── test-mutmtx.html ├── test-scatterplot.html ├── test-tooltip.html ├── test-transcript.html └── testData │ ├── entireJson.json │ ├── example-annotation.json │ ├── example-cna-browser.json │ ├── example-cna.json │ ├── example-dendrogram-N30.json │ ├── example-graph.json │ ├── example-heatmap.json │ ├── example-mutation-matrix.json │ ├── example-scatterplot.json │ ├── example-transcript.json │ ├── example2-mutation-matrix.json │ ├── example2-transcript.json │ ├── example3-transcript.json │ ├── exampleAnnotations.json │ ├── extractAnnotationfromwhole.py │ ├── extractCNAfromwhole.py │ ├── extractMutMtxfromwhole.py │ └── extractTranscriptfromwhole.py ├── gd3.js ├── package.json ├── src ├── cna │ ├── cna.js │ ├── cnaChart.js │ ├── cnaData.js │ ├── cnaStyle.js │ └── index.js ├── core │ ├── class.js │ ├── color.js │ ├── dataStructures.js │ ├── index.js │ └── util.js ├── dendrogram │ ├── dendrogram.js │ ├── dendrogramChart.js │ ├── dendrogramData.js │ ├── dendrogramStyle.js │ └── index.js ├── dispatch │ ├── dispatch.js │ └── index.js ├── end.js ├── gd3.js ├── graph │ ├── graph.js │ ├── graphChart.js │ ├── graphData.js │ ├── graphStyle.js │ └── index.js ├── heatmap │ ├── heatmap.js │ ├── heatmapChart.js │ ├── heatmapData.js │ ├── heatmapStyle.js │ └── index.js ├── mutmtx │ ├── index.js │ ├── mutmtx.js │ ├── mutmtxChart.js │ ├── mutmtxData.js │ └── mutmtxStyle.js ├── scatterplot │ ├── index.js │ ├── scatterplot.js │ ├── scatterplotChart.js │ ├── scatterplotData.js │ └── scatterplotStyle.js ├── start.js ├── tooltip │ ├── annotationView-old.js │ ├── index.js │ ├── tooltip-license.md │ ├── tooltip.js │ ├── tooltipElement.js │ ├── tooltipImage.js │ ├── tooltipLink.js │ ├── tooltipStyle.js │ ├── tooltipTable.js │ ├── tooltipText.js │ ├── tooltipView.js │ └── tooltipVote.js └── transcript │ ├── index.js │ ├── transcript.js │ ├── transcriptChart.js │ ├── transcriptData.js │ └── transcriptStyle.js └── test └── placeholder.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bower_components/ 3 | node_modules/ 4 | gd3.test.js 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013,2014,2015 Brown University, Providence, RI. 2 | 3 | All Rights Reserved 4 | 5 | Permission to use, copy, modify, and distribute this software and its 6 | documentation for any purpose other than its incorporation into a 7 | commercial product is hereby granted without fee, provided that the 8 | above copyright notice appear in all copies and that both that 9 | copyright notice and this permission notice appear in supporting 10 | documentation, and that the name of Brown University not be used in 11 | advertising or publicity pertaining to distribution of the software 12 | without specific, written prior permission. 13 | 14 | BROWN UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 | INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ANY 16 | PARTICULAR PURPOSE. IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE FOR 17 | ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 20 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | gd3.js: $(shell node_modules/.bin/smash --list src/gd3.js) 2 | node_modules/.bin/smash src/gd3.js | node_modules/.bin/uglifyjs - -b indent-level=2 -o $@ 3 | 4 | global: $(shell smash --list src/gd3.js) 5 | smash src/gd3.js | uglifyjs - -b indent-level=2 -o $@ 6 | 7 | test: $(shell node_modules/.bin/smash --list src/gd3.js) 8 | node_modules/.bin/smash src/gd3.js > gd3.test.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #gd3 2 | 3 | A library of genomics visualizations. 4 | 5 | ### Setup 6 | 7 | 1. **Requirements**: [npm](https://npmjs.org/) and [node](http://nodejs.org/). 8 | 2. **Clone**: `git clone https://@bitbucket.org/raphaellab/gd3.git`. 9 | 3. **Install dependencies**: `npm install`. 10 | 11 | To use the Python scripts, you must also: 12 | 13 | 1. **Clone HotNet2**: `git clone https://@bitbucket.org/raphaellab/hotnet2.git`. 14 | 2. **Add "HOTNET" environment variable**: Add `setenvvar HOTNET ` to `~/.environment` 15 | 16 | ### Javascript: (`js/`) 17 | 18 | *Visualization scripts* 19 | 20 | Mutation Matrix(`js/mutation-matrix.js`) 21 | 22 | Subnetworks(`js/subnetwork.js`) 23 | 24 | Transcript Plot(`js/transcript-plot.js`) 25 | 26 | ### Styles (`js/style/`) 27 | 28 | *Pre-defined style files.* 29 | 30 | `js/default-style.js`: default styles 31 | 32 | `js/pancancer-sample-coloring.json`: cancer type -> color mapping for PanCancer 33 | 34 | ### Draw (`.`) 35 | 36 | *Python scripts for saving visualizations to SVG.* 37 | 38 | Python Mutation Matrix (`drawMutationMatrix.py`) 39 | 40 | Usage: see python drawMutationMatrix.py -h 41 | 42 | *Node.js scripts for saving visualizations to SVG.* 43 | 44 | Mutation Matrix(`drawMutationMatrix.js`) 45 | 46 | Usage: node drawMutationMatrix.js --json= --outdir= 47 | 48 | Transcript Plot(`drawTranscriptPlot.js`) 49 | 50 | Usage: node drawTranscriptPlot.js --json= --outfile= 51 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gd3", 3 | "version": "1.0.0", 4 | "homepage": "https://github.com/raphael-group/gd3", 5 | "authors": [ 6 | "connor ", 7 | "max " 8 | ], 9 | "main": "gd3.js", 10 | "license": "MIT", 11 | "private": true, 12 | "ignore": [ 13 | "**/.*", 14 | "node_modules", 15 | "bower_components", 16 | "test", 17 | "tests" 18 | ], 19 | "dependencies": { 20 | "d3-tip": "~0.6.4", 21 | "d3": "latest", 22 | "jquery": "~2.1.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demo/test-all.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Testing Page 7 | 8 | 9 | 17 | 18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 | 127 | 128 | -------------------------------------------------------------------------------- /demo/test-cna.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example CNA 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 21 | 22 | 23 | 27 | 28 |
14 |

MYC (modified to include some deletions)

15 |
16 |
18 |

PDGFRA (modified to include some deletions)

19 |
20 |
24 |

PTEN

25 |
26 |
29 | 30 | 31 | 44 | 45 | -------------------------------------------------------------------------------- /demo/test-color.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Color Formatting 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | 243 | 244 | -------------------------------------------------------------------------------- /demo/test-dendrogram.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Transcript Annotation 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /demo/test-dispatch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Linking of Views with Dispatch 7 | 8 | 9 | 23 | 24 | 25 |

Sample linking

26 |
27 |
28 |

Form update

29 |
30 |
31 | Form

32 | 33 |

34 | 35 |

36 | 37 |

38 | 39 |
40 |
41 | 42 | 43 | 229 | 230 | -------------------------------------------------------------------------------- /demo/test-graph.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Transcript Annotation 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 26 | 27 | -------------------------------------------------------------------------------- /demo/test-heatmap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Transcript Annotation 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 25 | 26 | -------------------------------------------------------------------------------- /demo/test-mutmtx.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Mutation Matrix 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 26 | 27 | -------------------------------------------------------------------------------- /demo/test-scatterplot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Scatterplot 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 26 | 27 | -------------------------------------------------------------------------------- /demo/test-tooltip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Mutation Matrix 7 | 8 | 9 | 19 | 20 | 21 |
22 | 23 | 24 | 245 | 246 | -------------------------------------------------------------------------------- /demo/test-transcript.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GD3 Example Transcript Annotation 7 | 8 | 9 | 10 | 11 |
12 | Select protein domain database for first transcript plot
13 | CD 14 |
15 | PFAM 16 |
17 | SMART 18 |
19 |
20 |
21 | 22 | 23 | 24 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /demo/testData/example-annotation.json: -------------------------------------------------------------------------------- 1 | {"categories":["Surival (days)","Gender","ABSOLUTE Purity"],"sampleToAnnotations":{"TCGA-A3-3311":[1191,"male",0.49],"TCGA-A3-3316":[0,"male",0.29],"TCGA-A3-3319":[0,"male",0.39],"TCGA-A3-3320":[0,"female",0.71],"TCGA-A3-3322":[0,"male",0.76],"TCGA-A3-3323":[0,"male",0.56],"TCGA-A3-3362":[0,"female",0.49],"TCGA-A3-3363":[0,"male",0.79],"TCGA-A3-3367":[0,"male",0.72],"TCGA-A3-3372":[0,"male",0.51],"TCGA-A3-3373":[0,"female",0.57],"TCGA-A3-3382":[0,"male",0.63],"TCGA-A3-3387":[0,"male",0],"TCGA-AK-3425":[0,"male",0.45],"TCGA-AK-3431":[0,"female",0.47],"TCGA-AK-3434":[0,"male",0.47],"TCGA-AK-3444":[0,"female",0.66],"TCGA-AK-3445":[0,"male",0.56],"TCGA-AK-3455":[683,"female",0.56],"TCGA-AK-3458":[0,"male",0],"TCGA-B0-4706":[65,"male",0],"TCGA-B0-4718":[0,"male",0.75],"TCGA-B0-4815":[1588,"male",0],"TCGA-B0-4816":[1371,"male",0.43],"TCGA-B0-4817":[1019,"male",0.24],"TCGA-B0-4818":[510,"female",0.48],"TCGA-B0-4822":[1111,"male",0.3],"TCGA-B0-4823":[454,"male",0.56],"TCGA-B0-4824":[1657,"female",0.56],"TCGA-B0-4828":[307,"male",0],"TCGA-B0-4842":[1724,"female",0.21],"TCGA-B0-4843":[354,"male",0.31],"TCGA-B0-4845":[1986,"male",0.64],"TCGA-B0-4847":[793,"male",0.54],"TCGA-B0-4849":[69,"male",0.6],"TCGA-B0-4852":[1121,"female",0],"TCGA-B0-5085":[770,"female",0.68],"TCGA-B0-5094":[333,"male",0.68],"TCGA-B0-5096":[68,"female",0.43],"TCGA-B0-5102":[2764,"female",0.63],"TCGA-B0-5104":[2752,"female",0.55],"TCGA-B0-5107":[927,"female",0.62],"TCGA-B0-5110":[0,"female",0.53],"TCGA-B0-5115":[0,"male",0.64],"TCGA-B0-5116":[0,"male",0.56],"TCGA-B0-5120":[0,"female",0.49],"TCGA-B0-5399":[0,"male",0],"TCGA-B0-5692":[0,"female",0],"TCGA-B0-5693":[0,"female",0],"TCGA-B0-5694":[480,"male",0],"TCGA-B0-5696":[0,"male",0],"TCGA-B0-5698":[0,"male",0.71],"TCGA-B0-5699":[0,"male",0.74],"TCGA-B0-5702":[0,"male",0],"TCGA-B0-5703":[0,"male",0],"TCGA-B0-5705":[0,"female",0],"TCGA-B0-5710":[0,"male",0.57],"TCGA-B0-5711":[0,"male",0.6],"TCGA-B0-5713":[0,"female",0.59],"TCGA-B2-4099":[0,"male",0.64],"TCGA-B2-4101":[0,"male",0.47],"TCGA-B4-5377":[0,"female",0],"TCGA-B8-4148":[0,"female",0.55],"TCGA-B8-4153":[0,"male",0.72],"TCGA-B8-4154":[0,"female",0],"TCGA-B8-5158":[0,"male",0],"TCGA-B8-5549":[0,"male",0],"TCGA-BP-4158":[0,"male",0.62],"TCGA-BP-4159":[2601,"male",0.6],"TCGA-BP-4164":[992,"female",0.73],"TCGA-BP-4176":[0,"male",0.56],"TCGA-BP-4329":[845,"male",0.56],"TCGA-BP-4337":[2,"female",0.35],"TCGA-BP-4340":[562,"female",0.73],"TCGA-BP-4759":[0,"male",0],"TCGA-BP-4774":[0,"female",0.52],"TCGA-BP-4801":[0,"male",0.49],"TCGA-BP-4803":[0,"male",0],"TCGA-BP-4960":[0,"male",0],"TCGA-BP-4963":[0,"male",0.54],"TCGA-BP-4964":[0,"female",0.73],"TCGA-BP-4973":[0,"male",0.68],"TCGA-BP-4975":[0,"male",0.72],"TCGA-BP-4981":[1097,"female",0.79],"TCGA-BP-4982":[0,"male",0.46],"TCGA-BP-4985":[952,"male",0.44],"TCGA-BP-4987":[0,"female",0.68],"TCGA-BP-4988":[828,"male",0.23],"TCGA-BP-4989":[0,"male",0.46],"TCGA-BP-4992":[0,"male",0.38],"TCGA-BP-4993":[0,"male",0.66],"TCGA-BP-4995":[0,"male",0.7],"TCGA-BP-4999":[0,"male",0.48],"TCGA-BP-5168":[1463,"male",0.6],"TCGA-BP-5169":[0,"male",0.4],"TCGA-BP-5170":[0,"male",0.49],"TCGA-BP-5180":[0,"male",0.76],"TCGA-BP-5183":[0,"male",0.68],"TCGA-BP-5184":[0,"male",0.56],"TCGA-BP-5186":[0,"female",0.57],"TCGA-BP-5187":[0,"male",0.42],"TCGA-BP-5189":[822,"male",0.52],"TCGA-BP-5198":[0,"male",0.43],"TCGA-BP-5199":[0,"male",0.49],"TCGA-BP-5200":[0,"male",0.42],"TCGA-CJ-4634":[0,"female",0.75],"TCGA-CJ-4635":[0,"male",0.51],"TCGA-CJ-4644":[336,"female",0],"TCGA-CJ-4871":[0,"male",0],"TCGA-CJ-4872":[0,"male",0],"TCGA-CJ-4875":[0,"male",0.75],"TCGA-CJ-4876":[0,"male",0.62],"TCGA-CJ-4884":[0,"female",0.45],"TCGA-CJ-4887":[932,"male",0.45],"TCGA-CJ-4888":[1567,"male",0],"TCGA-CJ-4892":[0,"female",0.52],"TCGA-CJ-4903":[0,"male",0.68],"TCGA-CJ-4905":[0,"female",0.57],"TCGA-CJ-4908":[0,"male",0.46],"TCGA-CJ-4912":[0,"male",0.64],"TCGA-CJ-4913":[1173,"female",0.59],"TCGA-CJ-4920":[139,"female",0.56],"TCGA-CJ-4923":[572,"female",0.34],"TCGA-CJ-5676":[0,"male",0],"TCGA-CJ-5678":[574,"male",0],"TCGA-CJ-5679":[679,"male",0],"TCGA-CJ-5682":[0,"male",0],"TCGA-CJ-5684":[0,"male",0],"TCGA-CJ-6030":[2298,"male",0.49],"TCGA-CJ-6032":[0,"female",0.57],"TCGA-CJ-6033":[224,"female",0.37],"TCGA-CW-5580":[1964,"female",0.63],"TCGA-CW-5589":[0,"male",0],"TCGA-CW-6093":[0,"male",0.56],"TCGA-CZ-4853":[0,"male",0.78],"TCGA-CZ-4854":[1404,"male",0],"TCGA-CZ-4857":[1432,"male",0.57],"TCGA-CZ-4861":[446,"male",0.72],"TCGA-CZ-5453":[0,"male",0],"TCGA-CZ-5454":[722,"male",0],"TCGA-CZ-5455":[561,"male",0.51],"TCGA-CZ-5456":[0,"male",0],"TCGA-CZ-5457":[0,"male",0.46],"TCGA-CZ-5460":[0,"male",0],"TCGA-CZ-5461":[330,"male",0],"TCGA-CZ-5465":[0,"female",0],"TCGA-CZ-5466":[0,"male",0],"TCGA-CZ-5470":[0,"female",0],"TCGA-CZ-5982":[0,"female",0.62],"TCGA-CZ-5986":[0,"male",0.68],"TCGA-CZ-5989":[0,"male",0.47],"TCGA-DV-5566":[0,"female",0.59],"TCGA-EU-5907":[null,"male",0.87],"TCGA-A5-A0GB":[0,"female",0.89],"TCGA-A5-A0GH":[0,"female",0.85],"TCGA-A5-A0GI":[0,"female",0.97],"TCGA-A5-A0GM":[0,"female",0],"TCGA-A5-A0GP":[0,"female",0],"TCGA-A5-A0GQ":[0,"female",0.41],"TCGA-A5-A0R8":[0,"female",0],"TCGA-A5-A0VO":[0,"female",0],"TCGA-A5-A0VP":[0,"female",0.82],"TCGA-AP-A051":[0,"female",0.71],"TCGA-AP-A054":[709,"female",0.95],"TCGA-AP-A056":[0,"female",0.64],"TCGA-AP-A059":[0,"female",0],"TCGA-AP-A05N":[726,"female",0.91],"TCGA-AP-A0LE":[0,"female",0.94],"TCGA-AP-A0LG":[0,"female",0],"TCGA-AP-A0LL":[0,"female",0.93],"TCGA-AP-A0LM":[0,"female",0],"TCGA-AP-A0LT":[0,"female",0],"TCGA-AX-A05T":[0,"female",0.86],"TCGA-AX-A05W":[0,"female",0.69],"TCGA-AX-A05Y":[0,"female",0.98],"TCGA-AX-A05Z":[0,"female",0.53],"TCGA-AX-A060":[0,"female",0.97],"TCGA-AX-A062":[0,"female",0.93],"TCGA-AX-A063":[0,"female",0.77],"TCGA-AX-A0IS":[0,"female",0.84],"TCGA-AX-A0J0":[0,"female",0.9],"TCGA-AX-A0J1":[0,"female",0.77],"TCGA-AX-A2HF":[0,"female",0.9],"TCGA-B5-A0JS":[0,"female",0.58],"TCGA-B5-A0JV":[0,"female",0.85],"TCGA-B5-A0JY":[0,"female",0.66],"TCGA-B5-A0K0":[0,"female",0.71],"TCGA-B5-A0K1":[0,"female",0],"TCGA-B5-A0K4":[0,"female",0.81],"TCGA-B5-A11E":[0,"female",0.81],"TCGA-B5-A11H":[0,"female",0.65],"TCGA-B5-A11M":[0,"female",0],"TCGA-B5-A11O":[0,"female",0],"TCGA-B5-A11R":[0,"female",0.8],"TCGA-B5-A11V":[0,"female",0.85],"TCGA-B5-A11W":[0,"female",0.85],"TCGA-B5-A11X":[0,"female",0],"TCGA-B5-A121":[0,"female",0],"TCGA-BG-A0LX":[0,"female",0.82],"TCGA-BG-A0M3":[0,"female",0],"TCGA-BG-A0M9":[0,"female",0],"TCGA-BG-A0MG":[0,"female",0.77],"TCGA-BG-A0MT":[0,"female",0.82],"TCGA-BG-A0VV":[0,"female",0.64],"TCGA-BG-A0VX":[0,"female",0],"TCGA-BG-A0W1":[0,"female",0.88],"TCGA-BG-A0YV":[0,"female",0.82],"TCGA-BG-A18A":[0,"female",0.62],"TCGA-BG-A18B":[0,"female",0.9],"TCGA-BG-A2AE":[0,"female",0.68],"TCGA-BK-A0CA":[0,"female",0.23],"TCGA-BK-A0CB":[0,"female",0.29],"TCGA-BS-A0TC":[0,"female",0.79],"TCGA-BS-A0TG":[0,"female",0],"TCGA-BS-A0TJ":[0,"female",0],"TCGA-BS-A0U5":[0,"female",0.76],"TCGA-BS-A0U8":[0,"female",0.82],"TCGA-BS-A0U9":[0,"female",0.83],"TCGA-BS-A0UA":[0,"female",0],"TCGA-BS-A0UF":[0,"female",0],"TCGA-BS-A0UJ":[0,"female",0],"TCGA-BS-A0UL":[0,"female",0.4],"TCGA-BS-A0UM":[0,"female",0.68],"TCGA-BS-A0UV":[0,"female",0.67],"TCGA-BS-A0V6":[0,"female",0],"TCGA-BS-A0V8":[0,"female",0.61],"TCGA-D1-A0ZN":[0,"female",0.76],"TCGA-D1-A0ZO":[0,"female",0.58],"TCGA-D1-A0ZS":[0,"female",0],"TCGA-D1-A0ZU":[0,"female",0.67],"TCGA-D1-A101":[0,"female",0],"TCGA-D1-A103":[0,"female",0],"TCGA-D1-A15V":[0,"female",0.78],"TCGA-D1-A15W":[0,"female",0],"TCGA-D1-A15X":[0,"female",0.58],"TCGA-D1-A15Z":[58,"female",0.82],"TCGA-D1-A160":[0,"female",0.71],"TCGA-D1-A167":[0,"female",0.92],"TCGA-D1-A168":[0,"female",0.85],"TCGA-D1-A16B":[0,"female",0],"TCGA-D1-A16D":[0,"female",0.54],"TCGA-D1-A16E":[0,"female",0.57],"TCGA-D1-A16O":[0,"female",0],"TCGA-D1-A16Q":[0,"female",0],"TCGA-D1-A16X":[0,"female",0],"TCGA-D1-A176":[0,"female",0.85],"TCGA-D1-A17A":[0,"female",0.63],"TCGA-D1-A17C":[0,"female",0],"TCGA-D1-A17D":[0,"female",0],"TCGA-D1-A17H":[0,"female",0],"TCGA-D1-A17L":[0,"female",0.73],"TCGA-D1-A17Q":[0,"female",0],"TCGA-D1-A17R":[339,"female",0.85],"TCGA-D1-A17T":[0,"female",0.81],"TCGA-D1-A17U":[0,"female",0.7],"TCGA-BL-A0C8":[0,"male",0],"TCGA-BL-A13I":[223,"female",0.33],"TCGA-BL-A13J":[81,"male",0.55],"TCGA-BL-A3JM":[205,"male",0.76],"TCGA-BT-A0YX":[400,"female",0.49],"TCGA-BT-A20N":[999,"male",0.35],"TCGA-BT-A20Q":[593,"male",0.68],"TCGA-BT-A20R":[154,"female",0.44],"TCGA-BT-A20T":[453,"male",0.75],"TCGA-BT-A2LA":[0,"male",0.95],"TCGA-BT-A2LB":[492,"female",0.5],"TCGA-BT-A2LD":[623,"female",0],"TCGA-BT-A3PH":[142,"male",0.8],"TCGA-BT-A3PJ":[0,"male",0.51],"TCGA-BT-A3PK":[303,"male",0],"TCGA-C4-A0F6":[0,"female",0.68],"TCGA-CF-A1HR":[0,"male",0.4],"TCGA-CF-A3MG":[0,"male",0],"TCGA-CF-A3MI":[0,"male",0.79],"TCGA-CU-A0YO":[149,"male",0],"TCGA-CU-A0YR":[0,"male",0.47],"TCGA-CU-A3KJ":[0,"male",0.74],"TCGA-DK-A1A3":[0,"male",0.42],"TCGA-DK-A1AC":[0,"male",0.68],"TCGA-DK-A1AD":[0,"male",0],"TCGA-DK-A1AE":[0,"male",0.83],"TCGA-DK-A2HX":[0,"female",0.49],"TCGA-DK-A2I4":[0,"male",0.29],"TCGA-DK-A3IK":[0,"male",0.5],"TCGA-DK-A3IM":[0,"male",0],"TCGA-DK-A3IN":[250,"male",0.38],"TCGA-DK-A3IT":[0,"male",0.46],"TCGA-DK-A3IU":[0,"male",0.24],"TCGA-E5-A2PC":[0,"female",0.82],"TCGA-FD-A3B3":[0,"female",0.21],"TCGA-FD-A3B5":[272,"male",0.52],"TCGA-FD-A3N5":[685,"male",0.66],"TCGA-G2-A2EF":[0,"male",0.6],"TCGA-G2-A2EK":[0,"male",0.55],"TCGA-G2-A2EL":[819,"male",0.98],"TCGA-G2-A2EO":[0,"male",0.38],"TCGA-G2-A2ES":[1004,"male",0.55],"TCGA-G2-A3IE":[612,"male",0],"TCGA-GD-A2C5":[0,"female",1],"TCGA-GD-A3OP":[0,"female",0.2],"TCGA-GD-A3OQ":[0,"male",0.78],"TCGA-GV-A3JV":[434,"male",0.67],"TCGA-GV-A3JZ":[0,"male",0.82],"TCGA-H4-A2HO":[0,"male",0.64],"TCGA-04-1342":[563,"female",0.67],"TCGA-04-1343":[361,"female",0.61],"TCGA-04-1367":[0,"female",0.89],"TCGA-09-1661":[1169,"female",0],"TCGA-10-0927":[2490,"female",0.82],"TCGA-10-0930":[1040,"female",0.66],"TCGA-10-0934":[204,"female",0.91],"TCGA-13-0717":[748,"female",0],"TCGA-13-0720":[1355,"female",0.92],"TCGA-13-0755":[76,"female",0.84],"TCGA-13-0760":[351,"female",0.81],"TCGA-13-0762":[0,"female",0.78],"TCGA-13-0900":[0,"female",0.8],"TCGA-13-0912":[0,"female",0.95],"TCGA-13-0913":[0,"female",0.95],"TCGA-13-1411":[0,"female",0.39],"TCGA-23-1123":[1018,"female",0.8],"TCGA-24-1470":[0,"female",0.84],"TCGA-24-2024":[1769,"female",0.9],"TCGA-24-2260":[1100,"female",0.83],"TCGA-25-1319":[1977,"female",0.87],"TCGA-25-1628":[627,"female",0],"TCGA-25-2400":[1278,"female",0],"TCGA-36-1578":[0,"female",0.81],"TCGA-57-1993":[0,"female",0],"TCGA-61-1998":[0,"female",0.72],"TCGA-18-3409":[0,"male",0],"TCGA-18-3412":[345,"male",0],"TCGA-18-3417":[0,"male",0.3],"TCGA-18-3419":[0,"male",0.57],"TCGA-21-1076":[0,"female",0.2],"TCGA-21-1078":[474,"male",0.92],"TCGA-21-5786":[0,"male",0],"TCGA-22-1002":[131,"male",0.21],"TCGA-22-5473":[1933,"male",0.58],"TCGA-33-4566":[5295,"male",0.54],"TCGA-33-6737":[601,"male",0.48],"TCGA-34-2596":[80,"male",0.65],"TCGA-34-5231":[1984,"male",0.67],"TCGA-37-4141":[0,"female",0.75],"TCGA-39-5022":[1679,"male",0],"TCGA-39-5024":[0,"female",0.19],"TCGA-39-5031":[0,"female",0],"TCGA-39-5037":[0,"male",0],"TCGA-39-5039":[544,"male",0.28],"TCGA-43-3394":[0,"male",0],"TCGA-43-3920":[0,"male",0.52],"TCGA-43-5668":[559,"male",0.34],"TCGA-51-4080":[12,"male",0.96],"TCGA-51-4081":[0,"male",0],"TCGA-56-6546":[19,"male",0],"TCGA-60-2698":[311,"male",0.41],"TCGA-60-2720":[0,"female",0.32],"TCGA-63-6202":[0,"male",0.27],"TCGA-66-2744":[0,"male",0.37],"TCGA-66-2754":[0,"male",0.87],"TCGA-66-2756":[0,"male",0.72],"TCGA-66-2757":[0,"female",0.86],"TCGA-66-2766":[0,"male",0.75],"TCGA-66-2782":[365,"male",0],"TCGA-66-2785":[0,"male",0.4],"TCGA-66-2793":[306,"male",0.85],"TCGA-66-2795":[0,"male",0.66],"TCGA-70-6722":[0,"male",0.3],"TCGA-85-6560":[0,"male",0.49],"TCGA-05-4250":[121,"female",0.5],"TCGA-05-4382":[0,"male",0.23],"TCGA-05-4396":[303,"male",0.77],"TCGA-05-4397":[731,"male",0.87],"TCGA-05-4410":[0,"male",0.24],"TCGA-05-4424":[0,"male",0],"TCGA-05-4425":[0,"female",0.61],"TCGA-05-4427":[0,"female",0],"TCGA-05-4432":[0,"male",0],"TCGA-05-5420":[0,"male",0.25],"TCGA-05-5715":[0,"female",0.74],"TCGA-17-Z000":[0,0,0],"TCGA-17-Z017":[0,0,0],"TCGA-17-Z018":[0,0,0],"TCGA-17-Z022":[0,0,0],"TCGA-17-Z026":[0,0,0],"TCGA-17-Z030":[0,0,0],"TCGA-17-Z031":[0,0,0],"TCGA-17-Z055":[0,0,0],"TCGA-17-Z057":[0,0,0],"TCGA-17-Z059":[0,0,0],"TCGA-35-3615":[0,"male",0.69],"TCGA-35-3621":[0,0,0],"TCGA-35-5375":[0,"male",0],"TCGA-38-4629":[864,"male",0.31],"TCGA-38-4631":[354,"female",0],"TCGA-44-2656":[0,"male",0],"TCGA-44-2657":[0,"female",0.34],"TCGA-44-2659":[0,"female",0.41],"TCGA-44-3398":[0,"female",0.24],"TCGA-44-4112":[808,"female",0.55],"TCGA-44-6147":[0,"female",0],"TCGA-44-6776":[0,"female",0.65],"TCGA-49-4514":[0,"female",0.35],"TCGA-49-6742":[0,"male",0.49],"TCGA-49-6743":[0,"female",0.27],"TCGA-49-6767":[0,"female",0.43],"TCGA-50-5072":[250,"male",0],"TCGA-50-5931":[434,"female",0],"TCGA-50-5936":[257,"male",0.44],"TCGA-50-6590":[1288,"female",0.25],"TCGA-50-6592":[777,"female",0.28],"TCGA-55-1596":[0,"male",0.63],"TCGA-55-5899":[0,"male",0.67],"TCGA-55-6543":[0,"female",0.65],"TCGA-64-1678":[0,"female",0.57],"TCGA-64-1680":[0,"male",0.62],"TCGA-64-5775":[62,"male",0.71],"TCGA-67-3770":[0,"female",0],"TCGA-67-3771":[0,"female",0.46],"TCGA-67-6217":[0,"female",0.25],"TCGA-73-4658":[1600,"female",0.35],"TCGA-73-4675":[0,"male",0.57],"TCGA-75-5126":[0,"female",0.29],"TCGA-75-6207":[0,"male",0.72],"TCGA-80-5611":[0,"male",0.51],"TCGA-91-6831":[0,"male",0],"TCGA-AB-2908":[31,"male",0],"TCGA-AB-2991":[0,"female",0],"TCGA-A1-A0SP":[0,"female",0],"TCGA-A2-A04Q":[0,"female",0],"TCGA-A2-A0CU":[158,"female",0.57],"TCGA-A2-A0D2":[0,"female",0.53],"TCGA-A2-A0EQ":[0,"female",0],"TCGA-A2-A0T0":[0,"female",0.57],"TCGA-A8-A06N":[0,"female",0.83],"TCGA-A8-A06Q":[0,"female",0.79],"TCGA-A8-A07I":[0,"female",0.71],"TCGA-A8-A07J":[0,"female",0.6],"TCGA-A8-A07L":[0,"female",0.81],"TCGA-A8-A07R":[0,"female",0.77],"TCGA-AN-A0FW":[0,"female",0.66],"TCGA-AN-A0FX":[0,"female",0.51],"TCGA-AO-A0J2":[0,"female",0.66],"TCGA-AO-A0JL":[0,"female",0.64],"TCGA-AR-A0TX":[0,"female",0.41],"TCGA-AR-A0U0":[0,"female",0.4],"TCGA-B6-A0RG":[0,"female",0.81],"TCGA-B6-A0RV":[0,"female",0.46],"TCGA-B6-A0WT":[0,"female",0.66],"TCGA-B6-A0X5":[2097,"female",0.89],"TCGA-BH-A0B8":[0,"female",0.87],"TCGA-BH-A0E7":[0,"female",0.8],"TCGA-BH-A0EE":[0,"female",0.49],"TCGA-BH-A0HP":[0,"female",0.5],"TCGA-BH-A0HW":[0,"female",0.9],"TCGA-BH-A0W7":[0,"female",0.57],"TCGA-BH-A1EO":[2798,"female",0.48],"TCGA-BH-A1EV":[365,"female",0.61],"TCGA-C8-A12K":[0,"female",0.53],"TCGA-C8-A12T":[0,"female",0.7],"TCGA-C8-A12U":[0,"female",0.62],"TCGA-C8-A1HL":[0,"female",0.69],"TCGA-C8-A1HM":[0,"female",0.42],"TCGA-D8-A1J9":[0,"female",0.52],"TCGA-D8-A1JD":[0,"female",0.5],"TCGA-D8-A1JI":[0,"female",0.81],"TCGA-E2-A10C":[0,"female",0.52],"TCGA-E2-A158":[0,"female",1],"TCGA-E2-A159":[0,"female",0],"TCGA-E2-A1IG":[0,"female",0.77],"TCGA-E2-A1LG":[0,"female",0.77],"TCGA-E9-A1NA":[0,"female",0.61],"TCGA-E9-A1R3":[0,"female",0.48],"TCGA-EW-A1IZ":[0,"female",0.29],"TCGA-EW-A1OX":[0,"female",0.94],"TCGA-BA-4077":[1134,"female",0.73],"TCGA-BA-5149":[806,"male",0.52],"TCGA-BA-5151":[0,"male",0.95],"TCGA-BB-7862":[0,"male",0.8],"TCGA-BB-7863":[0,"female",0.52],"TCGA-BB-7871":[0,"female",0.59],"TCGA-CN-4723":[0,"male",0],"TCGA-CN-4727":[0,"male",0.47],"TCGA-CN-4728":[0,"male",0.24],"TCGA-CN-5355":[0,"male",0.3],"TCGA-CN-5356":[0,"male",0.37],"TCGA-CN-5365":[351,"male",1],"TCGA-CN-5366":[360,"male",0.26],"TCGA-CQ-5326":[89,"male",0.45],"TCGA-CR-6484":[0,"female",0.29],"TCGA-CR-7367":[0,"male",0.34],"TCGA-CR-7371":[94,"female",0.4],"TCGA-CR-7373":[0,"male",0],"TCGA-CR-7374":[0,"female",0.66],"TCGA-CR-7383":[521,"female",0.33],"TCGA-CR-7388":[823,"female",0.4],"TCGA-CR-7394":[0,"male",0.21],"TCGA-CR-7395":[0,"female",0.26],"TCGA-CR-7397":[0,"male",0.18],"TCGA-CR-7402":[0,"male",0.57],"TCGA-CV-5435":[2319,"male",0.67],"TCGA-CV-5441":[0,"male",0.46],"TCGA-CV-5442":[0,"female",0.85],"TCGA-CV-5976":[0,"male",0.56],"TCGA-CV-5978":[215,"female",0.38],"TCGA-CV-6433":[0,"male",0.72],"TCGA-CV-6934":[65,"female",0.41],"TCGA-CV-6960":[862,"male",0.67],"TCGA-CV-6961":[76,"male",0.57],"TCGA-CV-7089":[1972,"male",0.26],"TCGA-CV-7091":[0,"male",0.45],"TCGA-CV-7235":[0,"male",0],"TCGA-CV-7250":[2900,"male",0.65],"TCGA-CV-7252":[151,"female",0.48],"TCGA-CV-7253":[361,"male",0.82],"TCGA-CV-7254":[1459,"male",0.37],"TCGA-CV-7406":[1748,"male",0.6],"TCGA-CV-7422":[1037,"female",0.27],"TCGA-CV-7429":[107,"male",0],"TCGA-CV-7430":[495,"male",0.76],"TCGA-CV-7440":[675,"male",0.45],"TCGA-CX-7219":[0,"male",0],"TCGA-D6-6516":[0,"male",0.51],"TCGA-D6-6826":[348,"female",0.34],"TCGA-DQ-5629":[941,"male",0],"TCGA-DQ-5630":[0,"male",0.27],"TCGA-DQ-7588":[427,"male",0],"TCGA-DQ-7589":[0,"male",0],"TCGA-DQ-7591":[0,"male",0.73],"TCGA-HL-7533":[0,"male",0.63],"TCGA-06-0192":[1185,"male",0],"TCGA-06-1806":[466,"male",0.7],"TCGA-06-5856":[114,"male",0],"TCGA-19-2629":[737,"male",0],"TCGA-27-2521":[0,"male",0],"TCGA-27-2527":[438,"male",0.37],"TCGA-28-5207":[343,"male",0],"TCGA-32-1977":[0,"female",0.53],"TCGA-32-2634":[0,"male",0.89],"TCGA-41-3915":[360,"male",0.77],"TCGA-41-5651":[460,"female",0.96],"TCGA-74-6578":[0,"male",0.7],"TCGA-A6-2672":[0,"female",0.59],"TCGA-A6-2676":[0,"female",0.69],"TCGA-AA-3514":[0,"female",0.78],"TCGA-AA-3516":[0,"female",0.39],"TCGA-AA-3518":[0,"female",0.4],"TCGA-AA-3524":[0,"male",0.8],"TCGA-AA-3529":[0,"female",0.6],"TCGA-AA-3542":[0,"male",0.87],"TCGA-AA-3554":[0,"female",0.39],"TCGA-AA-3555":[0,"female",0.74],"TCGA-AA-3558":[0,"male",0.9],"TCGA-AA-3664":[0,"female",0.87],"TCGA-AA-3672":[0,"female",0.69],"TCGA-AA-3695":[0,"female",0.75],"TCGA-AA-3710":[0,"female",0.9],"TCGA-AA-3715":[0,"male",0.47],"TCGA-AA-3811":[0,"female",0],"TCGA-AA-3831":[0,"male",0.62],"TCGA-AA-3845":[0,"female",0.6],"TCGA-AA-3864":[0,"male",0.75],"TCGA-AA-3867":[0,"male",0.6],"TCGA-AA-3947":[0,"female",0.71],"TCGA-AA-3977":[0,"male",0.72],"TCGA-AA-3984":[0,"female",0.44],"TCGA-AA-A00A":[0,"male",1],"TCGA-AA-A00E":[0,"male",0.54],"TCGA-AA-A00J":[0,"male",0.82],"TCGA-AA-A00L":[0,"male",0.79],"TCGA-AA-A00N":[122,"male",0.63],"TCGA-AA-A010":[0,"female",0.19],"TCGA-AA-A017":[0,"female",0.58],"TCGA-AA-A01Q":[0,"female",0.86],"TCGA-AA-A01R":[0,"male",0],"TCGA-AA-A022":[0,"female",0.7],"TCGA-AA-A02F":[0,"female",0.94],"TCGA-AA-A02W":[0,"female",0.71],"TCGA-AG-3574":[0,"female",0.86],"TCGA-AG-3575":[0,"male",0.74],"TCGA-AG-3878":[0,"male",0.41],"TCGA-AG-3892":[0,"female",0.47],"TCGA-AG-4001":[0,"female",0.48],"TCGA-AG-4007":[0,"male",0.69],"TCGA-AG-A002":[0,"male",1],"TCGA-AG-A008":[0,"female",1],"TCGA-AG-A00C":[0,"female",0.78],"TCGA-AG-A00H":[0,"male",0],"TCGA-AG-A00Y":[0,"male",0],"TCGA-AG-A011":[0,"male",0.55],"TCGA-AG-A025":[0,"female",0.76],"TCGA-AG-A032":[0,"male",0.69]},"annotationToColor":{"Surival (days)":{},"Gender":{"female":"#ff0000","male":"#0000ff"},"ABSOLUTE Purity":{}}} -------------------------------------------------------------------------------- /demo/testData/example-dendrogram-N30.json: -------------------------------------------------------------------------------- 1 | {"labels": ["WLGP24", "HUTC2", "FDKQ34", "YHMU21", "FQOK66", "LXOC20", "DZWJ45", "XKYL48", "RFOT44", "PAYJ59", "DMHX57", "NQTR26", "CJWR18", "GYUX23", "OCJH37", "LSTJ2", "MZLU22", "SCVK90", "MPBC21", "KOPC21", "BJDM4", "LOAU43", "OQRU21", "CWQY53", "YOGV34", "URIN99", "FZVG15", "PVQH51", "MABL80", "AEHY41"], "Z": [[19.0, 18.0, 0.22357146513771842, 2.0], [6.0, 5.0, 0.23164974389492826, 2.0], [17.0, 16.0, 1.096223014768973, 2.0], [11.0, 10.0, 1.1411988462116236, 2.0], [9.0, 8.0, 1.2653626621738814, 2.0], [23.0, 22.0, 1.4081405176681216, 2.0], [21.0, 20.0, 1.440267410283907, 2.0], [15.0, 14.0, 1.440436020826679, 2.0], [35.0, 36.0, 1.472376264573272, 4.0], [4.0, 3.0, 1.6202918831943587, 2.0], [30.0, 32.0, 1.6961680489280437, 4.0], [26.0, 25.0, 1.700241133964007, 2.0], [33.0, 34.0, 1.7483552785868897, 4.0], [12.0, 42.0, 1.8403656219034539, 5.0], [31.0, 39.0, 1.8782725165267031, 4.0], [38.0, 40.0, 1.9582551479370531, 8.0], [24.0, 45.0, 2.065424911007753, 9.0], [27.0, 41.0, 2.2835308946555726, 3.0], [13.0, 43.0, 2.352004580352415, 6.0], [37.0, 48.0, 2.3571807711431276, 8.0], [7.0, 44.0, 2.450443331485584, 5.0], [49.0, 50.0, 2.6019256738370227, 13.0], [46.0, 51.0, 2.614804691058466, 22.0], [1.0, 0.0, 2.7543044170245268, 2.0], [47.0, 52.0, 2.890359803426632, 25.0], [2.0, 53.0, 3.190844454698029, 3.0], [54.0, 55.0, 3.4583137894635243, 28.0], [28.0, 56.0, 4.2554965896875565, 29.0], [29.0, 57.0, 4.823132475699091, 30.0]]} -------------------------------------------------------------------------------- /demo/testData/example-graph.json: -------------------------------------------------------------------------------- 1 | {"title":"Title\nwith a newline","edges":[{"source":"SMC1A","target":"SMC3","weight":1,"categories":["Multinet","iRefIndex","HPRD","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"15657099"}],"HPRD":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"9789013"},{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11076961"}],"HINT":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11076961"}]}},{"source":"SMC3","target":"STAG2","weight":1,"categories":["Multinet","iRefIndex","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"17962804"}],"HINT":[]}},{"source":"SMC3","target":"STAG1","weight":1,"categories":["Multinet","iRefIndex","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"17962804"}],"HINT":[]}},{"source":"SMC1A","target":"STAG2","weight":1,"categories":["Multinet","HINT"],"references":{"Multinet":[],"HINT":[]}},{"source":"SMC1A","target":"STAG1","weight":1,"categories":["Multinet","iRefIndex","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"15657099"}],"HINT":[]}},{"source":"RAD21","target":"SMC1A","weight":1,"categories":["Multinet","iRefIndex","HPRD","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"10931856"}],"HPRD":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"10931856"}],"HINT":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"17113138"}]}},{"source":"RAD21","target":"SMC3","weight":1,"categories":["Multinet","HINT","iRefIndex"],"references":{"Multinet":[],"HINT":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"17113138"}],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"20818333"}]}},{"source":"STAG1","target":"STAG2","weight":1,"categories":["Multinet","iRefIndex","HPRD","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11076961"}],"HPRD":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11076961"}],"HINT":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"17962804"}]}},{"source":"RAD21","target":"STAG1","weight":1,"categories":["Multinet","iRefIndex","HPRD","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"15657099"}],"HPRD":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11590136"}],"HINT":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11590136"}]}},{"source":"RAD21","target":"STAG2","weight":1,"categories":["Multinet","iRefIndex","HPRD","HINT"],"references":{"Multinet":[],"iRefIndex":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"20818333"}],"HPRD":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11590136"},{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"12034751"}],"HINT":[{"downvotes":[],"upvotes":[],"annotation":false,"pmid":"11590136"}]}}],"nodes":[{"name":"STAG2","value":87,"index":0,"weight":8,"x":39.5951066786309,"y":144.9409813262876,"px":39.5951066786309,"py":144.9409813262876,"fixed":1},{"name":"SMC1A","value":62,"index":1,"weight":8,"x":86.57602537116267,"y":79.2586445635246,"px":86.57602537116267,"py":79.2586445635246,"fixed":1},{"name":"STAG1","value":54,"index":2,"weight":8,"x":196.16525565415174,"y":149.42597923040444,"px":196.16525565415174,"py":149.42597923040444,"fixed":1},{"name":"SMC3","value":50,"index":3,"weight":8,"x":131.98027877616215,"y":298.8685642814993,"px":131.98027877616215,"py":298.8685642814993,"fixed":1},{"name":"RAD21","value":41,"index":4,"weight":8,"x":180.62306798095938,"y":234.4132240801091,"px":180.62306798095938,"py":234.4132240801091,"fixed":1}],"refs":{"Multinet":{"SMC1A":{"SMC3":{},"STAG2":{},"STAG1":{}},"SMC3":{"STAG2":{},"STAG1":{}},"RAD21":{"SMC1A":{},"SMC3":{},"STAG1":{},"STAG2":{}},"STAG1":{"STAG2":{}}},"iRefIndex":{"SMC1A":{"SMC3":{"15657099":{"vote":null,"score":0}},"STAG1":{"15657099":{"vote":null,"score":0}}},"SMC3":{"STAG2":{"17962804":{"vote":null,"score":0}},"STAG1":{"17962804":{"vote":null,"score":0}}},"RAD21":{"SMC1A":{"10931856":{"vote":null,"score":0}},"SMC3":{"20818333":{"vote":null,"score":0}},"STAG1":{"15657099":{"vote":null,"score":0}},"STAG2":{"20818333":{"vote":null,"score":0}}},"STAG1":{"STAG2":{"11076961":{"vote":null,"score":0}}}},"HPRD":{"SMC1A":{"SMC3":{"9789013":{"vote":null,"score":0},"11076961":{"vote":null,"score":0}}},"RAD21":{"SMC1A":{"10931856":{"vote":null,"score":0}},"STAG1":{"11590136":{"vote":null,"score":0}},"STAG2":{"11590136":{"vote":null,"score":0},"12034751":{"vote":null,"score":0}}},"STAG1":{"STAG2":{"11076961":{"vote":null,"score":0}}}},"HINT":{"SMC1A":{"SMC3":{"11076961":{"vote":null,"score":0}},"STAG2":{},"STAG1":{}},"SMC3":{"STAG2":{},"STAG1":{}},"RAD21":{"SMC1A":{"17113138":{"vote":null,"score":0}},"SMC3":{"17113138":{"vote":null,"score":0}},"STAG1":{"11590136":{"vote":null,"score":0}},"STAG2":{"11590136":{"vote":null,"score":0}}},"STAG1":{"STAG2":{"17962804":{"vote":null,"score":0}}}}},"comments":{"SMC1A":{"SMC3":{"Multinet":{},"iRefIndex":{"15657099":""},"HPRD":{"9789013":"","11076961":""},"HINT":{"11076961":""}},"STAG2":{"Multinet":{},"HINT":{}},"STAG1":{"Multinet":{},"iRefIndex":{"15657099":""},"HINT":{}}},"SMC3":{"STAG2":{"Multinet":{},"iRefIndex":{"17962804":""},"HINT":{}},"STAG1":{"Multinet":{},"iRefIndex":{"17962804":""},"HINT":{}}},"RAD21":{"SMC1A":{"Multinet":{},"iRefIndex":{"10931856":""},"HPRD":{"10931856":""},"HINT":{"17113138":""}},"SMC3":{"Multinet":{},"HINT":{"17113138":""},"iRefIndex":{"20818333":""}},"STAG1":{"Multinet":{},"iRefIndex":{"15657099":""},"HPRD":{"11590136":""},"HINT":{"11590136":""}},"STAG2":{"Multinet":{},"iRefIndex":{"20818333":""},"HPRD":{"11590136":"","12034751":""},"HINT":{"11590136":""}}},"STAG1":{"STAG2":{"Multinet":{},"iRefIndex":{"11076961":""},"HPRD":{"11076961":""},"HINT":{"17962804":""}}}}} -------------------------------------------------------------------------------- /demo/testData/example-scatterplot.json: -------------------------------------------------------------------------------- 1 | { 2 | "pts": [ 3 | { "x": 1, "y": 1, "category": "Cohort1"}, 4 | { "x": 2, "y": 2, "category": "Cohort2"}, 5 | { "x": 3, "y": 3, "category": "Cohort1"}, 6 | { "x": 4, "y": 4, "category": "Cohort2"}, 7 | { "x": 5, "y": 5, "category": "Cohort1"}, 8 | { "x": 6, "y": 6, "category": "Cohort2"}, 9 | { "x": 7, "y": 7, "category": "Cohort1"}, 10 | { "x": 8, "y": 8, "category": "Cohort2"}, 11 | { "x": 9, "y": 9, "category": "Cohort1"}, 12 | { "x": 10, "y": 10, "category": "Cohort2"} 13 | ], 14 | "title": "Genomics is rad", 15 | "xLabel": "An x-axis label", 16 | "xScale": { "max":10, "min": 0}, 17 | "yLabel": "A y-axis label", 18 | "yScale": { "max": 10, "min": 0} 19 | } -------------------------------------------------------------------------------- /demo/testData/exampleAnnotations.json: -------------------------------------------------------------------------------- 1 | {"categories":["Survival (days)","Gender","ABSOLUTE Purity"],"sampleToAnnotations":{"TCGA-A3-3311":[1191,"male",0.49],"TCGA-A3-3316":[0,"male",0.29],"TCGA-A3-3319":[0,"male",0.39],"TCGA-A3-3320":[0,"female",0.71],"TCGA-A3-3322":[0,"male",0.76],"TCGA-A3-3323":[0,"male",0.56],"TCGA-A3-3362":[0,"female",0.49],"TCGA-A3-3363":[0,"male",0.79],"TCGA-A3-3367":[0,"male",0.72],"TCGA-A3-3372":[0,"male",0.51],"TCGA-A3-3373":[0,"female",0.57],"TCGA-A3-3382":[0,"male",0.63],"TCGA-A3-3387":[0,"male",0],"TCGA-AK-3425":[0,"male",0.45],"TCGA-AK-3431":[0,"female",0.47],"TCGA-AK-3434":[0,"male",0.47],"TCGA-AK-3444":[0,"female",0.66],"TCGA-AK-3445":[0,"male",0.56],"TCGA-AK-3455":[683,"female",0.56],"TCGA-AK-3458":[0,"male",0],"TCGA-B0-4706":[65,"male",0],"TCGA-B0-4718":[0,"male",0.75],"TCGA-B0-4815":[1588,"male",0],"TCGA-B0-4816":[1371,"male",0.43],"TCGA-B0-4817":[1019,"male",0.24],"TCGA-B0-4818":[510,"female",0.48],"TCGA-B0-4822":[1111,"male",0.3],"TCGA-B0-4823":[454,"male",0.56],"TCGA-B0-4824":[1657,"female",0.56],"TCGA-B0-4828":[307,"male",0],"TCGA-B0-4842":[1724,"female",0.21],"TCGA-B0-4843":[354,"male",0.31],"TCGA-B0-4845":[1986,"male",0.64],"TCGA-B0-4847":[793,"male",0.54],"TCGA-B0-4849":[69,"male",0.6],"TCGA-B0-4852":[1121,"female",0],"TCGA-B0-5085":[770,"female",0.68],"TCGA-B0-5094":[333,"male",0.68],"TCGA-B0-5096":[68,"female",0.43],"TCGA-B0-5102":[2764,"female",0.63],"TCGA-B0-5104":[2752,"female",0.55],"TCGA-B0-5107":[927,"female",0.62],"TCGA-B0-5110":[0,"female",0.53],"TCGA-B0-5115":[0,"male",0.64],"TCGA-B0-5116":[0,"male",0.56],"TCGA-B0-5120":[0,"female",0.49],"TCGA-B0-5399":[0,"male",0],"TCGA-B0-5692":[0,"female",0],"TCGA-B0-5693":[0,"female",0],"TCGA-B0-5694":[480,"male",0],"TCGA-B0-5696":[0,"male",0],"TCGA-B0-5698":[0,"male",0.71],"TCGA-B0-5699":[0,"male",0.74],"TCGA-B0-5702":[0,"male",0],"TCGA-B0-5703":[0,"male",0],"TCGA-B0-5705":[0,"female",0],"TCGA-B0-5710":[0,"male",0.57],"TCGA-B0-5711":[0,"male",0.6],"TCGA-B0-5713":[0,"female",0.59],"TCGA-B2-4099":[0,"male",0.64],"TCGA-B2-4101":[0,"male",0.47],"TCGA-B4-5377":[0,"female",0],"TCGA-B8-4148":[0,"female",0.55],"TCGA-B8-4153":[0,"male",0.72],"TCGA-B8-4154":[0,"female",0],"TCGA-B8-5158":[0,"male",0],"TCGA-B8-5549":[0,"male",0],"TCGA-BP-4158":[0,"male",0.62],"TCGA-BP-4159":[2601,"male",0.6],"TCGA-BP-4164":[992,"female",0.73],"TCGA-BP-4176":[0,"male",0.56],"TCGA-BP-4329":[845,"male",0.56],"TCGA-BP-4337":[2,"female",0.35],"TCGA-BP-4340":[562,"female",0.73],"TCGA-BP-4759":[0,"male",0],"TCGA-BP-4774":[0,"female",0.52],"TCGA-BP-4801":[0,"male",0.49],"TCGA-BP-4803":[0,"male",0],"TCGA-BP-4960":[0,"male",0],"TCGA-BP-4963":[0,"male",0.54],"TCGA-BP-4964":[0,"female",0.73],"TCGA-BP-4973":[0,"male",0.68],"TCGA-BP-4975":[0,"male",0.72],"TCGA-BP-4981":[1097,"female",0.79],"TCGA-BP-4982":[0,"male",0.46],"TCGA-BP-4985":[952,"male",0.44],"TCGA-BP-4987":[0,"female",0.68],"TCGA-BP-4988":[828,"male",0.23],"TCGA-BP-4989":[0,"male",0.46],"TCGA-BP-4992":[0,"male",0.38],"TCGA-BP-4993":[0,"male",0.66],"TCGA-BP-4995":[0,"male",0.7],"TCGA-BP-4999":[0,"male",0.48],"TCGA-BP-5168":[1463,"male",0.6],"TCGA-BP-5169":[0,"male",0.4],"TCGA-BP-5170":[0,"male",0.49],"TCGA-BP-5180":[0,"male",0.76],"TCGA-BP-5183":[0,"male",0.68],"TCGA-BP-5184":[0,"male",0.56],"TCGA-BP-5186":[0,"female",0.57],"TCGA-BP-5187":[0,"male",0.42],"TCGA-BP-5189":[822,"male",0.52],"TCGA-BP-5198":[0,"male",0.43],"TCGA-BP-5199":[0,"male",0.49],"TCGA-BP-5200":[0,"male",0.42],"TCGA-CJ-4634":[0,"female",0.75],"TCGA-CJ-4635":[0,"male",0.51],"TCGA-CJ-4644":[336,"female",0],"TCGA-CJ-4871":[0,"male",0],"TCGA-CJ-4872":[0,"male",0],"TCGA-CJ-4875":[0,"male",0.75],"TCGA-CJ-4876":[0,"male",0.62],"TCGA-CJ-4884":[0,"female",0.45],"TCGA-CJ-4887":[932,"male",0.45],"TCGA-CJ-4888":[1567,"male",0],"TCGA-CJ-4892":[0,"female",0.52],"TCGA-CJ-4903":[0,"male",0.68],"TCGA-CJ-4905":[0,"female",0.57],"TCGA-CJ-4908":[0,"male",0.46],"TCGA-CJ-4912":[0,"male",0.64],"TCGA-CJ-4913":[1173,"female",0.59],"TCGA-CJ-4920":[139,"female",0.56],"TCGA-CJ-4923":[572,"female",0.34],"TCGA-CJ-5676":[0,"male",0],"TCGA-CJ-5678":[574,"male",0],"TCGA-CJ-5679":[679,"male",0],"TCGA-CJ-5682":[0,"male",0],"TCGA-CJ-5684":[0,"male",0],"TCGA-CJ-6030":[2298,"male",0.49],"TCGA-CJ-6032":[0,"female",0.57],"TCGA-CJ-6033":[224,"female",0.37],"TCGA-CW-5580":[1964,"female",0.63],"TCGA-CW-5589":[0,"male",0],"TCGA-CW-6093":[0,"male",0.56],"TCGA-CZ-4853":[0,"male",0.78],"TCGA-CZ-4854":[1404,"male",0],"TCGA-CZ-4857":[1432,"male",0.57],"TCGA-CZ-4861":[446,"male",0.72],"TCGA-CZ-5453":[0,"male",0],"TCGA-CZ-5454":[722,"male",0],"TCGA-CZ-5455":[561,"male",0.51],"TCGA-CZ-5456":[0,"male",0],"TCGA-CZ-5457":[0,"male",0.46],"TCGA-CZ-5460":[0,"male",0],"TCGA-CZ-5461":[330,"male",0],"TCGA-CZ-5465":[0,"female",0],"TCGA-CZ-5466":[0,"male",0],"TCGA-CZ-5470":[0,"female",0],"TCGA-CZ-5982":[0,"female",0.62],"TCGA-CZ-5986":[0,"male",0.68],"TCGA-CZ-5989":[0,"male",0.47],"TCGA-DV-5566":[0,"female",0.59],"TCGA-EU-5907":[null,"male",0.87],"TCGA-A5-A0GB":[0,"female",0.89],"TCGA-A5-A0GH":[0,"female",0.85],"TCGA-A5-A0GI":[0,"female",0.97],"TCGA-A5-A0GM":[0,"female",0],"TCGA-A5-A0GP":[0,"female",0],"TCGA-A5-A0GQ":[0,"female",0.41],"TCGA-A5-A0R8":[0,"female",0],"TCGA-A5-A0VO":[0,"female",0],"TCGA-A5-A0VP":[0,"female",0.82],"TCGA-AP-A051":[0,"female",0.71],"TCGA-AP-A054":[709,"female",0.95],"TCGA-AP-A056":[0,"female",0.64],"TCGA-AP-A059":[0,"female",0],"TCGA-AP-A05N":[726,"female",0.91],"TCGA-AP-A0LE":[0,"female",0.94],"TCGA-AP-A0LG":[0,"female",0],"TCGA-AP-A0LL":[0,"female",0.93],"TCGA-AP-A0LM":[0,"female",0],"TCGA-AP-A0LT":[0,"female",0],"TCGA-AX-A05T":[0,"female",0.86],"TCGA-AX-A05W":[0,"female",0.69],"TCGA-AX-A05Y":[0,"female",0.98],"TCGA-AX-A05Z":[0,"female",0.53],"TCGA-AX-A060":[0,"female",0.97],"TCGA-AX-A062":[0,"female",0.93],"TCGA-AX-A063":[0,"female",0.77],"TCGA-AX-A0IS":[0,"female",0.84],"TCGA-AX-A0J0":[0,"female",0.9],"TCGA-AX-A0J1":[0,"female",0.77],"TCGA-AX-A2HF":[0,"female",0.9],"TCGA-B5-A0JS":[0,"female",0.58],"TCGA-B5-A0JV":[0,"female",0.85],"TCGA-B5-A0JY":[0,"female",0.66],"TCGA-B5-A0K0":[0,"female",0.71],"TCGA-B5-A0K1":[0,"female",0],"TCGA-B5-A0K4":[0,"female",0.81],"TCGA-B5-A11E":[0,"female",0.81],"TCGA-B5-A11H":[0,"female",0.65],"TCGA-B5-A11M":[0,"female",0],"TCGA-B5-A11O":[0,"female",0],"TCGA-B5-A11R":[0,"female",0.8],"TCGA-B5-A11V":[0,"female",0.85],"TCGA-B5-A11W":[0,"female",0.85],"TCGA-B5-A11X":[0,"female",0],"TCGA-B5-A121":[0,"female",0],"TCGA-BG-A0LX":[0,"female",0.82],"TCGA-BG-A0M3":[0,"female",0],"TCGA-BG-A0M9":[0,"female",0],"TCGA-BG-A0MG":[0,"female",0.77],"TCGA-BG-A0MT":[0,"female",0.82],"TCGA-BG-A0VV":[0,"female",0.64],"TCGA-BG-A0VX":[0,"female",0],"TCGA-BG-A0W1":[0,"female",0.88],"TCGA-BG-A0YV":[0,"female",0.82],"TCGA-BG-A18A":[0,"female",0.62],"TCGA-BG-A18B":[0,"female",0.9],"TCGA-BG-A2AE":[0,"female",0.68],"TCGA-BK-A0CA":[0,"female",0.23],"TCGA-BK-A0CB":[0,"female",0.29],"TCGA-BS-A0TC":[0,"female",0.79],"TCGA-BS-A0TG":[0,"female",0],"TCGA-BS-A0TJ":[0,"female",0],"TCGA-BS-A0U5":[0,"female",0.76],"TCGA-BS-A0U8":[0,"female",0.82],"TCGA-BS-A0U9":[0,"female",0.83],"TCGA-BS-A0UA":[0,"female",0],"TCGA-BS-A0UF":[0,"female",0],"TCGA-BS-A0UJ":[0,"female",0],"TCGA-BS-A0UL":[0,"female",0.4],"TCGA-BS-A0UM":[0,"female",0.68],"TCGA-BS-A0UV":[0,"female",0.67],"TCGA-BS-A0V6":[0,"female",0],"TCGA-BS-A0V8":[0,"female",0.61],"TCGA-D1-A0ZN":[0,"female",0.76],"TCGA-D1-A0ZO":[0,"female",0.58],"TCGA-D1-A0ZS":[0,"female",0],"TCGA-D1-A0ZU":[0,"female",0.67],"TCGA-D1-A101":[0,"female",0],"TCGA-D1-A103":[0,"female",0],"TCGA-D1-A15V":[0,"female",0.78],"TCGA-D1-A15W":[0,"female",0],"TCGA-D1-A15X":[0,"female",0.58],"TCGA-D1-A15Z":[58,"female",0.82],"TCGA-D1-A160":[0,"female",0.71],"TCGA-D1-A167":[0,"female",0.92],"TCGA-D1-A168":[0,"female",0.85],"TCGA-D1-A16B":[0,"female",0],"TCGA-D1-A16D":[0,"female",0.54],"TCGA-D1-A16E":[0,"female",0.57],"TCGA-D1-A16O":[0,"female",0],"TCGA-D1-A16Q":[0,"female",0],"TCGA-D1-A16X":[0,"female",0],"TCGA-D1-A176":[0,"female",0.85],"TCGA-D1-A17A":[0,"female",0.63],"TCGA-D1-A17C":[0,"female",0],"TCGA-D1-A17D":[0,"female",0],"TCGA-D1-A17H":[0,"female",0],"TCGA-D1-A17L":[0,"female",0.73],"TCGA-D1-A17Q":[0,"female",0],"TCGA-D1-A17R":[339,"female",0.85],"TCGA-D1-A17T":[0,"female",0.81],"TCGA-D1-A17U":[0,"female",0.7],"TCGA-BL-A0C8":[0,"male",0],"TCGA-BL-A13I":[223,"female",0.33],"TCGA-BL-A13J":[81,"male",0.55],"TCGA-BL-A3JM":[205,"male",0.76],"TCGA-BT-A0YX":[400,"female",0.49],"TCGA-BT-A20N":[999,"male",0.35],"TCGA-BT-A20Q":[593,"male",0.68],"TCGA-BT-A20R":[154,"female",0.44],"TCGA-BT-A20T":[453,"male",0.75],"TCGA-BT-A2LA":[0,"male",0.95],"TCGA-BT-A2LB":[492,"female",0.5],"TCGA-BT-A2LD":[623,"female",0],"TCGA-BT-A3PH":[142,"male",0.8],"TCGA-BT-A3PJ":[0,"male",0.51],"TCGA-BT-A3PK":[303,"male",0],"TCGA-C4-A0F6":[0,"female",0.68],"TCGA-CF-A1HR":[0,"male",0.4],"TCGA-CF-A3MG":[0,"male",0],"TCGA-CF-A3MI":[0,"male",0.79],"TCGA-CU-A0YO":[149,"male",0],"TCGA-CU-A0YR":[0,"male",0.47],"TCGA-CU-A3KJ":[0,"male",0.74],"TCGA-DK-A1A3":[0,"male",0.42],"TCGA-DK-A1AC":[0,"male",0.68],"TCGA-DK-A1AD":[0,"male",0],"TCGA-DK-A1AE":[0,"male",0.83],"TCGA-DK-A2HX":[0,"female",0.49],"TCGA-DK-A2I4":[0,"male",0.29],"TCGA-DK-A3IK":[0,"male",0.5],"TCGA-DK-A3IM":[0,"male",0],"TCGA-DK-A3IN":[250,"male",0.38],"TCGA-DK-A3IT":[0,"male",0.46],"TCGA-DK-A3IU":[0,"male",0.24],"TCGA-E5-A2PC":[0,"female",0.82],"TCGA-FD-A3B3":[0,"female",0.21],"TCGA-FD-A3B5":[272,"male",0.52],"TCGA-FD-A3N5":[685,"male",0.66],"TCGA-G2-A2EF":[0,"male",0.6],"TCGA-G2-A2EK":[0,"male",0.55],"TCGA-G2-A2EL":[819,"male",0.98],"TCGA-G2-A2EO":[0,"male",0.38],"TCGA-G2-A2ES":[1004,"male",0.55],"TCGA-G2-A3IE":[612,"male",0],"TCGA-GD-A2C5":[0,"female",1],"TCGA-GD-A3OP":[0,"female",0.2],"TCGA-GD-A3OQ":[0,"male",0.78],"TCGA-GV-A3JV":[434,"male",0.67],"TCGA-GV-A3JZ":[0,"male",0.82],"TCGA-H4-A2HO":[0,"male",0.64],"TCGA-04-1342":[563,"female",0.67],"TCGA-04-1343":[361,"female",0.61],"TCGA-04-1367":[0,"female",0.89],"TCGA-09-1661":[1169,"female",0],"TCGA-10-0927":[2490,"female",0.82],"TCGA-10-0930":[1040,"female",0.66],"TCGA-10-0934":[204,"female",0.91],"TCGA-13-0717":[748,"female",0],"TCGA-13-0720":[1355,"female",0.92],"TCGA-13-0755":[76,"female",0.84],"TCGA-13-0760":[351,"female",0.81],"TCGA-13-0762":[0,"female",0.78],"TCGA-13-0900":[0,"female",0.8],"TCGA-13-0912":[0,"female",0.95],"TCGA-13-0913":[0,"female",0.95],"TCGA-13-1411":[0,"female",0.39],"TCGA-23-1123":[1018,"female",0.8],"TCGA-24-1470":[0,"female",0.84],"TCGA-24-2024":[1769,"female",0.9],"TCGA-24-2260":[1100,"female",0.83],"TCGA-25-1319":[1977,"female",0.87],"TCGA-25-1628":[627,"female",0],"TCGA-25-2400":[1278,"female",0],"TCGA-36-1578":[0,"female",0.81],"TCGA-57-1993":[0,"female",0],"TCGA-61-1998":[0,"female",0.72],"TCGA-18-3409":[0,"male",0],"TCGA-18-3412":[345,"male",0],"TCGA-18-3417":[0,"male",0.3],"TCGA-18-3419":[0,"male",0.57],"TCGA-21-1076":[0,"female",0.2],"TCGA-21-1078":[474,"male",0.92],"TCGA-21-5786":[0,"male",0],"TCGA-22-1002":[131,"male",0.21],"TCGA-22-5473":[1933,"male",0.58],"TCGA-33-4566":[5295,"male",0.54],"TCGA-33-6737":[601,"male",0.48],"TCGA-34-2596":[80,"male",0.65],"TCGA-34-5231":[1984,"male",0.67],"TCGA-37-4141":[0,"female",0.75],"TCGA-39-5022":[1679,"male",0],"TCGA-39-5024":[0,"female",0.19],"TCGA-39-5031":[0,"female",0],"TCGA-39-5037":[0,"male",0],"TCGA-39-5039":[544,"male",0.28],"TCGA-43-3394":[0,"male",0],"TCGA-43-3920":[0,"male",0.52],"TCGA-43-5668":[559,"male",0.34],"TCGA-51-4080":[12,"male",0.96],"TCGA-51-4081":[0,"male",0],"TCGA-56-6546":[19,"male",0],"TCGA-60-2698":[311,"male",0.41],"TCGA-60-2720":[0,"female",0.32],"TCGA-63-6202":[0,"male",0.27],"TCGA-66-2744":[0,"male",0.37],"TCGA-66-2754":[0,"male",0.87],"TCGA-66-2756":[0,"male",0.72],"TCGA-66-2757":[0,"female",0.86],"TCGA-66-2766":[0,"male",0.75],"TCGA-66-2782":[365,"male",0],"TCGA-66-2785":[0,"male",0.4],"TCGA-66-2793":[306,"male",0.85],"TCGA-66-2795":[0,"male",0.66],"TCGA-70-6722":[0,"male",0.3],"TCGA-85-6560":[0,"male",0.49],"TCGA-05-4250":[121,"female",0.5],"TCGA-05-4382":[0,"male",0.23],"TCGA-05-4396":[303,"male",0.77],"TCGA-05-4397":[731,"male",0.87],"TCGA-05-4410":[0,"male",0.24],"TCGA-05-4424":[0,"male",0],"TCGA-05-4425":[0,"female",0.61],"TCGA-05-4427":[0,"female",0],"TCGA-05-4432":[0,"male",0],"TCGA-05-5420":[0,"male",0.25],"TCGA-05-5715":[0,"female",0.74],"TCGA-17-Z000":[0,0,0],"TCGA-17-Z017":[0,0,0],"TCGA-17-Z018":[0,0,0],"TCGA-17-Z022":[0,0,0],"TCGA-17-Z026":[0,0,0],"TCGA-17-Z030":[0,0,0],"TCGA-17-Z031":[0,0,0],"TCGA-17-Z055":[0,0,0],"TCGA-17-Z057":[0,0,0],"TCGA-17-Z059":[0,0,0],"TCGA-35-3615":[0,"male",0.69],"TCGA-35-3621":[0,0,0],"TCGA-35-5375":[0,"male",0],"TCGA-38-4629":[864,"male",0.31],"TCGA-38-4631":[354,"female",0],"TCGA-44-2656":[0,"male",0],"TCGA-44-2657":[0,"female",0.34],"TCGA-44-2659":[0,"female",0.41],"TCGA-44-3398":[0,"female",0.24],"TCGA-44-4112":[808,"female",0.55],"TCGA-44-6147":[0,"female",0],"TCGA-44-6776":[0,"female",0.65],"TCGA-49-4514":[0,"female",0.35],"TCGA-49-6742":[0,"male",0.49],"TCGA-49-6743":[0,"female",0.27],"TCGA-49-6767":[0,"female",0.43],"TCGA-50-5072":[250,"male",0],"TCGA-50-5931":[434,"female",0],"TCGA-50-5936":[257,"male",0.44],"TCGA-50-6590":[1288,"female",0.25],"TCGA-50-6592":[777,"female",0.28],"TCGA-55-1596":[0,"male",0.63],"TCGA-55-5899":[0,"male",0.67],"TCGA-55-6543":[0,"female",0.65],"TCGA-64-1678":[0,"female",0.57],"TCGA-64-1680":[0,"male",0.62],"TCGA-64-5775":[62,"male",0.71],"TCGA-67-3770":[0,"female",0],"TCGA-67-3771":[0,"female",0.46],"TCGA-67-6217":[0,"female",0.25],"TCGA-73-4658":[1600,"female",0.35],"TCGA-73-4675":[0,"male",0.57],"TCGA-75-5126":[0,"female",0.29],"TCGA-75-6207":[0,"male",0.72],"TCGA-80-5611":[0,"male",0.51],"TCGA-91-6831":[0,"male",0],"TCGA-AB-2908":[31,"male",0],"TCGA-AB-2991":[0,"female",0],"TCGA-A1-A0SP":[0,"female",0],"TCGA-A2-A04Q":[0,"female",0],"TCGA-A2-A0CU":[158,"female",0.57],"TCGA-A2-A0D2":[0,"female",0.53],"TCGA-A2-A0EQ":[0,"female",0],"TCGA-A2-A0T0":[0,"female",0.57],"TCGA-A8-A06N":[0,"female",0.83],"TCGA-A8-A06Q":[0,"female",0.79],"TCGA-A8-A07I":[0,"female",0.71],"TCGA-A8-A07J":[0,"female",0.6],"TCGA-A8-A07L":[0,"female",0.81],"TCGA-A8-A07R":[0,"female",0.77],"TCGA-AN-A0FW":[0,"female",0.66],"TCGA-AN-A0FX":[0,"female",0.51],"TCGA-AO-A0J2":[0,"female",0.66],"TCGA-AO-A0JL":[0,"female",0.64],"TCGA-AR-A0TX":[0,"female",0.41],"TCGA-AR-A0U0":[0,"female",0.4],"TCGA-B6-A0RG":[0,"female",0.81],"TCGA-B6-A0RV":[0,"female",0.46],"TCGA-B6-A0WT":[0,"female",0.66],"TCGA-B6-A0X5":[2097,"female",0.89],"TCGA-BH-A0B8":[0,"female",0.87],"TCGA-BH-A0E7":[0,"female",0.8],"TCGA-BH-A0EE":[0,"female",0.49],"TCGA-BH-A0HP":[0,"female",0.5],"TCGA-BH-A0HW":[0,"female",0.9],"TCGA-BH-A0W7":[0,"female",0.57],"TCGA-BH-A1EO":[2798,"female",0.48],"TCGA-BH-A1EV":[365,"female",0.61],"TCGA-C8-A12K":[0,"female",0.53],"TCGA-C8-A12T":[0,"female",0.7],"TCGA-C8-A12U":[0,"female",0.62],"TCGA-C8-A1HL":[0,"female",0.69],"TCGA-C8-A1HM":[0,"female",0.42],"TCGA-D8-A1J9":[0,"female",0.52],"TCGA-D8-A1JD":[0,"female",0.5],"TCGA-D8-A1JI":[0,"female",0.81],"TCGA-E2-A10C":[0,"female",0.52],"TCGA-E2-A158":[0,"female",1],"TCGA-E2-A159":[0,"female",0],"TCGA-E2-A1IG":[0,"female",0.77],"TCGA-E2-A1LG":[0,"female",0.77],"TCGA-E9-A1NA":[0,"female",0.61],"TCGA-E9-A1R3":[0,"female",0.48],"TCGA-EW-A1IZ":[0,"female",0.29],"TCGA-EW-A1OX":[0,"female",0.94],"TCGA-BA-4077":[1134,"female",0.73],"TCGA-BA-5149":[806,"male",0.52],"TCGA-BA-5151":[0,"male",0.95],"TCGA-BB-7862":[0,"male",0.8],"TCGA-BB-7863":[0,"female",0.52],"TCGA-BB-7871":[0,"female",0.59],"TCGA-CN-4723":[0,"male",0],"TCGA-CN-4727":[0,"male",0.47],"TCGA-CN-4728":[0,"male",0.24],"TCGA-CN-5355":[0,"male",0.3],"TCGA-CN-5356":[0,"male",0.37],"TCGA-CN-5365":[351,"male",1],"TCGA-CN-5366":[360,"male",0.26],"TCGA-CQ-5326":[89,"male",0.45],"TCGA-CR-6484":[0,"female",0.29],"TCGA-CR-7367":[0,"male",0.34],"TCGA-CR-7371":[94,"female",0.4],"TCGA-CR-7373":[0,"male",0],"TCGA-CR-7374":[0,"female",0.66],"TCGA-CR-7383":[521,"female",0.33],"TCGA-CR-7388":[823,"female",0.4],"TCGA-CR-7394":[0,"male",0.21],"TCGA-CR-7395":[0,"female",0.26],"TCGA-CR-7397":[0,"male",0.18],"TCGA-CR-7402":[0,"male",0.57],"TCGA-CV-5435":[2319,"male",0.67],"TCGA-CV-5441":[0,"male",0.46],"TCGA-CV-5442":[0,"female",0.85],"TCGA-CV-5976":[0,"male",0.56],"TCGA-CV-5978":[215,"female",0.38],"TCGA-CV-6433":[0,"male",0.72],"TCGA-CV-6934":[65,"female",0.41],"TCGA-CV-6960":[862,"male",0.67],"TCGA-CV-6961":[76,"male",0.57],"TCGA-CV-7089":[1972,"male",0.26],"TCGA-CV-7091":[0,"male",0.45],"TCGA-CV-7235":[0,"male",0],"TCGA-CV-7250":[2900,"male",0.65],"TCGA-CV-7252":[151,"female",0.48],"TCGA-CV-7253":[361,"male",0.82],"TCGA-CV-7254":[1459,"male",0.37],"TCGA-CV-7406":[1748,"male",0.6],"TCGA-CV-7422":[1037,"female",0.27],"TCGA-CV-7429":[107,"male",0],"TCGA-CV-7430":[495,"male",0.76],"TCGA-CV-7440":[675,"male",0.45],"TCGA-CX-7219":[0,"male",0],"TCGA-D6-6516":[0,"male",0.51],"TCGA-D6-6826":[348,"female",0.34],"TCGA-DQ-5629":[941,"male",0],"TCGA-DQ-5630":[0,"male",0.27],"TCGA-DQ-7588":[427,"male",0],"TCGA-DQ-7589":[0,"male",0],"TCGA-DQ-7591":[0,"male",0.73],"TCGA-HL-7533":[0,"male",0.63],"TCGA-06-0192":[1185,"male",0],"TCGA-06-1806":[466,"male",0.7],"TCGA-06-5856":[114,"male",0],"TCGA-19-2629":[737,"male",0],"TCGA-27-2521":[0,"male",0],"TCGA-27-2527":[438,"male",0.37],"TCGA-28-5207":[343,"male",0],"TCGA-32-1977":[0,"female",0.53],"TCGA-32-2634":[0,"male",0.89],"TCGA-41-3915":[360,"male",0.77],"TCGA-41-5651":[460,"female",0.96],"TCGA-74-6578":[0,"male",0.7],"TCGA-A6-2672":[0,"female",0.59],"TCGA-A6-2676":[0,"female",0.69],"TCGA-AA-3514":[0,"female",0.78],"TCGA-AA-3516":[0,"female",0.39],"TCGA-AA-3518":[0,"female",0.4],"TCGA-AA-3524":[0,"male",0.8],"TCGA-AA-3529":[0,"female",0.6],"TCGA-AA-3542":[0,"male",0.87],"TCGA-AA-3554":[0,"female",0.39],"TCGA-AA-3555":[0,"female",0.74],"TCGA-AA-3558":[0,"male",0.9],"TCGA-AA-3664":[0,"female",0.87],"TCGA-AA-3672":[0,"female",0.69],"TCGA-AA-3695":[0,"female",0.75],"TCGA-AA-3710":[0,"female",0.9],"TCGA-AA-3715":[0,"male",0.47],"TCGA-AA-3811":[0,"female",0],"TCGA-AA-3831":[0,"male",0.62],"TCGA-AA-3845":[0,"female",0.6],"TCGA-AA-3864":[0,"male",0.75],"TCGA-AA-3867":[0,"male",0.6],"TCGA-AA-3947":[0,"female",0.71],"TCGA-AA-3977":[0,"male",0.72],"TCGA-AA-3984":[0,"female",0.44],"TCGA-AA-A00A":[0,"male",1],"TCGA-AA-A00E":[0,"male",0.54],"TCGA-AA-A00J":[0,"male",0.82],"TCGA-AA-A00L":[0,"male",0.79],"TCGA-AA-A00N":[122,"male",0.63],"TCGA-AA-A010":[0,"female",0.19],"TCGA-AA-A017":[0,"female",0.58],"TCGA-AA-A01Q":[0,"female",0.86],"TCGA-AA-A01R":[0,"male",0],"TCGA-AA-A022":[0,"female",0.7],"TCGA-AA-A02F":[0,"female",0.94],"TCGA-AA-A02W":[0,"female",0.71],"TCGA-AG-3574":[0,"female",0.86],"TCGA-AG-3575":[0,"male",0.74],"TCGA-AG-3878":[0,"male",0.41],"TCGA-AG-3892":[0,"female",0.47],"TCGA-AG-4001":[0,"female",0.48],"TCGA-AG-4007":[0,"male",0.69],"TCGA-AG-A002":[0,"male",1],"TCGA-AG-A008":[0,"female",1],"TCGA-AG-A00C":[0,"female",0.78],"TCGA-AG-A00H":[0,"male",0],"TCGA-AG-A00Y":[0,"male",0],"TCGA-AG-A011":[0,"male",0.55],"TCGA-AG-A025":[0,"female",0.76],"TCGA-AG-A032":[0,"male",0.69]},"annotationToColor":{"Survival (days)":{},"Gender":{"female":"#ff0000","male":"#0000ff"},"ABSOLUTE Purity":{}}} -------------------------------------------------------------------------------- /demo/testData/extractAnnotationfromwhole.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open('entireJson.json') as dataFile: 4 | data = json.load(dataFile) 5 | 6 | print data.keys() 7 | 8 | antData = data['annotations'] 9 | with open('example-annotation.json', 'w') as outfile: 10 | json.dump(antData, outfile) -------------------------------------------------------------------------------- /demo/testData/extractCNAfromwhole.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open('entireJson.json') as dataFile: 4 | data = json.load(dataFile) 5 | 6 | cnaData = data['cna_browser_data'] 7 | with open('example-cna.json', 'w') as outfile: 8 | json.dump(cnaData, outfile) -------------------------------------------------------------------------------- /demo/testData/extractMutMtxfromwhole.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open('entireJson.json') as dataFile: 4 | data = json.load(dataFile) 5 | 6 | print data.keys() 7 | 8 | mtxData = data['mutation_matrix'] 9 | with open('example2-mutation-matrix.json', 'w') as outfile: 10 | json.dump(mtxData, outfile) -------------------------------------------------------------------------------- /demo/testData/extractTranscriptfromwhole.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open('entireJson.json') as dataFile: 4 | data = json.load(dataFile) 5 | 6 | tData = data['transcript_data'] 7 | with open('example2-transcript.json', 'w') as outfile: 8 | json.dump(tData, outfile) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gd3prime", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gd3.js", 6 | "dependencies": { 7 | "uglify-js": "~2.4.15", 8 | "smash": "~0.0.12", 9 | "bower": "latest" 10 | }, 11 | "devDependencies": {}, 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1", 14 | "postinstall": "./node_modules/bower/bin/bower install" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/connorgr/gd3prime.git" 19 | }, 20 | "author": "", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/connorgr/gd3prime/issues" 24 | }, 25 | "homepage": "https://github.com/connorgr/gd3prime" 26 | } 27 | -------------------------------------------------------------------------------- /src/cna/cna.js: -------------------------------------------------------------------------------- 1 | import "cnaChart"; 2 | import "cnaStyle"; 3 | 4 | gd3.cna = function(params) { 5 | var params = params || {}, 6 | style = cnaStyle(params.style || {}); 7 | 8 | // cnaChart functions as a partial application, binding the given variables 9 | // into the returned instance. 10 | return cnaChart(style); 11 | }; -------------------------------------------------------------------------------- /src/cna/cnaData.js: -------------------------------------------------------------------------------- 1 | function cnaData(data) { 2 | function braph(cdata) { 3 | var gene = cdata.gene || "", 4 | geneinfo = cdata.neighbors || [], 5 | region = cdata.region || {} 6 | samplesToTypes = cdata.sampleToTypes || {}, 7 | seg = cdata.segments; 8 | 9 | var chrm = region.chr, 10 | allmin = 0, 11 | allmax = 0, 12 | minSegXLoc = region.minSegX, 13 | maxSegXLoc = region.maxSegX; 14 | 15 | // Initialize data structures 16 | var geneJSON = geneinfo.map(function(d) { 17 | var selected = d.name == gene; 18 | return { fixed: selected ? true: false , start: d.start, end: d.end, label: d.name, selected: selected }; 19 | }); 20 | 21 | var sampleTypes = [], 22 | samplelst = [], 23 | segJSON = []; 24 | 25 | // Flatten the segments data 26 | seg.forEach(function(d){ 27 | samplelst.push( d.sample ); 28 | 29 | var dSegments = d.segments; 30 | dSegments.forEach(function(s){ 31 | // create simulated annotation data if it does not exist. 32 | // var vote = { 33 | // type: 'vote', 34 | // score: 100 35 | // } 36 | // var link = { 37 | // type: 'link', 38 | // href: 'http://www.cs.brown.edu', 39 | // text: 'BrownCS' 40 | // } 41 | // var testAnnotation = [ 42 | // { 43 | // type: 'text', 44 | // title: 'Sample', 45 | // text: d.sample 46 | // }, 47 | // { 48 | // type: 'table', 49 | // header: ['Cancer', 'PMIDs', 'Votes'], 50 | // data: [ 51 | // ['1', link, vote], 52 | // ['4', link, vote] 53 | // ] 54 | // } 55 | // ]; 56 | 57 | segJSON.push({ 58 | // annotation: testAnnotation, 59 | gene: gene, 60 | start: s.start, 61 | end: s.end, 62 | label: s.sample, 63 | sample: d.sample, 64 | dataset: samplesToTypes[d.sample], 65 | ty: s.ty 66 | }) 67 | 68 | if (sampleTypes.indexOf(samplesToTypes[d.sample]) === -1){ 69 | sampleTypes.push( samplesToTypes[s.sample] ); 70 | } 71 | }); 72 | }); 73 | 74 | 75 | // Sort the segments by cancer type and then by length 76 | segJSON.sort(function(a, b){ 77 | if (a.dataset != b.dataset) return d3.ascending(a.dataset, b.dataset); 78 | else return d3.ascending(a.end-a.start, b.end-b.start); 79 | }); 80 | 81 | var sampleTypeToInclude = {}; 82 | sampleTypes.sort().forEach(function(d){ sampleTypeToInclude[d] = true; }); 83 | 84 | var d = { 85 | genes: geneJSON, 86 | sampleTypes: sampleTypes, 87 | samplesToTypes: samplesToTypes, 88 | segments: segJSON, 89 | segmentDomain: [minSegXLoc, maxSegXLoc], 90 | sampleTypeToInclude: sampleTypeToInclude 91 | }; 92 | 93 | d.get = function(arg) { 94 | if (arg == 'genes') return d.genes; 95 | else if (arg == 'sampleTypes') return d.sampleTypes; 96 | else if (arg == 'samplesToTypes') return d.samplesToTypes; 97 | else if (arg == 'segments') return d.segments; 98 | else if (arg == 'amps') return d.amps; 99 | else if (arg == 'dels') return d.dels; 100 | else if (arg == 'segmentDomain') return d.segmentDomain; 101 | else return undefined; 102 | } 103 | 104 | // We stack amplifications above and deletions below the 105 | // genome bar, so we need to figure out which position in 106 | // the stack each *visible* segment has 107 | d.recomputeSegmentIndices = function(){ 108 | var ampIndex = 0, 109 | delIndex = 0; 110 | 111 | d.segments.forEach(function(datum){ 112 | if (d.sampleTypeToInclude[datum.dataset]){ 113 | if (datum.ty == "amp") datum.index = ampIndex++; 114 | if (datum.ty == "del") datum.index = delIndex++; 115 | } 116 | }); 117 | d.numAmps = ampIndex; 118 | d.numDels = delIndex; 119 | } 120 | d.recomputeSegmentIndices(); 121 | 122 | return d; 123 | } 124 | var cnaData = braph(data); 125 | 126 | return cnaData; 127 | } -------------------------------------------------------------------------------- /src/cna/cnaStyle.js: -------------------------------------------------------------------------------- 1 | function cnaStyle(style) { 2 | return { 3 | fontFamily: style.fontFamily || '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 4 | fontSize: style.fontSize || '12px', 5 | geneColor: style.geneColor || '#aaa', 6 | backgroundColor: style.backgroundColor || '#f6f6f6', 7 | geneHeightOverflow: style.geneHeightOverflow || 5, 8 | geneHighlightColor: style.geneHighlightColor || '#f00', 9 | geneSelectedColor: style.geneSelectedColor || '#f00', 10 | genomeBarHeight: style.genomeBarHeight || 14, 11 | height: style.height || 400, // only works if scrolling is activated 12 | horizontalBarHeight: style.horizontalBarHeight || 5, 13 | horizontalBarSpacing: style.horizontalBarSpacing || 6, 14 | width: style.width || 500, 15 | margin: style.margin || {top: 10, right: 20, bottom: 10, left: 0} 16 | }; 17 | } -------------------------------------------------------------------------------- /src/cna/index.js: -------------------------------------------------------------------------------- 1 | import "cna.js"; -------------------------------------------------------------------------------- /src/core/class.js: -------------------------------------------------------------------------------- 1 | // Thanks goes to mbostock and D3 implementation 2 | // https://github.com/mbostock/d3/blob/master/src/core/class.js 3 | 4 | function gd3_class(ctor, properties) { 5 | try { 6 | for (var key in properties) { 7 | Object.defineProperty(ctor.prototype, key, { 8 | value: properties[key], 9 | enumerable: false 10 | }); 11 | } 12 | } catch (e) { 13 | ctor.prototype = properties; 14 | } 15 | } -------------------------------------------------------------------------------- /src/core/color.js: -------------------------------------------------------------------------------- 1 | gd3.color = {}; 2 | 3 | gd3.color.noData = '#eeeeee'; 4 | 5 | gd3.color.categoryPalette; 6 | gd3.color.annotationPalettes = {}; 7 | gd3.color.annotationToType = {}; 8 | 9 | gd3.color.palettes = {}; 10 | 11 | // colorbrewer paired qualitative paired scale with modified 2 and 1 element versions 12 | // Color blind safe! 13 | gd3.color.palettes.categorical_cbSafe = { 14 | 1: ["#1f78b4"], 15 | 2: ["#1f78b4","#b2df8a"], 16 | 3: ["#a6cee3","#1f78b4","#b2df8a"], 17 | 4: ["#a6cee3","#1f78b4","#b2df8a","#33a02c"] 18 | }; 19 | 20 | // colorbrewer paired qualitative paired scale, but above range of colorblind friendly 21 | // Even though the two use the same scale, they are separated for clarity 22 | gd3.color.palettes.categorical = { 23 | 5: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99"], 24 | 6: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c"], 25 | 7: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f"], 26 | 8: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00"], 27 | 9: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6"], 28 | 10: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a"], 29 | 11: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#ffff99"], 30 | 12: ["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#ffff99","#b15928"] 31 | }; 32 | 33 | gd3.color.palettes.annotation_discrete = [ 34 | ["#ad494a", "#a55194", "#8ca252", "#8c6d31", "#843c39", "#393b79", "#7b4173", "#637939", "#e7ba52", "#bd9e39", "#cedb9c", "#ce6dbd", "#d6616b", "#9c9ede", "#b5cf6b", "#5254a3", "#e7969c", "#6b6ecf", "#e7cb94", "#de9ed6"], 35 | ["#fd8d3c", "#31a354", "#9e9ac8", "#969696", "#756bb1", "#3182bd", "#636363", "#e6550d", "#a1d99b", "#74c476", "#fdd0a2", "#bdbdbd", "#bcbddc", "#c6dbef", "#fdae6b", "#6baed6", "#dadaeb", "#9ecae1", "#c7e9c0", "#d9d9d9"] 36 | ]; 37 | 38 | // These default to the colorbrewer sequential, single-hue palettes 39 | // The blue scale has been discluded because of its use for the heatmap chart 40 | // Additionally, the ordering of scales is made to be as colorblind-friendly as possible 41 | gd3.color.palettes.annotation_continuous = [ 42 | ['rgb(247,252,245)','rgb(0,68,27)'], 43 | ['rgb(252,251,253)','rgb(63,0,125)'], 44 | ['rgb(240,240,240)','rgb(0,0,0)'], 45 | ['rgb(255,245,235)','rgb(127,39,4)'], 46 | ['rgb(255,245,240)','rgb(103,0,13)'] 47 | ]; 48 | 49 | // The behavior for annotations is as follows: 50 | // annotations() : return the annotation palette object 51 | // annotations(key) : return the annotation palette object key's value 52 | // annotation(key, data) : set the annotation key's palette to have a domain of data 53 | // --> The scale will default to discrete, unless data.length == 2 && typeof(each datum) == Number 54 | // annotation(key, data, type) : as before, except hardcode scale as "discrete" or "continuous" 55 | // annotation(key, data, type, colors) : as before, except hardcode in palette colors 56 | gd3.color.annotations = function() { 57 | if(arguments.length == 0) return gd3.color.annotationPalettes; 58 | if(arguments.length == 1) return gd3.color.annotationPalettes[arguments[0]]; 59 | // Else, expect two arguments where the first is the name and the second is the type 60 | if(Object.prototype.toString.call(arguments[1]) !== '[object Array]' ) { 61 | throw 'annotations() must be passed: (1) the annotation name, (2) an array of annotation values' 62 | + ' OR the range of values, (3) [optionally] a string declaring if the data is "discrete"' 63 | + ' or "continuous"'; 64 | } 65 | if(arguments.length > 2 && arguments[2] != "discrete" && arguments[2] != "continuous") { 66 | throw 'annotations() third argument must either be "discrete" or "continuous"'; 67 | } 68 | 69 | var scale; 70 | 71 | var annotation = arguments[0], 72 | data = arguments[1]; 73 | 74 | // Assign scale type 75 | var type; 76 | if(arguments.length > 2) type = arguments[2]; 77 | else if(data.length == 2 && typeof(data[0]) === 'number' && typeof(data[1]) === 'number') type = 'continuous'; 78 | else type = 'discrete'; 79 | 80 | gd3.color.annotationToType[annotation] = type; 81 | 82 | // Define the type of scale and the domain 83 | if(type == 'continuous') { 84 | scale = d3.scale.linear().domain([d3.min(data),d3.max(data)]); 85 | } else { 86 | scale = d3.scale.ordinal().domain(data); 87 | } 88 | 89 | // Define the color scale range of the annotation 90 | var colors; 91 | if(arguments.length > 3) { 92 | if(Object.prototype.toString.call(arguments[3]) !== '[object Array]' ) { 93 | throw 'annotations()\'s third argument must be an array of colors you wish to use in your annotation scale'; 94 | } 95 | colors = arguments[3]; 96 | } else { 97 | var numOfType = Object.keys(gd3.color.annotationPalettes).filter(function(d) { 98 | return gd3.color.annotationToType[d] == type; 99 | }).length, // # of previously defined of this type of scale 100 | palettes = gd3.color.palettes; 101 | 102 | var paletteIndex; 103 | if(type == 'discrete') { 104 | paletteIndex = (numOfType + 1) % palettes.annotation_discrete.length; 105 | } else { 106 | paletteIndex = (numOfType + 1) % palettes.annotation_continuous.length; 107 | } 108 | 109 | var palette = (type == 'discrete' ? palettes.annotation_discrete : palettes.annotation_continuous)[paletteIndex]; 110 | 111 | colors = palette; 112 | } 113 | scale.range(colors); 114 | 115 | // Define the annotation scale in the annotationPalettes object 116 | gd3.color.annotationPalettes[annotation] = scale; 117 | } 118 | 119 | // Create a palette for category data (e.g., cancer type) given the categories 120 | // or given categories and colors 121 | // If no arguments are given, the function returns the current palette 122 | gd3.color.categories = function() { 123 | function isArrayTest() { 124 | for(var i = 0; i < arguments.length; i++) { 125 | var a = arguments[i]; 126 | if( Object.prototype.toString.call(a) !== '[object Array]' ) { 127 | throw 'categories() must be passed: (1) an array of categories, (2) an array of categories' 128 | + ' and an array of colors'; 129 | } 130 | if(a.length == 0) throw 'categories() must be passed non-empty arrays for arguments'; 131 | } 132 | } 133 | 134 | if(arguments.length == 0) return gd3.color.categoryPalette; 135 | else if(arguments.length == 1) { 136 | var categories = arguments[0]; 137 | isArrayTest(categories); 138 | 139 | var colors; 140 | if(categories.length < 5) { 141 | colors = gd3.color.palettes.categorical_cbSafe[categories.length]; 142 | } else if (categories.length < 13) { 143 | colors = gd3.color.palettes.categorical[categories.length]; 144 | } else { 145 | colors = d3.scale.category20().range(); 146 | } 147 | 148 | gd3.color.categoryPalette = d3.scale.ordinal().domain(categories).range(colors); 149 | } else if(arguments.length > 1) { 150 | var categories = arguments[0], 151 | colors = arguments[1]; 152 | 153 | isArrayTest(categories, colors); 154 | gd3.color.categoryPalette = d3.scale.ordinal().domain(categories).range(colors); 155 | } 156 | 157 | return gd3.color.categoryPalette; 158 | } -------------------------------------------------------------------------------- /src/core/dataStructures.js: -------------------------------------------------------------------------------- 1 | var gd3_data_structures = { 2 | UnionFind: function(){ 3 | // Instance variables 4 | var weights = {}, 5 | parents = {}; 6 | 7 | // Find and return the name of the set containing the object 8 | function get(x){ 9 | // check for previously unknown object 10 | if (!(x in parents)){ 11 | parents[x] = x; 12 | weights[x] = 1; 13 | return x; 14 | } else { 15 | // find path of objects leading to the root 16 | var path = [x], 17 | root = parents[x], 18 | count = 0; 19 | 20 | while (root != path[path.length - 1] && count <= 15){ 21 | path.push( root ); 22 | root = parents[root]; 23 | count++; 24 | } 25 | 26 | // compress the path and return 27 | path.forEach(function(ancestor){ 28 | parents[ancestor] = root; 29 | }); 30 | 31 | return root; 32 | } 33 | } 34 | 35 | // Find the sets containing the objects and merge them all 36 | function union(xs){ 37 | // Convert xs to a list if it isn't one already 38 | if (xs.constructor != Array){ 39 | xs = [xs]; 40 | } 41 | 42 | // Merge all sets containing any x in xs 43 | var roots = xs.map(get), 44 | heaviest = d3.max(roots.map(function(r){ return [weights[r], r]; }))[1]; 45 | 46 | roots.forEach(function(r){ 47 | if (r != heaviest){ 48 | weights[heaviest] += weights[r]; 49 | parents[r] = heaviest; 50 | } 51 | }); 52 | } 53 | 54 | // Return a list of lists containing each group 55 | function groups(){ 56 | var groupIndex = 0, 57 | groupToIndex = {}, 58 | currentGroups = [[]]; 59 | 60 | Object.keys(parents).forEach(function(n){ 61 | var group = get(n); 62 | if (!(group in groupToIndex)) groupToIndex[group] = groupIndex++; 63 | if (currentGroups.length <= groupToIndex[group]) currentGroups.push([]); 64 | currentGroups[groupToIndex[group]].push( +n ); 65 | }); 66 | 67 | return currentGroups; 68 | } 69 | 70 | return { get: get, union: union, groups: groups }; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/core/index.js: -------------------------------------------------------------------------------- 1 | import "class"; 2 | import "color"; 3 | import "util"; 4 | import "dataStructures"; -------------------------------------------------------------------------------- /src/core/util.js: -------------------------------------------------------------------------------- 1 | var gd3_util = { 2 | arraysEqual: function(a, b) { 3 | if (a === b) return true; 4 | if (a == null || b == null) return false; 5 | if (a.length != b.length) return false; 6 | 7 | // If you don't care about the order of the elements inside 8 | // the array, you should sort both arrays here. 9 | 10 | for (var i = 0; i < a.length; ++i) { 11 | if (a[i] !== b[i]) return false; 12 | } 13 | return true; 14 | }, 15 | arrayToSet: function(a) { 16 | var seen = {}; 17 | return a.filter(function(item) { 18 | return seen.hasOwnProperty(item) ? false : (seen[item] = true); 19 | }); 20 | }, 21 | allPairs: function(xs){ 22 | var n = xs.length, 23 | pairs = []; 24 | 25 | for (var i = 0; i < n; i++){ 26 | for (var j = i+1; j < n; j++){ 27 | pairs.push( [xs[i], xs[j]] ); 28 | } 29 | } 30 | return pairs; 31 | }, 32 | selectionSize: function(selection){ 33 | var n = 0; 34 | selection.each(function() { ++n; }); 35 | return n; 36 | } 37 | } -------------------------------------------------------------------------------- /src/dendrogram/dendrogram.js: -------------------------------------------------------------------------------- 1 | import "../core/util.js" 2 | import "../core/dataStructures.js" 3 | import "dendrogramChart"; 4 | import "dendrogramStyle"; 5 | 6 | gd3.dendrogram = function(params) { 7 | var params = params || {}, 8 | style = dendrogramStyle(params.style || {}); 9 | 10 | // dendrogramChart functions as a partial application, binding the given variables 11 | // into the returned instance. 12 | return dendrogramChart(style); 13 | }; -------------------------------------------------------------------------------- /src/dendrogram/dendrogramChart.js: -------------------------------------------------------------------------------- 1 | import "dendrogramData.js"; 2 | 3 | function dendrogramChart(style) { 4 | // Globals controllable by the user 5 | var update, 6 | currentDelta, 7 | cutAndUpdate, 8 | showSlider = false, 9 | useLogScale = false; 10 | 11 | function chart(selection) { 12 | selection.each(function(inputData) { 13 | /////////////////////////////////////////////////////////////////// 14 | // Perform simple validation of data 15 | // Z: N-1 x 4 linkage matrix, where N is the number of leaves. Each 16 | // row stores which two nodes (columns 1 and 2) merge at distance 17 | // (column 3). See Scipy for details: http://goo.gl/nycOCS 18 | // labels: array of labels (strings). Labels will be drawn in the order 19 | // given in this array. 20 | if (!inputData.Z || !inputData.labels){ 21 | throw "dendrogram: Z and labels *required*." 22 | } 23 | 24 | // Parse the given data into the data structures we'll be using 25 | var T = treeFromLinkageMatrix(inputData); 26 | data = dendrogramData(inputData, currentDelta, T); 27 | 28 | // Make sure that there is a unique index in the 4th column 29 | var indices = data.Z.map(function(r){ return r[3]; }); 30 | if (indices.length != d3.set(indices).values().length){ 31 | data.Z.forEach(function(r, i){ 32 | r[3] = i + "-" + r[3]; 33 | }) 34 | } 35 | 36 | /////////////////////////////////////////////////////////////////// 37 | // Give some of the style elements shorter variable handles 38 | var height = style.height, 39 | width = style.width, 40 | treeWidth, 41 | colorScheme = style.colorScheme, // name of color scheme 42 | colorSchemes = style.colorSchemes; 43 | 44 | // Set up the SVG 45 | var svg = d3.select(this).selectAll('svg') 46 | .data([data]).enter() 47 | .append('svg') 48 | .attr("xmlns", "http://www.w3.org/2000/svg"), 49 | fig = svg.append("g"), 50 | edges = fig.append("g").attr("id" ,"edges"), // add edges first so they are shown below nodes 51 | leafGroup = fig.append("g").attr("id", "leaves"); 52 | 53 | svg.attr('id', 'figure') 54 | .attr('height', height) 55 | .attr('width', width) 56 | .style('font-family', style.fontFamily) 57 | .style('font-size', style.fontSize); 58 | 59 | // Set up the x-axis 60 | var xAxis = d3.svg.axis(), 61 | xAxisGroup = fig.append("g") 62 | .style({'stroke': style.fontColor, 'fill': 'none', 'stroke-width': style.strokeWidth}) 63 | .attr("transform", "translate(0," + height + ")"); 64 | 65 | // Set up colors 66 | if (!(colorScheme in colorSchemes)){ 67 | colorScheme = "default"; 68 | } 69 | var color = colorSchemes[colorScheme]; 70 | 71 | /////////////////////////////////////////////////////////////////// 72 | // Update redraws the dendrogram using the given treeData 73 | update = function (treeData){ 74 | // Parse the data into shorter variable handles 75 | var Z = treeData.Z, 76 | labels = treeData.labels, 77 | labelToGroup = treeData.labelToGroup, 78 | n = labels.length; // number of nodes 79 | 80 | if (!treeData.labelToGroup){ 81 | labelToGroup = {}; 82 | labels.forEach(function(d, i){ labelToGroup[d] = i; }); 83 | } 84 | 85 | // Create a mapping of leaves (labels) to when they first are 86 | // added, and then sort the labels in this order 87 | function isLeaf(v){ return v < n; } 88 | var nodeToIndex = []; 89 | labels.forEach(function(n, i){ nodeToIndex[i] = i; }); 90 | 91 | // Set up a linear y-axis scale 92 | var y = d3.scale.linear() 93 | .domain([0, labels.length-1]) 94 | .range([style.nodeRadius, height-style.nodeRadius]), 95 | labelToY = d3.scale.ordinal() // convenience scale for labels directly 96 | .domain(labels) 97 | .rangePoints([style.nodeRadius, height-style.nodeRadius]); 98 | 99 | /////////////////////////////////////////////////////////////// 100 | // LEAVES' GENERAL UPDATE 101 | 102 | // DATA JOIN: join data with old elements 103 | var leaves = leafGroup.selectAll("g") 104 | .data(labels, function(d){ return d; }); 105 | 106 | // UPDATE: transition old elements 107 | leaves.transition() 108 | .duration(style.animationSpeed) 109 | .attr("transform", function(d){ return "translate(0," + labelToY(d) + ")"; }); 110 | 111 | leaves.select("circle") 112 | .attr("fill", function(d){ return color(labelToGroup[d]); }); 113 | 114 | // ENTER: create new elements 115 | var leafGs = leaves.enter() 116 | .append("g") 117 | .attr("transform", function(d){ return "translate(0," + labelToY(d) + ")"; }); 118 | 119 | leafGs.append("circle") 120 | .attr("r", style.nodeRadius) 121 | .style("fill-opacity", 1e-6) 122 | .attr("fill", function(d){ return color(labelToGroup[d]); }) 123 | .transition() 124 | .duration(style.animationSpeed) 125 | .style("fill-opacity", 1); 126 | 127 | leafGs.append("text") 128 | .attr("text-anchor", "start") 129 | .attr("x", style.nodeRadius + 5) 130 | .attr("y", style.nodeRadius/2) 131 | .text(function(d){ return d; }); 132 | 133 | // EXIT: remove old elements 134 | leaves.exit().transition() 135 | .duration(style.animationSpeed) 136 | .style("fill-opacity", 1e-6) 137 | .remove(); 138 | 139 | /////////////////////////////////////////////////////////////// 140 | // Set up the linear x-axis scale by: 141 | 142 | // (1) Compute the size of the largest label, so we can 143 | // set the tree width 144 | var labelWidth = leafGroup.node().getBBox().width; 145 | treeWidth = width - labelWidth - style.margins.left; 146 | leafGroup.attr("transform", "translate(" + treeWidth + ",0)"); 147 | 148 | // (2) Using the updated treeWidth to set up the x-axis scale 149 | var dists = Z.map(function(row){ return row[2]; }), 150 | xExtent = d3.extent(dists); 151 | 152 | if (useLogScale) x = d3.scale.log(); 153 | else x = d3.scale.linear() 154 | x.domain(xExtent) 155 | .range([treeWidth, style.margins.left]); // go in reverse, since low distances are to the furthest right 156 | 157 | /////////////////////////////////////////////////////////////// 158 | // Create objects to represent each edge 159 | var edgeData = [], 160 | distances = labels.map(function(_){ return xExtent[0]; }) 161 | groups = labels.map(function(d){ return [labelToGroup[d]]; }); 162 | 163 | function connectNodes(u, v, w, index){ 164 | // Find the y-index of each node 165 | var i = nodeToIndex[u], 166 | j = nodeToIndex[v], 167 | d1 = distances[u], 168 | d2 = distances[v], 169 | g1 = groups[u], 170 | g2 = groups[v], 171 | newG = g1.length == 1 && g2.length == 1 && g1[0] == g2[0] ? g1 : g1.concat(g2); 172 | 173 | // Draw the horizontal line from u 174 | edgeData.push({name: "u" + index, x1: x(d1), x2: x(w), y1: y(i), y2: y(i), groups: g1 }); 175 | 176 | // Draw the horizontal line from v 177 | edgeData.push({name: "v" + index, x1: x(d2), x2: x(w), y1: y(j), y2: y(j), groups: g2 }); 178 | 179 | // Connect the two horizontal lines with a vertical line 180 | edgeData.push({name: "uv" + index, x1: x(w), x2: x(w), y1: y(i), y2: y(j), groups: newG }) 181 | 182 | // Add an index for the new internal nodes 183 | nodeToIndex.push( (i + j) / 2. ); 184 | distances.push(w); 185 | groups.push( newG ); 186 | 187 | } 188 | 189 | Z.forEach(function(row){ connectNodes(row[0], row[1], row[2], row[3]); }); 190 | 191 | /////////////////////////////////////////////////////////////// 192 | // EDGES' GENERAL UPDATE 193 | // Data join 194 | var lines = edges.selectAll("line") 195 | .data(edgeData, function(d){ return d.name + " " + d.ty; }); 196 | 197 | // Transition existing elements 198 | lines.transition() 199 | .duration(style.animationSpeed) 200 | .attr("x1", function(d){ return d.x1; }) 201 | .attr("x2", function(d){ return d.x2; }) 202 | .attr("y1", function(d){ return d.y1; }) 203 | .attr("y2", function(d){ return d.y2; }) 204 | .attr("stroke-dasharray", function(d){ 205 | if (d.groups.length == 1){ return ""; } 206 | else { return ("3", "3"); } 207 | }); 208 | 209 | // Add new elements 210 | lines.enter() 211 | .append("line") 212 | .attr("x1", function(d){ return d.x1; }) 213 | .attr("x2", function(d){ return d.x1; }) 214 | .attr("y1", function(d){ return d.y1; }) 215 | .attr("y2", function(d){ return d.y1; }) 216 | .attr("stroke", style.strokeColor) 217 | .attr("stroke-width", style.strokeWidth) 218 | .attr("stroke-dasharray", function(d){ 219 | if (d.groups.length == 1){ return ""; } 220 | else { return ("3", "3"); } 221 | }) 222 | .attr("fill-opacity", 1e-6) 223 | .transition() 224 | .duration(style.animationSpeed) 225 | .attr("x2", function(d){ return d.x2; }) 226 | .attr("y2", function(d){ return d.y2; }) 227 | .attr("fill-opacity", 1); 228 | 229 | // Fade and remove old elements 230 | lines.exit().transition() 231 | .duration(style.animationSpeed) 232 | .style("stroke-opacity", 1e-6) 233 | .remove(); 234 | 235 | /////////////////////////////////////////////////////////////////// 236 | // Update the x-axis 237 | xAxis.scale(x); 238 | xAxisGroup.call(xAxis); 239 | xAxisGroup.selectAll("text") 240 | .style({'stroke-width': '0px', 'fill': style.fontColor }); 241 | 242 | /////////////////////////////////////////////////////////////////// 243 | // Resize the SVG to make sure everything fits 244 | svg.attr('height', fig.node().getBBox().height); 245 | } 246 | 247 | // Draw the inital dendrogram 248 | update(data); 249 | 250 | // Show the slider if necessary 251 | if (showSlider){ 252 | // Create the data 253 | var U = gd3_data_structures.UnionFind(), 254 | N = inputData.labels.length, 255 | step = Math.ceil(N/100), // only sample points after every N/100 merges 256 | series = [{values: [], name: "Largest component"}, 257 | {values: [], name: "Non-singleton components"}]; 258 | 259 | inputData.Z.forEach(function(row, i){ 260 | // Merge nodes row[0] and row[1] with their parent 261 | U.union([row[0], row[1], N+i]); 262 | 263 | // Record the largest and number of non-singleton components 264 | // every N/100 merges 265 | if (i % step == 0){ 266 | var groups = U.groups(), 267 | largestSize = d3.max(groups, function(g){ return g.length; }), 268 | nonSingletons = d3.sum(groups.map(function(g){ return g.length > 1; })); 269 | if (row[2] != 0){ 270 | series[0].values.push({x: row[2], y: largestSize }); 271 | series[1].values.push({x: row[2], y: nonSingletons }); 272 | } 273 | } 274 | }); 275 | 276 | var allPoints = d3.merge(series.map(function(d, i){ return d.values; })); 277 | 278 | // Set up the sizes 279 | var sliderWidth = treeWidth - style.sliderMargins.left - style.sliderMargins.right, 280 | sliderHeight = style.sliderHeight - style.sliderMargins.top - style.sliderMargins.bottom; 281 | 282 | // Set up the scales 283 | if (useLogScale) sliderX = d3.scale.log().range([0, sliderWidth]); 284 | else sliderX = d3.scale.linear().range([0, sliderWidth]); 285 | 286 | var yExtent = d3.extent(allPoints, function(d){ return d.y; }); 287 | if (yExtent[1] - yExtent[0] > 100) sliderY = d3.scale.log().range([sliderHeight, 0]); 288 | else sliderY = d3.scale.linear().range([sliderHeight, 0]); 289 | 290 | sliderX.domain( d3.extent(allPoints, function(d) { return d.x; }).reverse() ); 291 | sliderY.domain( yExtent ).nice(); 292 | 293 | // Set up the axes 294 | var sliderColor = d3.scale.category10(), 295 | sliderXAxis = d3.svg.axis() 296 | .scale(sliderX) 297 | .orient("bottom"), 298 | sliderYAxis = d3.svg.axis() 299 | .scale(sliderY) 300 | .ticks(5) 301 | .orient("left"), 302 | line = d3.svg.line() 303 | .interpolate("basis") 304 | .x(function(d) { return sliderX(d.x); }) 305 | .y(function(d) { return sliderY(d.y); }); 306 | 307 | // Add the instructions and the SVG 308 | d3.select(this).insert("div", "svg") 309 | .html("Instructions: Choose δ by mousing over the plot below and double-clicking. You can change your selection by double-clicking again.

") 310 | 311 | var sliderSVG = d3.select(this).insert("svg", "svg") 312 | .attr("width", sliderWidth + style.sliderMargins.left + style.sliderMargins.right) 313 | .attr("height", sliderHeight + style.sliderMargins.top + style.sliderMargins.bottom) 314 | .append("g") 315 | .attr("transform", "translate(" + style.sliderMargins.left + "," + style.sliderMargins.top + ")") 316 | .style("font-size", style.fontSize); 317 | 318 | sliderSVG.append("rect") // background 319 | .attr("width", sliderWidth + style.sliderMargins.left + style.sliderMargins.right) 320 | .attr("height", sliderHeight + style.sliderMargins.top + style.sliderMargins.bottom) 321 | .attr("fill", style.backgroundColor); 322 | 323 | // Draw the lines 324 | var lines = sliderSVG.selectAll(".point") 325 | .data(series) 326 | .enter().append("g") 327 | .attr("class", "point"); 328 | 329 | lines.append("path") 330 | .attr("class", "line") 331 | .attr("d", function(d) { return line(d.values); }) 332 | .style("fill", "none") 333 | .style("stroke", function(d) { return sliderColor(d.name); }); 334 | 335 | // Add the axes 336 | sliderSVG.append("g") 337 | .attr("class", "x axis") 338 | .attr("transform", "translate(0," + sliderHeight + ")") 339 | .call(sliderXAxis) 340 | .append("text") 341 | .attr("x", sliderWidth/2) 342 | .attr("dy", "30px") 343 | .style("text-anchor", "middle") 344 | .text("\u03B4"); 345 | 346 | sliderSVG.append("g") 347 | .attr("class", "y axis") 348 | .call(sliderYAxis); 349 | 350 | sliderSVG.selectAll(".axis path") 351 | .style({fill: "none", stroke: "#000", "shape-rendering": "crispEdges"}); 352 | sliderSVG.selectAll(".axis line") 353 | .style({fill: "none", stroke: "#000", "shape-rendering": "crispEdges"}); 354 | 355 | // Add a series legend, aligned to the right topmost corner of the plot 356 | var legend = sliderSVG.append("g"), 357 | legendGroups = legend.selectAll(".legend-text") 358 | .data(series).enter() 359 | .append("g"); 360 | 361 | legendGroups.append("line") 362 | .attr("x1", 0) 363 | .attr("x1", 20) 364 | .attr("y1", 0) 365 | .attr("y2", 0) 366 | .style("stroke", function(d) { return sliderColor(d.name); }); 367 | 368 | legendGroups.append("text").attr("x", 25).attr("y", 3).text(function(d){ return d.name; }); 369 | 370 | var legendWidth = legend.node().getBBox().width; 371 | legendGroups.attr("transform", function(d, i){ 372 | var thisX = treeWidth - legendWidth - style.sliderMargins.left, 373 | thisY = sliderY(d3.max(sliderY.domain())) + (i)*15; 374 | return "translate(" + thisX + "," + thisY + ")"; 375 | }); 376 | 377 | // Add the onmouseover line that we use to select delta 378 | var deltaFixed = false, 379 | deltaFormat = d3.format(".5r"), 380 | deltaLine = sliderSVG.append("line") 381 | .attr("y1", sliderY(d3.min(sliderY.domain()))) 382 | .attr("y2", sliderY(d3.max(sliderY.domain()))) 383 | .style("fill", "none") 384 | .style("stroke", style.strokeColor) 385 | .style("opacity", 0), 386 | delta = sliderSVG.append("text") 387 | .attr("text-anchor", "start") 388 | .attr("x", treeWidth - legendWidth - style.sliderMargins.left) 389 | .attr("y", sliderY(d3.max(sliderY.domain())) + (series.length*15) + 5) 390 | .text("\u03B4: " + deltaFormat(d3.max(sliderX.domain()))); 391 | 392 | // Move the line to match the mouse position 393 | sliderSVG.on("mousemove", function(){ 394 | var coordinates = d3.mouse(this); 395 | if (!deltaFixed && coordinates[0] > 0){ 396 | deltaLine.attr("x1", coordinates[0]) 397 | .attr("x2", coordinates[0]) 398 | .style("opacity", 1); 399 | } 400 | }); 401 | 402 | // On double click update the plot with the given delta 403 | sliderSVG.on("dblclick", function(){ 404 | // Fix the delta on double click if it wasn't before 405 | if (!deltaFixed){ 406 | currentDelta = sliderX.invert(deltaLine.attr("x1")); 407 | cutAndUpdate(); 408 | } 409 | deltaFixed = !deltaFixed; 410 | }); 411 | 412 | } 413 | 414 | // Cut the plot using the current value of delta, 415 | // then update the dendrogram accordingly 416 | cutAndUpdate = function(){ 417 | // Update the value shown in the delta plot 418 | if (showSlider) delta.text("\u03B4: " + deltaFormat(currentDelta)); 419 | update(cutDendrogram(inputData, currentDelta, T)); 420 | } 421 | }); 422 | } 423 | 424 | // "Public" functions that the user can call 425 | chart.update = function(treeData){ 426 | update(treeData); 427 | return chart; 428 | } 429 | 430 | chart.animationSpeed = function(){ 431 | return style.animationSpeed; // note this returns the animation speed, NOT the chart 432 | } 433 | 434 | chart.showSlider = function(){ 435 | showSlider = true; 436 | return chart; 437 | } 438 | 439 | chart.setDelta = function(delta){ 440 | currentDelta = delta; 441 | if (cutAndUpdate) cutAndUpdate(delta); 442 | return chart; 443 | }; 444 | 445 | chart.logScale = function(_){ 446 | if (arguments.length) useLogScale = _; 447 | return chart; 448 | }; 449 | 450 | return chart; 451 | } -------------------------------------------------------------------------------- /src/dendrogram/dendrogramData.js: -------------------------------------------------------------------------------- 1 | function dendrogramData(inputData, delta, T) { 2 | // Set delta to merge everything (unless it was set already) 3 | if (!delta) delta = inputData.Z[inputData.Z.length-1][2]; 4 | 5 | // Cut the dendrogram accordingly and return 6 | return cutDendrogram(inputData, delta, T); 7 | } 8 | 9 | function cutDendrogram(data, delta, T){ 10 | function MRCA(u, v){ 11 | var parentsU = [], 12 | parentsV = [], 13 | node = u; 14 | 15 | while (node != null){ 16 | node = T[node].parent; 17 | parentsU.push(node); 18 | } 19 | 20 | node = v; 21 | while (node != null){ 22 | node = T[node].parent; 23 | if (parentsU.indexOf(node) != -1){ 24 | return T[node].weight; 25 | } 26 | } 27 | return 0; 28 | } 29 | 30 | function subtreeDistance(t1, t2){ return MRCA(d3.max(t1), d3.max(t2)); } 31 | function isLeaf(v){ return v < N; } 32 | 33 | // Declarations 34 | var Z = [], labelToGroup = {}, groupLabels = {}, groupIndex = 0, 35 | rows = data.Z.filter(function(row){ return row[2] <= +delta; }) 36 | .map(function(row, i){ return row.slice(0, 3).concat([i]); }), 37 | n = rows.length, 38 | N = data.labels.length; 39 | 40 | // Add all the nodes that are below the cut value to the UnionFind 41 | var U = gd3_data_structures.UnionFind(), 42 | leafIndices = []; 43 | 44 | rows.forEach(function(row, i){ 45 | U.union([row[0], row[1], N+i]); 46 | if (isLeaf(row[0])) leafIndices.push(row[0]); 47 | if (isLeaf(row[1])) leafIndices.push(row[1]); 48 | }); 49 | 50 | leafIndices = leafIndices.sort(function(i, j){ return d3.ascending(+i, +j); }); 51 | var labels = leafIndices.map(function(i){ return data.labels[i]; }); 52 | 53 | // Then extract the labels that will still be required, normalizing 54 | // the rows to match their new numbers 55 | function addIfLeaf(v){ 56 | // Ignore internal nodes, leaving the row unchanged 57 | if (!isLeaf(v)) return v; 58 | 59 | // Record leaves and assign them to groups 60 | var group = U.get(v); 61 | if (!(group in groupLabels)) groupLabels[group] = groupIndex++; 62 | // labels.push(data.labels[v]); 63 | labelToGroup[data.labels[v]] = groupLabels[group]; 64 | 65 | // Relabel the leaf to its new index 66 | return leafIndices.indexOf(v); 67 | } 68 | 69 | rows.forEach(function(row, i){ 70 | row[0] = addIfLeaf(row[0]); 71 | row[1] = addIfLeaf(row[1]); 72 | }); 73 | 74 | // Normalize the values of each internal node (we can only 75 | // do this once we know the number of labels) 76 | var m = labels.length; 77 | rows.forEach(function(row){ 78 | if (row[0] >= m) row[0] -= N - m; 79 | if (row[1] >= m) row[1] -= N - m; 80 | }); 81 | 82 | // Compute the pseudoedges 83 | var groups = U.groups().map(function(g){ 84 | return {members: g, _id: d3.max(g) - N + m}; 85 | }), 86 | pairs = gd3_util.allPairs(groups), 87 | d = {}; 88 | 89 | pairs.forEach(function(P){ 90 | var dist = subtreeDistance(P[0].members, P[1].members); 91 | if (!d[P[0]._id]) d[P[0]._id] = {}; 92 | if (!d[P[1]._id]) d[P[1]._id] = {}; 93 | d[P[0]._id][P[1]._id] = dist; 94 | d[P[1]._id][P[0]._id] = dist; 95 | }); 96 | 97 | var iterations = 0; 98 | while (pairs.length > 0){ 99 | // Some sanity checking so the browser won't crash even if this infinitely loops 100 | if (iterations > 10000) throw new Error("This while loop shouldn't execute 10k times.") 101 | iterations++; 102 | 103 | // Identify the pair with minimum distance 104 | var toMerge, lowest = Number.MAX_VALUE; 105 | pairs.forEach(function(P, i){ 106 | if (d[P[0]._id][P[1]._id] < lowest){ 107 | lowest = d[P[0]._id][P[1]._id] 108 | toMerge = P; 109 | } 110 | }); 111 | 112 | // Add that link to the linkage matrix 113 | rows.push([toMerge[0]._id, toMerge[1]._id, lowest, rows.length]); 114 | 115 | // Remove the groups/pairs that include either of the merged pairs 116 | var idsToRemove = [toMerge[0]._id, toMerge[1]._id]; 117 | groups = groups.filter(function(G){ return idsToRemove.indexOf(G._id) === -1; }); 118 | pairs = pairs.filter(function(P){ 119 | return idsToRemove.indexOf(P[0]._id) === -1 && 120 | idsToRemove.indexOf(P[1]._id) === -1; 121 | }); 122 | 123 | // Add new pairs including the new group 124 | var newGroup = { _id: m + rows.length-1, 125 | members: d3.merge([toMerge[0].members, toMerge[1].members]) 126 | }; 127 | d[newGroup._id] = {}; 128 | 129 | groups.forEach(function(G){ 130 | var dist = d3.min([d[toMerge[0]._id][G._id], d[toMerge[1]._id][G._id]]); 131 | pairs.push([G, newGroup]); 132 | d[G._id][newGroup._id] = dist; 133 | d[newGroup._id][G._id] = dist; 134 | }); 135 | groups.push(newGroup); 136 | } 137 | 138 | Z = rows.map(function(row){ return row.slice(0); }); 139 | 140 | return { Z: Z, labels: labels, labelToGroup: labelToGroup }; 141 | } 142 | 143 | /////////////////////////////////////////////////////////////////////////// 144 | // Create a tree to represent the linkage matrix and make it easy to 145 | // compute distances between subtrees 146 | function treeFromLinkageMatrix(data){ 147 | var N = data.labels.length, 148 | T = {}; 149 | 150 | data.Z.forEach(function(row, i){ 151 | T[N+i] = { children: [row[0], row[1]], weight: row[2], parent: null }; 152 | if (!(row[0] in T)) T[row[0]] = {}; 153 | T[row[0]].parent = N+i; 154 | if (!(row[1] in T)) T[row[1]] = {}; 155 | T[row[1]].parent = N+i; 156 | }); 157 | 158 | return T; 159 | } 160 | -------------------------------------------------------------------------------- /src/dendrogram/dendrogramStyle.js: -------------------------------------------------------------------------------- 1 | function dendrogramStyle(style) { 2 | return { 3 | colorSchemes: style.colorSchemes || {"default": d3.scale.category20() }, 4 | edgeWidth: style.edgeWidth || 1.5, 5 | fontColor: style.fontColor || '#333', 6 | backgroundColor: "#fff", 7 | fontFamily: style.fontFamily || '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 8 | fontSize: style.fontSize || 12, 9 | margins: style.margins || {bottom: 0, left: 5, right: 0, top: 0}, 10 | nodeRadius: style.nodeRadius || 10, 11 | width: style.width || 1200, 12 | height: style.height || 600, 13 | strokeWidth: style.strokeWidth || 1, 14 | strokeColor: style.strokeColor || "#333", 15 | colorScheme: style.colorScheme || "default", 16 | animationSpeed: style.animationSpeed || 750, 17 | margins: { left: 40, right: 0, top: 0, bottom: 0}, 18 | sliderMargins: { left: 40, right: 0, top: 5, bottom: 20}, 19 | sliderHeight: 125 20 | } 21 | } -------------------------------------------------------------------------------- /src/dendrogram/index.js: -------------------------------------------------------------------------------- 1 | import "dendrogram.js" -------------------------------------------------------------------------------- /src/dispatch/dispatch.js: -------------------------------------------------------------------------------- 1 | gd3.dispatch = d3.dispatch('sample', 'interaction', 'sort', 'filterCategory', 'filterType', 'mutation', 'filterMutationType'); 2 | 3 | // filter dataset is akin to filtering by cancer type, since each cancer type is its own dataset 4 | // filter cell type is akin to filtering by insertion or deletion mutations, since they are a datatype within a dataset 5 | -------------------------------------------------------------------------------- /src/dispatch/index.js: -------------------------------------------------------------------------------- 1 | import "dispatch"; -------------------------------------------------------------------------------- /src/end.js: -------------------------------------------------------------------------------- 1 | if (typeof define === 'function' && define.amd) define(gd3); 2 | else if (typeof module === 'object' && module.exports) module.exports = gd3; 3 | this.gd3 = gd3; 4 | }(); -------------------------------------------------------------------------------- /src/gd3.js: -------------------------------------------------------------------------------- 1 | import "start.js"; 2 | import "dispatch/"; 3 | import "core/"; 4 | import "cna/"; 5 | import "dendrogram/"; 6 | import "graph/"; 7 | import "heatmap/"; 8 | import "mutmtx/"; 9 | import "scatterplot/"; 10 | import "tooltip/"; 11 | import "transcript/"; 12 | import "end.js"; -------------------------------------------------------------------------------- /src/graph/graph.js: -------------------------------------------------------------------------------- 1 | import "graphChart.js"; 2 | import "graphStyle.js"; 3 | 4 | gd3.graph = function(params) { 5 | var params = params || {}, 6 | style = graphStyle(params.style || {}); 7 | 8 | // graphChart functions as a partial application, binding the given variables 9 | // into the returned instance. 10 | return graphChart(style); 11 | }; -------------------------------------------------------------------------------- /src/graph/graphChart.js: -------------------------------------------------------------------------------- 1 | import "graphData.js"; 2 | 3 | function graphChart(style) { 4 | var anchorNodesOnClick = true, 5 | drawLegend = true; 6 | 7 | function chart(selection) { 8 | selection.each(function(data) { 9 | data = graphData(data); 10 | 11 | // Used for edge classes and filtering 12 | var instanceIDConst = 'gd3-graph-'+Date.now(); 13 | 14 | var height = style.height, 15 | width = style.width; 16 | 17 | var svg = d3.select(this) 18 | .selectAll('svg') 19 | .data([data]) 20 | .enter() 21 | .append('svg') 22 | .attr('height', height) 23 | .attr('width', width) 24 | .style('font-family', style.fontFamily) 25 | .style('font-size', style.fontSize); 26 | 27 | var graph = svg.append('g'); 28 | 29 | // Set up edge coloring 30 | var edgeColor = d3.scale.ordinal() 31 | .domain(data.edgeCategories) 32 | .range(style.edgeColors); 33 | 34 | // Set up node coloring 35 | var nodeColor = d3.scale.linear() 36 | .domain([data.minNodeValue, data.maxNodeValue]) 37 | .range(style.nodeColor) 38 | .interpolate(d3.interpolateLab); 39 | 40 | var forceHeight = height, 41 | forceWidth = width; 42 | 43 | if(drawLegend) { 44 | var legend = svg.append('g'); 45 | drawLegendFn(legend); 46 | } 47 | 48 | // Set up force directed graph 49 | var force = d3.layout.force() 50 | .charge(-400) 51 | .linkDistance(100) 52 | .size([forceWidth,forceHeight]); 53 | 54 | var x = d3.scale.linear().range([0,forceWidth]), 55 | y = d3.scale.linear().range([0,forceHeight]); 56 | 57 | // Start the force directed layout 58 | force.nodes(data.nodes).links(data.links).start(); 59 | 60 | // Draw the edges 61 | var link = graph.append('g').selectAll('.link') 62 | .data(data.links) 63 | .enter() 64 | .append('g') 65 | .attr('class', 'gd3Link'); 66 | 67 | // Draw categories for each edge 68 | if(data.edgeCategories) { 69 | link.each(function(d) { 70 | var thisEdge = d3.select(this); 71 | d.categories.forEach(function(c) { 72 | thisEdge.append('line') 73 | .attr('class', instanceIDConst+'-'+c) 74 | .style('stroke-width', style.edgeWidth) 75 | .style('stroke', edgeColor(c)); 76 | }); 77 | }); 78 | } else { 79 | link.append('line').style('stroke-width', style.edgeWidth).style('stroke', edgeColor(null)); 80 | } 81 | 82 | link.selectAll('line').style('stroke-linecap', 'round'); 83 | 84 | 85 | // Draw the nodes 86 | var node = graph.append('g').selectAll('.node') 87 | .data(data.nodes) 88 | .enter() 89 | .append('g') 90 | .style('cursor', 'move') 91 | .attr('class', 'gd3Node') 92 | .call(force.drag); 93 | 94 | node.append('circle') 95 | .attr('r', style.nodeRadius) 96 | .attr('fill', function(d) { return nodeColor(d.value); }) 97 | .style('stroke-width', style.nodeStrokeWidth) 98 | .style('stroke', style.nodeStrokeColor); 99 | 100 | node.append('text') 101 | .attr('x', style.nodeRadius+style.nodeLabelPadding) 102 | .attr('y', style.nodeRadius+style.nodeLabelPadding) 103 | .style('font-family', style.fontFamily) 104 | .style('font-size', style.fontSize) 105 | .style('font-weight', style.nodeLabelFontWeight) 106 | .text(function(d) { return d.name; }); 107 | 108 | force.on('tick', function() { 109 | node.attr('transform', function(d) { 110 | var maxBound = style.nodeRadius+style.nodeStrokeWidth, 111 | minBoundX = forceWidth - style.nodeRadius - style.nodeStrokeWidth, 112 | minBoundY = forceHeight - style.nodeRadius - style.nodeStrokeWidth; 113 | if(drawLegend) maxBoundX = maxBound + style.legendWidth; 114 | else maxBoundX = maxBound; 115 | d.x = Math.max(maxBoundX, Math.min(minBoundX, d.x)); 116 | d.y = Math.max(maxBound, Math.min(minBoundY, d.y)); 117 | return 'translate('+ d.x + ',' + d.y + ')'; 118 | }); 119 | 120 | // position the edges 121 | link.each(function(d) { 122 | var thisEdgeSet = d3.select(this), 123 | categories = d.categories || [null], 124 | numCategories = categories.length; 125 | 126 | var offset = (numCategories/2) * style.edgeWidth; 127 | 128 | thisEdgeSet.selectAll('line').each(function(d,i) { 129 | var thisEdge = d3.select(this); 130 | thisEdge.attr('x1', d.source.x - offset + style.edgeWidth * i) 131 | .attr('x2', d.target.x - offset + style.edgeWidth * i) 132 | .attr('y1', d.source.y - offset + style.edgeWidth * i) 133 | .attr('y2', d.target.y - offset + style.edgeWidth * i); 134 | }); 135 | }); 136 | }); 137 | 138 | if(anchorNodesOnClick) { 139 | force.drag().on('dragstart', function(d) { 140 | d.fixed = true; 141 | //d3.select(this).select('circle').style('stroke-opacity', 0); 142 | }); 143 | node.on('dblclick', function(d) { 144 | d.fixed = d.fixed ? false : true; 145 | //d3.select(this).select('circle').style('stroke-opacity', 1); 146 | }); 147 | } // end anchorNodesOnClick block 148 | 149 | 150 | function drawLegendFn(legend) { 151 | legend.style('font-family', style.fontFamily); 152 | legend.append('rect') 153 | .attr('width',style.legendWidth) 154 | .attr('height', style.height) 155 | .style('fill', '#ffffff') 156 | .style('opacity', .95); 157 | 158 | var title = legend.append('text') 159 | .style('font-size', style.legendFontSize); 160 | 161 | title.selectAll('tspan') 162 | .data(data.title.split('\n')) 163 | .enter() 164 | .append('tspan') 165 | .attr('x', 0) 166 | .attr('dy', style.legendFontSize+2) 167 | .text(function(d){ return d; }); 168 | 169 | // Render the scale 170 | var titleHeight = title.node().getBBox().height + 4, 171 | scaleG = legend.append('g') 172 | .attr('transform','translate(0,'+titleHeight+')'); 173 | scaleG.append('text') 174 | .attr('x', style.legendScaleWidth + 2) 175 | .attr('y', style.legendFontSize) 176 | .style('font-size', style.legendFontSize) 177 | .text(data.maxNodeValue); 178 | scaleG.append('text') 179 | .attr('x', style.legendScaleWidth + 2) 180 | .attr('y', style.height/2) 181 | .style('font-size', style.legendFontSize) 182 | .text(data.minNodeValue); 183 | var colorScaleRect = scaleG.append('rect') 184 | .attr('height', style.height/2) 185 | .attr('width', style.legendScaleWidth) 186 | .attr('class', 'gd3GraphGradientLegend'); 187 | 188 | // Create a unique ID for the color map gradient in case multiple graphs are made 189 | var now = Date.now(), 190 | gradientId = 'gd3-graph-gradient'+now; 191 | 192 | // Configure the gradient to be mapped on to the legend 193 | var gradient = scaleG.append('svg:defs') 194 | .append('svg:linearGradient') 195 | .attr('id', gradientId) 196 | .attr('x1', '0%') 197 | .attr('y1', '100%') 198 | .attr('x2', '0%') 199 | .attr('y2', '0%'); 200 | 201 | var scaleRange = nodeColor.range(); 202 | scaleRange.forEach(function(c, i){ 203 | gradient.append('svg:stop') 204 | .attr('offset', i*1./(scaleRange.length-1)) 205 | .attr('stop-color', c) 206 | .attr('stop-opacity', 1); 207 | }); 208 | 209 | colorScaleRect.attr('fill', 'url(#'+gradientId+')'); 210 | 211 | // Add the edge keys to the graph 212 | var scaleHeight = scaleG.node().getBBox().height + 4, 213 | edgeKeys = legend.append('g').attr('class', 'gd3GraphNetworkLegend').selectAll('g') 214 | .data(data.edgeCategories) 215 | .enter() 216 | .append('g') 217 | .style('cursor', 'pointer') 218 | .on('click', function(category) { 219 | var catEdges = d3.selectAll('.'+instanceIDConst+'-'+category), 220 | visible = catEdges.style('opacity') == 1; 221 | d3.select(this).style("opacity", visible ? 0.5 : 1); 222 | catEdges.style('opacity', visible ? 0 : 1); 223 | }) 224 | .on('mouseover', function() { 225 | d3.select(this).selectAll('text').style('fill', 'red'); 226 | }) 227 | .on('mouseout', function() { 228 | d3.select(this).selectAll('text').style('fill', 'black'); 229 | }); 230 | edgeKeys.each(function(category, i) { 231 | var thisEl = d3.select(this), 232 | thisY = (i+1)*style.legendFontSize + titleHeight + scaleHeight; 233 | thisEl.append('line') 234 | .attr('x1', 0) 235 | .attr('y1', thisY - style.legendFontSize/4) 236 | .attr('x2', 15) 237 | .attr('y2', thisY - style.legendFontSize/4) 238 | .style('stroke', edgeColor(category)) 239 | .style('stroke-width', style.legendFontSize/2); 240 | 241 | thisEl.append('text') 242 | .attr('x', 16) 243 | .attr('y', (i+1)*style.legendFontSize + titleHeight + scaleHeight) 244 | .style('font-size', style.legendFontSize) 245 | .text(category); 246 | }); 247 | } 248 | 249 | // Add dispatch 250 | link.on("click.dispatch-interaction", function(d){ 251 | gd3.dispatch.interaction({ source: d.source.name, target: d.target.name }); 252 | }) 253 | }); 254 | } 255 | 256 | chart.clickAnchorsNodes = function(state) { 257 | anchorNodesOnClick = state; 258 | return chart; 259 | } 260 | 261 | chart.showLegend = function(state) { 262 | drawLegend = state; 263 | return chart; 264 | } 265 | 266 | return chart; 267 | } 268 | -------------------------------------------------------------------------------- /src/graph/graphData.js: -------------------------------------------------------------------------------- 1 | function graphData(inputData) { 2 | var data = { 3 | edges : [], 4 | nodes : [] 5 | } 6 | 7 | 8 | function defaultParse () { 9 | data.title = inputData.title || ''; 10 | data.edges = inputData.edges; 11 | data.nodes = inputData.nodes; 12 | data.links = loadLinks(data.edges,data.nodes); 13 | 14 | data.maxNodeValue = d3.max(data.nodes.map(function(d) { return d.value; })); 15 | data.minNodeValue = d3.min(data.nodes.map(function(d) { return d.value; })); 16 | 17 | data.edgeCategories = []; 18 | 19 | // add edge categories only if they exist 20 | var categories = {}; 21 | if(data.edges.length && data.edges[0].categories) { 22 | data.edges.forEach(function(e) { 23 | e.categories.forEach(function(c) { 24 | categories[c] = null; 25 | }); 26 | }); 27 | data.edgeCategories = Object.keys(categories); 28 | } 29 | 30 | // creates a force-directed layout friendly link list 31 | function loadLinks(edges, nodes) { 32 | var links = [], 33 | nodeToIndex = {}; 34 | 35 | nodes.forEach(function(n, i){ nodeToIndex[n.name] = i; }); 36 | edges.forEach(function(d){ 37 | links.push({ 38 | source: nodes[nodeToIndex[d.source]], 39 | target: nodes[nodeToIndex[d.target]], 40 | weight: d.weight, 41 | categories: d.categories, 42 | references: d.references 43 | }); 44 | }); 45 | 46 | return links; 47 | } // end loadLinks() 48 | } 49 | 50 | defaultParse(); 51 | 52 | return data; 53 | } -------------------------------------------------------------------------------- /src/graph/graphStyle.js: -------------------------------------------------------------------------------- 1 | function graphStyle(style) { 2 | return { 3 | edgeColors : style.edgeColors || d3.scale.category10().range(), 4 | edgeWidth : style.edgeWidth || 3, 5 | fontFamily : style.fontFamily || '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 6 | fontSize : style.fontSize || 12, 7 | height : style.height || 200, 8 | legendFontSize : style.legendFontSize || 11, 9 | legendScaleWidth : style.legendScaleWidth || 30, 10 | legendWidth : style.legendWidth || 75, 11 | margins : style.margins || {bottom: 0, left: 0, right: 0, top: 0}, 12 | nodeColor : style.nodeColor || ['#ccc','#ccc'], 13 | nodeRadius : style.nodeRadius || 10, 14 | nodeLabelPadding : style.nodeLabelPadding || 2, 15 | nodeLabelFontWeight : style.nodeLabelFontWeight || 'bold', 16 | nodeStrokeColor : style.nodeStrokeColor || '#333', 17 | nodeStrokeWidth : style.nodeStrokeWidth || 2, 18 | width : style.width || 300, 19 | }; 20 | } -------------------------------------------------------------------------------- /src/graph/index.js: -------------------------------------------------------------------------------- 1 | import "graph.js"; -------------------------------------------------------------------------------- /src/heatmap/heatmap.js: -------------------------------------------------------------------------------- 1 | import "heatmapChart.js"; 2 | import "heatmapStyle.js"; 3 | 4 | gd3.heatmap = function(params) { 5 | var params = params || {}, 6 | style = heatmapStyle(params.style || {}); 7 | 8 | // heatmapChart functions as a partial application, binding the given variables 9 | // into the returned instance. 10 | return heatmapChart(style); 11 | }; -------------------------------------------------------------------------------- /src/heatmap/heatmapData.js: -------------------------------------------------------------------------------- 1 | function heatmapData(inputData) { 2 | var data = { 3 | annotations : undefined, 4 | cells : [], 5 | maxCellValue : Number.NEGATIVE_INFINITY, 6 | minCellValue : Number.POSITIVE_INFINITY, 7 | xs : [], 8 | ys : [] 9 | } 10 | 11 | 12 | function defaultParse () { 13 | data.cells = inputData.cells; 14 | data.name = inputData.name; 15 | data.xs = inputData.xs; 16 | data.ys = inputData.ys; 17 | 18 | data.annotations = inputData.annotations; 19 | 20 | // Find max and min values to make color scale 21 | var tmp; 22 | for (var i=data.cells.length-1; i>=0; i--) { 23 | tmp = data.cells[i].value; 24 | if (tmp > data.maxCellValue) data.maxCellValue = tmp; 25 | if (tmp < data.minCellValue) data.minCellValue = tmp; 26 | } 27 | 28 | var datasetCatIndex = -1; 29 | if(data.annotations) { 30 | if(!data.annotations.annotationToColor) data.annotations.annotationToColor = {}; 31 | 32 | data.annotations.categories.forEach(function(category, categoryIndex) { 33 | var entry = data.annotations.annotationToColor[category]; 34 | if(entry && Object.keys(entry).length > 0) return; 35 | 36 | // Assume the data is continuous and find the min and max of the category 37 | var annotationNames = Object.keys(data.annotations.sampleToAnnotations), 38 | values = annotationNames.map(function(n) { 39 | return data.annotations.sampleToAnnotations[n][categoryIndex]; 40 | }); 41 | 42 | entry = [d3.min(values), d3.max(values)]; 43 | 44 | data.annotations.annotationToColor[category] = entry; 45 | 46 | }); 47 | // Set any missing column datasets to null 48 | data.annotations.categories.forEach(function(c, i){ 49 | if (c.toLowerCase() === "cancer type" || c.toLowerCase() === "dataset"){ 50 | datasetCatIndex = i; 51 | } 52 | }) 53 | } 54 | 55 | // If this category is giving the dataset (or cancer type) of the 56 | // column, add it to the map of column IDs to datasets 57 | data.columnIdToDataset = {}; 58 | if (datasetCatIndex !== -1){ 59 | data.xs.forEach(function(n){ 60 | data.columnIdToDataset[n] = data.annotations.sampleToAnnotations[n][datasetCatIndex]; 61 | }) 62 | } else { 63 | data.xs.forEach(function(n){ data.columnIdToDataset[n] = null; }); 64 | } 65 | } 66 | 67 | defaultParse(); 68 | 69 | data.sortColumns = function (columnIds) { 70 | data.xs.sort(function(a,b) { return columnIds.indexOf(a) - columnIds.indexOf(b); }); 71 | } 72 | 73 | 74 | return data; 75 | } 76 | -------------------------------------------------------------------------------- /src/heatmap/heatmapStyle.js: -------------------------------------------------------------------------------- 1 | function heatmapStyle(style) { 2 | return { 3 | annotationCellHeight : style.annotationCellHeight || 10, 4 | annotationCategorySpacing : style.annotationCategorySpacing || 5, 5 | annotationContinuousColorScale : style.annotationContinuousColorScale || ['#004529', '#f7fcb9'], 6 | annotationLabelFontSize : style.annotationLabelFontSize || style.fontSize || 12, 7 | cellHeight : style.cellHeight || 18, 8 | cellWidth : style.cellWidth || 14, 9 | colorScale : style.colorScale || ['rgb(222,235,247)','rgb(198,219,239)','rgb(158,202,225)','rgb(107,174,214)','rgb(66,146,198)','rgb(33,113,181)','rgb(8,81,156)','rgb(8,48,107)'], 10 | colorScaleHeight : style.colorScaleHeight || 14, 11 | colorScaleWidth : style.colorScaleWidth || 200, 12 | fontFamily : style.fontFamily || '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 13 | fontSize : style.fontSize || 14, 14 | labelMargins : style.labelMargins || {bottom: 5, right: 2}, 15 | margins : style.margins || {bottom: 0, left: 0, right: 0, top: 0}, 16 | noCellValueColor : style.noCellValueColor || '#a7a7a7', 17 | width : style.width || 400, 18 | }; 19 | } -------------------------------------------------------------------------------- /src/heatmap/index.js: -------------------------------------------------------------------------------- 1 | import "heatmap.js"; -------------------------------------------------------------------------------- /src/mutmtx/index.js: -------------------------------------------------------------------------------- 1 | import "mutmtx"; -------------------------------------------------------------------------------- /src/mutmtx/mutmtx.js: -------------------------------------------------------------------------------- 1 | import "../core/class"; 2 | import "mutmtxChart"; 3 | import "mutmtxStyle"; 4 | 5 | gd3.mutationMatrix = function(params) { 6 | var params = params || {}, 7 | style = mutmtxStyle(params.style || {}); 8 | 9 | // mutmtxChart functions as a partial application, binding the given variables 10 | // into the returned instance. 11 | return mutmtxChart(style); 12 | }; -------------------------------------------------------------------------------- /src/mutmtx/mutmtxData.js: -------------------------------------------------------------------------------- 1 | function mutmtxData(inputData) { 2 | var data = { 3 | datasets: [], 4 | glyphs: ['square', 'triangle-up', 'cross', 'circle', 'diamond', 'triangle-down'], 5 | hiddenColumns: { 6 | byCategory: [], 7 | byType: [] 8 | }, 9 | ids: { 10 | columns: [], 11 | rows: [] 12 | }, 13 | labels: { 14 | columns: [], 15 | rows: [] 16 | }, 17 | maps: { 18 | cellTypeToTick: inputData.cellTypeToTick || {snv: 'full', amp: 'up', del: 'down'}, 19 | cellTypeToLabel: inputData.cellTypeToLabel || {snv: 'SNV', inactive_snv: 'Inactivating SNV', amp: 'Amplification', del: 'Deletion'}, 20 | cellTypeToGlyph: inputData.cellTypeToGlyph || {snv: null, inactive_snv: 'square'}, 21 | cellTypeToSortIndex: inputData.cellTypeToSortIndex || {snv: 0, inactive_snv: 1, del: 2, amp: 3}, 22 | columnIdToLabel: {}, 23 | columnIdToCategory: {}, 24 | columnIdToTypes: {}, 25 | rowIdToLabel: {} 26 | }, 27 | matrix: { 28 | cells : {}, 29 | columnIdToActiveRows : {}, 30 | rowIdToActiveColumns : {} 31 | }, 32 | types: [] 33 | }; 34 | 35 | data.coverage = function(){ 36 | var mutatedSamples = d3.merge(data.ids.rows.map(function(d){ 37 | return data.matrix.rowIdToActiveColumns[d]; 38 | })), 39 | numMutatedSamples = d3.set(mutatedSamples).values().length, 40 | s = ((numMutatedSamples*100. / data.numSamples).toFixed(2)) + "%"; 41 | return s + " (" + numMutatedSamples + "/" + data.numSamples + ")"; 42 | } 43 | 44 | data.get = function(attr) { 45 | if (!attr) return null; 46 | else if (attr === 'datasets') return data.datasets; 47 | else if (attr === 'ids') return data.ids; 48 | else if (attr === 'labels') return data.labels; 49 | } 50 | 51 | data.reorderColumns = function(ordering) { 52 | // Sort by whether or not the column is visible (i.e., has been filtered) 53 | function sortByVisibility(c1, c2) { 54 | var c1Hidden = data.hiddenColumns.byCategory[c1] || data.hiddenColumns.byType[c1] ? true : false, 55 | c2Hidden = data.hiddenColumns.byCategory[c2] || data.hiddenColumns.byType[c2] ? true : false; 56 | 57 | if (c1Hidden == c2Hidden) return 0; 58 | else if (c1Hidden) return 1; 59 | else if (c2Hidden) return -1; 60 | else return 0; 61 | } 62 | 63 | // Sort by the column's most common cell type 64 | function sortByCellType(c1,c2) { 65 | var c1Type = data.maps.columnIdToTypes[c1][0], 66 | c2Type = data.maps.columnIdToTypes[c2][0]; 67 | return d3.ascending(data.maps.cellTypeToSortIndex[c1Type], data.maps.cellTypeToSortIndex[c2Type]); 68 | } 69 | // Sort by how exclusive each column's mutations are with one another 70 | function sortByExclusivity(c1, c2) { 71 | var c1X = data.matrix.columnIdToActiveRows[c1].length > 1, 72 | c2X = data.matrix.columnIdToActiveRows[c2].length > 1; 73 | return d3.ascending(c1X, c2X); 74 | } 75 | // Sort by which column has more "top" activations in the rendered graphic 76 | function sortByFirstActiveRow(c1, c2) { 77 | var c1First = data.matrix.columnIdToActiveRows[c1][0], 78 | c2First = data.matrix.columnIdToActiveRows[c2][0]; 79 | if (typeof(c1First) == 'undefined') c1First = Number.MAX_VALUE; 80 | if (typeof(c2First) == 'undefined') c2First = Number.MAX_VALUE; 81 | return d3.ascending(c1First, c2First); 82 | } 83 | // Sort by the name of the column 84 | function sortByName(c1,c2) { 85 | var c1Label = data.maps.columnIdToLabel[c1], 86 | c2Label = data.maps.columnIdToLabel[c2]; 87 | 88 | return d3.ascending(c1Label, c2Label); 89 | //return d3.ascending(data.labels.columns[c1],data.labels.columns[c2]); 90 | } 91 | // Sort by the column category (i.e, color) 92 | function sortByColumnCategory(c1,c2) { 93 | return d3.ascending(data.maps.columnIdToCategory[c1], data.maps.columnIdToCategory[c2]); 94 | } 95 | 96 | // Sort the data based on input, or if none, on default ordering 97 | var sortFns; 98 | if(ordering) { 99 | sortFns = [sortByVisibility]; 100 | ordering.forEach(function(d) { 101 | if(d == 'First active row') sortFns.push(sortByFirstActiveRow); 102 | if(d == 'Column category') sortFns.push(sortByColumnCategory); 103 | if(d == 'Exclusivity') sortFns.push(sortByExclusivity); 104 | if(d == 'Name') sortFns.push(sortByName); 105 | }); 106 | } 107 | else { 108 | sortFns = [sortByVisibility, sortByFirstActiveRow, sortByColumnCategory, sortByExclusivity, sortByCellType, sortByName]; 109 | } 110 | 111 | data.ids.columns.sort(function(c1,c2) { 112 | var sortResult; 113 | for(var i = 0; i < sortFns.length; i++) { 114 | sortResult = sortFns[i](c1,c2); 115 | if (sortResult != 0) { 116 | return sortResult; 117 | } 118 | } 119 | return sortResult; 120 | }); 121 | } // end data.reorderColumns() 122 | 123 | data.recomputeLabels = function(){ 124 | data.labels.rows = data.labels.rows.map(function(rowLabel){ 125 | var rowId = rowLabel.split(" (")[0], 126 | count = Object.keys(inputData.M[rowId]).reduce(function(sum, colId){ 127 | if (data.hiddenColumns.byCategory[colId] || data.hiddenColumns.byType[colId]) return sum; 128 | else return sum + 1; 129 | }, 0); 130 | return rowId + " (" + count + ")"; 131 | }); 132 | } 133 | 134 | function defaultParse() { 135 | // Scrape labels from the matrix 136 | inputData.samples.forEach(function(s) { 137 | data.maps.columnIdToLabel[s._id] = s.name; 138 | data.labels.columns.push(s.name); 139 | }); 140 | 141 | // Determine the total number of samples across all types 142 | if (inputData.typeToSamples && inputData.sampleTypes){ 143 | data.numSamples = inputData.sampleTypes.reduce(function(total, t){ 144 | return total + inputData.typeToSamples[t].length; 145 | },0); 146 | } else { 147 | data.numSamples = inputData.samples.length; 148 | } 149 | 150 | var rowAndCount = []; 151 | if (inputData.ordered_row_labels){ 152 | inputData.ordered_row_labels.forEach(function(k){ 153 | rowAndCount.push([k,Object.keys(inputData.M[k]).length]); 154 | }) 155 | } else { 156 | Object.keys(inputData.M).forEach(function(k, i) { 157 | // data.maps.rowIdToLabel[i.toString()] = k; 158 | var numSamples = Object.keys(inputData.M[k]).length; 159 | // data.labels.rows.push(k + ' ('+numSamples+')'); 160 | rowAndCount.push([k,numSamples]); 161 | }); 162 | 163 | rowAndCount.sort(function(a,b) { return a[1] < b[1] ? 1 : -1; }); 164 | } 165 | var sortedRowIds = []; 166 | rowAndCount.forEach(function(d, i) { 167 | var name = d[0], 168 | numSamples = d[1]; 169 | data.maps.rowIdToLabel[i.toString()] = name; 170 | data.labels.rows.push(name + ' ('+numSamples+')'); 171 | sortedRowIds.push(name); 172 | }); 173 | 174 | 175 | data.ids.columns = Object.keys(data.maps.columnIdToLabel); 176 | data.ids.rows = Object.keys(data.maps.rowIdToLabel); 177 | 178 | // Make set of datasets in data 179 | var setOfDatasets = {}; 180 | Object.keys(inputData.sampleToTypes).forEach(function(colId) { 181 | setOfDatasets[inputData.sampleToTypes[colId]] = null; 182 | data.maps.columnIdToCategory[colId] = inputData.sampleToTypes[colId]; 183 | }); 184 | data.datasets = Object.keys(setOfDatasets); 185 | 186 | // Build matrix data and maps 187 | var cellTypes = []; 188 | inputData.samples.forEach(function(d){ 189 | data.matrix.columnIdToActiveRows[d._id] = []; 190 | data.maps.columnIdToTypes[d._id] = []; 191 | }); 192 | 193 | sortedRowIds.forEach(function(rowLabel, rowId) { 194 | var columns = Object.keys(inputData.M[rowLabel]); 195 | rowId = rowId.toString(); 196 | // Add rowId -> columns mapping 197 | data.matrix.rowIdToActiveColumns[rowId] = columns; 198 | // Add columnId -> row mapping 199 | columns.forEach(function(colId) { 200 | // Add the row to the column 201 | data.matrix.columnIdToActiveRows[colId].push(rowId); 202 | 203 | // Add cell data 204 | var type = inputData.M[rowLabel][colId][0]; 205 | data.matrix.cells[[rowId,colId].join()] = { 206 | dataset: inputData.sampleToTypes[colId], 207 | type: inputData.M[rowLabel][colId][0] 208 | }; 209 | cellTypes.push(type); 210 | 211 | // Track the types of cells in the data 212 | data.maps.columnIdToTypes[colId].push(type); 213 | }); 214 | }); // end matrix mapping 215 | 216 | // Remove repeat types 217 | data.types = cellTypes.filter(function(item, pos, self) { 218 | return self.indexOf(item) == pos; 219 | }); 220 | 221 | // Process the column to type map s.t. there are no repeats and 222 | // the map is ordered by population of each type 223 | Object.keys(data.maps.columnIdToTypes).forEach(function(colId) { 224 | var types = data.maps.columnIdToTypes[colId], 225 | typeLog = {}; 226 | types.forEach(function(t) { 227 | if(!typeLog[t]) typeLog[t] = 0; 228 | typeLog[t] = typeLog[t] + 1; 229 | }); 230 | 231 | types = Object.keys(typeLog); 232 | types.sort(function(a,b) { return typeLog[a] < typeLog[b]; }); 233 | data.maps.columnIdToTypes[colId] = types; 234 | }); 235 | data.types.forEach(function(t){ 236 | if (!(t in data.maps.cellTypeToTick)){ 237 | data.maps.cellTypeToTick[t] = 'full'; 238 | } 239 | if (!(t in data.maps.cellTypeToLabel)){ 240 | data.maps.cellTypeToLabel[t] = t.replace("_", " "); 241 | } 242 | }) 243 | 244 | // Load the cell type to glyph mapping if it exists, else create it 245 | if (inputData.cellTypesToGlyph) { 246 | data.maps.cellTypeToGlyph = inputData.cellTypeToGlyph; 247 | } else { 248 | // Remove duplicates from the cellTypes array 249 | var typesTmp = {}; 250 | cellTypes.forEach(function(t) { 251 | if(typesTmp[t] == undefined) typesTmp[t] = 0; 252 | typesTmp[t] = typesTmp[t] + 1; 253 | }); 254 | var types = Object.keys(typesTmp).sort(function(a,b) { typesTmp[a] > typesTmp[b] }); 255 | 256 | types.forEach(function(d,i) { 257 | if (d in data.maps.cellTypeToGlyph) return; 258 | if (data.maps.cellTypeToTick[d] != 'full'){ 259 | data.maps.cellTypeToGlyph[d] = null; 260 | } else { 261 | data.maps.cellTypeToGlyph[d] = data.glyphs[i%data.glyphs.length]; 262 | } 263 | }); 264 | } // end glyph mapping 265 | } 266 | 267 | defaultParse(); 268 | 269 | // sample annotation data processing, if present 270 | if(inputData.annotations) { 271 | data.annotations = inputData.annotations; 272 | } else { 273 | data.annotations = { categories: [], sampleToAnnotations: {}, annotationToColor: {} }; 274 | data.ids.columns.forEach(function(s){ 275 | data.annotations.sampleToAnnotations[data.maps.columnIdToLabel[s]] = []; 276 | }); 277 | } 278 | 279 | // create simulated annotation data if it does not exist. 280 | // Object.keys(data.matrix.cells).forEach(function(key) { 281 | // if (data.matrix.cells[key].annotation == undefined) { 282 | // var vote = { 283 | // type: 'vote', 284 | // score: 100 285 | // } 286 | // var link = { 287 | // type: 'link', 288 | // href: 'http://www.cs.brown.edu', 289 | // text: 'BrownCS' 290 | // } 291 | // data.matrix.cells[key].annotation = [ 292 | // { 293 | // type: 'text', 294 | // title: 'Sample', 295 | // text: key 296 | // }, 297 | // { 298 | // type: 'table', 299 | // header: ['Cancer', 'PMIDs', 'Votes'], 300 | // data: [ 301 | // ['1', link, vote], 302 | // ['4', link, vote] 303 | // ] 304 | // } 305 | // ]; 306 | // } 307 | // }); // end simulated annotation data 308 | 309 | return data; 310 | } 311 | -------------------------------------------------------------------------------- /src/mutmtx/mutmtxStyle.js: -------------------------------------------------------------------------------- 1 | function mutmtxStyle(style) { 2 | return { 3 | animationSpeed : style.animationSpeed || 300, 4 | annotationContinuousScale : style.annotationContinuousScale || ['#fcc5c0','#49006a'], 5 | annotationFontSize : style.annotationFontSize || 10, 6 | annotationRowHeight : style.annotationRowHeight || 10, 7 | annotationRowSpacing : style.annotationRowSpacing || 5, 8 | bgColor : style.bgColor || '#F6F6F6', 9 | blockColorMedium : style.blockColorMedium || '#95A5A6', 10 | blockColorStrongest : style.blockColorStrongest || '#2C3E50', 11 | boxMargin : style.boxMargin || 5, // assumes uniform margins on all sides 12 | colorSampleTypes : style.colorSampleTypes || true, 13 | coocurringColor : style.coocurringColor || 'orange', 14 | exclusiveColor : style.exclusiveColor || 'blue', 15 | fontColor : style.fontColor || '#000', 16 | fontFamily : '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 17 | fontSize : style.fontSize || 14, 18 | glyphColor : style.glyphColor || '#888', 19 | glyphStrokeColor : style.glyphStrokeColor || '#ccc', 20 | height : style.height || 300, 21 | rowHeight : style.rowHeight || 20, 22 | labelHeight : style.labelHeight || 40, 23 | labelWidth : style.labelWidth || 100, 24 | minBoxWidth : style.minBoxWidth || 20, 25 | mutationLegendHeight : style.mutationLegendHeight || 30, 26 | sampleStroke : style.sampleStroke || 1, 27 | sortingMenuFontSize : style.sortingMenuFontSize || 12, 28 | width : style.width || 600, 29 | zBottom: 0, 30 | zTop: 100 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/scatterplot/index.js: -------------------------------------------------------------------------------- 1 | import "scatterplot" -------------------------------------------------------------------------------- /src/scatterplot/scatterplot.js: -------------------------------------------------------------------------------- 1 | import "scatterplotChart.js"; 2 | import "scatterplotStyle.js"; 3 | 4 | gd3.scatterplot = function(params) { 5 | var params = params || {}, 6 | style = scatterplotStyle(params.style || {}); 7 | 8 | return scatterplotChart(style); 9 | } -------------------------------------------------------------------------------- /src/scatterplot/scatterplotChart.js: -------------------------------------------------------------------------------- 1 | import "scatterplotData.js"; 2 | 3 | function scatterplotChart(style) { 4 | function chart(selection) { 5 | selection.each(function(data) { 6 | data = scatterplotData(data); 7 | 8 | // convenience function for rendering the scatterplot points 9 | function makeShape(d) { 10 | var category = d.category, 11 | hasCategory = data.categories.has(category), 12 | shape = hasCategory ? style.categoryShapes[data.categories.values().indexOf(category)] : 'circle', 13 | pt = d3.svg.symbol().type(shape); 14 | 15 | return pt.size(style.pointSize * style.pointSize)(); 16 | } 17 | 18 | 19 | var height = style.height - style.margins.top - style.margins.bottom, 20 | width = style.width - style.margins.left - style.margins.right; 21 | 22 | var pointColor = d3.scale.ordinal().domain(data.categories).range(style.categoryColors); 23 | 24 | var svg = d3.select(this) 25 | .selectAll('svg') 26 | .data([data]) 27 | .enter() 28 | .append('svg') 29 | .attr('height', style.height) 30 | .attr('width', style.width) 31 | .attr('xmlns', 'http://www.w3.org/2000/svg') 32 | .style('font-family', style.fontFamily) 33 | .style('font-size', style.fontSize); 34 | 35 | var scatterplot = svg.append('g').attr('transform', 'translate('+style.margins.left+','+style.margins.top+')'); 36 | 37 | // Construct x and y scales 38 | var x = d3.scale.linear().domain([data.xScale.min, data.xScale.max]).range([0, width]), 39 | y = d3.scale.linear().domain([data.yScale.min, data.yScale.max]).range([height, 0]); 40 | 41 | // Construct x and y axis 42 | var xAxis = d3.svg.axis().scale(x).orient('bottom'), 43 | yAxis = d3.svg.axis().scale(y).orient('left'); 44 | 45 | // Render both axis 46 | var axisStyle = { stroke: 'black', fill: 'none','shape-rendering': 'crispEdges', 'stroke-width': '1px'}, 47 | axisG = scatterplot.append('g').attr('class', 'gd3-scatterplot-axis'), 48 | xAxisRender = axisG.append('g') 49 | .attr('class', 'x axis') 50 | .attr('transform', 'translate(0,' + height + ')') 51 | .call(xAxis), 52 | xAxisLabel = axisG.append('text') 53 | .attr('text-anchor', 'middle') 54 | .attr('x', x(x.domain()[1])/2) 55 | .attr('y', height + xAxisRender.node().getBBox().height + style.axisFontSize) 56 | .style('font-size', style.axisFontSize) 57 | .text(data.xLabel), 58 | yAxisRender = axisG.append('g') 59 | .attr('class', 'y axis') 60 | .call(yAxis), 61 | yAxisLabel = axisG.append('text') 62 | .attr('text-anchor', 'middle') 63 | .attr('transform', 'rotate(-90)') 64 | .attr('x', -y(y.domain()[0])/2) 65 | .attr('y', -yAxisRender.node().getBBox().width) 66 | .text(data.yLabel); 67 | 68 | axisG.selectAll('.tick text').style('fill', 'black').style('font-size', style.axisFontSize); 69 | axisG.selectAll('path').style(axisStyle); 70 | 71 | // Make the title 72 | scatterplot.append('text') 73 | .attr('text-anchor', 'middle') 74 | .attr('x', x(x.domain()[1])/2) 75 | .style('font-size', style.titleFontSize) 76 | .style('font-weight', 'bold') 77 | .text(data.title); 78 | 79 | // Make the category legend 80 | var legendW = style.margins.right - style.legendPadding.left - style.legendPadding.right, 81 | legend = scatterplot.append('g') 82 | .attr('class', 'gd3-scatterplot-legend') 83 | .attr('transform', 'translate('+(width + style.legendPadding.left)+',0)'), 84 | legendCategories = legend.selectAll('.category') 85 | .data(data.categories.values()) 86 | .enter() 87 | .append('g') 88 | .attr('class', 'category'); 89 | 90 | legendCategories.each(function(d,i) { 91 | var thisEl = d3.select(this), 92 | shapeR = style.pointSize/2, 93 | shape = thisEl.append('path') 94 | .attr('d', makeShape) 95 | .attr('transform', 'translate(' + shapeR + ',' + (-shapeR) + ')') 96 | .style('fill', function(d) { return pointColor(d); }), 97 | text = thisEl.append('text') 98 | .attr('x', style.pointSize + 1) 99 | .text(d); 100 | 101 | thisEl.attr('transform','translate(0,' + i*(style.legendFontSize+2) + ')'); 102 | }); 103 | 104 | var pointsGroup = scatterplot.append('g').attr('class', 'gd3-scatterplot-points'); 105 | pointsGroup.selectAll('.point') 106 | .data(data.pts) 107 | .enter() 108 | .append('path') 109 | .attr('class', 'point') 110 | .attr('d', makeShape) 111 | .attr('transform', function(d) { return 'translate(' + x(d.x) + ',' + y(d.y) + ')'; }) 112 | .style('fill', function(d) { return pointColor(d.category); }); 113 | 114 | }); 115 | } 116 | 117 | 118 | return chart; 119 | } -------------------------------------------------------------------------------- /src/scatterplot/scatterplotData.js: -------------------------------------------------------------------------------- 1 | function scatterplotData(inputData) { 2 | var data = { 3 | categories: [], 4 | pts: [], 5 | title: '', 6 | xLabel: '', 7 | xScale: { max: Number.NEGATIVE_INFINITY, min: Number.POSITIVE_INFINITY }, 8 | yLabel: '', 9 | yScale: { max: Number.NEGATIVE_INFINITY, min: Number.POSITIVE_INFINITY } 10 | } 11 | 12 | // categories = Array of Strings 13 | // pts = Array of Objects s.t. each Object is {x: Number, y: Number}, 14 | // optionally {x: Number, y: Number, category: String} 15 | // title = String 16 | function parseJSON() { 17 | data.categories = d3.set(inputData.categories ? inputData.categories : []); 18 | data.pts = inputData.pts.map(function(d) { 19 | d.x = +(d.x); // safety sanitation 20 | d.y = +(d.y); 21 | 22 | if(d.category) data.categories.add(d.category); 23 | 24 | // include range tests to save a loop 25 | if (!inputData.xScale) { 26 | data.xScale.max = d3.max([d.x, data.xScale.max]); 27 | data.xScale.min = d3.min([d.x, data.xScale.min]); 28 | } 29 | if (!inputData.yScale) { 30 | data.yScale.max = d3.max([d.y, data.yScale.max]); 31 | data.yScale.min = d3.min([d.y, data.yScale.min]); 32 | } 33 | 34 | return d; 35 | }); 36 | 37 | data.title = inputData.title; 38 | 39 | data.xLabel = inputData.xLabel; 40 | data.yLabel = inputData.yLabel; 41 | 42 | if (inputData.xScale) data.xScale = inputData.xScale; 43 | if (inputData.yScale) data.yScale = inputData.yScale; 44 | } 45 | 46 | parseJSON(); 47 | 48 | return data; 49 | } -------------------------------------------------------------------------------- /src/scatterplot/scatterplotStyle.js: -------------------------------------------------------------------------------- 1 | function scatterplotStyle(style) { 2 | return { 3 | axisFontSize : style.axisFontSize || style.fontSize || 12, 4 | categoryColors : d3.scale.category10().range(), 5 | categoryShapes : ['circle', 'diamond', 'cross', 'triangle-down', 'square', 'triangle-up'], 6 | fontFamily : style.fontFamily || '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 7 | fontSize : style.fontSize || 12, 8 | height : style.height || 300, 9 | legendFontSize : style.legendFontSize || 11, 10 | legendScaleWidth : style.legendScaleWidth || 30, 11 | legendWidth : style.legendWidth || 75, 12 | pointSize : style.pointSize || 7, 13 | titleFontSize : style.titleFontSize || 14, 14 | width : style.width || 300, 15 | 16 | // Margins defines the chart rendering space whereas the margins serve as gutters for things 17 | // like the x axis ticks and labels and the chart title to be positioned 18 | margins : style.margins || {bottom: 50, left: 35, right: 15, top: style.titleFontSize || 14}, 19 | legendPadding : { top: 0, right: 0, bottom: 0, left: style.pointSize || 7 } 20 | }; 21 | } -------------------------------------------------------------------------------- /src/start.js: -------------------------------------------------------------------------------- 1 | !function() { 2 | var gd3 = {version: '0.2.1'}; -------------------------------------------------------------------------------- /src/tooltip/annotationView-old.js: -------------------------------------------------------------------------------- 1 | function annotationView(style, votingFns) { 2 | // Global variables for the annotation 3 | var point = null, 4 | svg = null, 5 | target = null; 6 | 7 | var votingFns = votingFns || {}; 8 | 9 | function view(selection) { 10 | // Append text to the annotation view 11 | function appendText(selection, data) { 12 | var title = data.title ? data.title+': ' : '', 13 | text = data.text ? data.text : ''; 14 | selection.append('p') 15 | .style('color', '#fff') 16 | .style('font-family', style.fontFamily) 17 | .style('font-size', style.fontSize) 18 | .style('margin', '0px') 19 | .style('padding', '0px') 20 | .text(title+text); 21 | } 22 | 23 | // Append link to the annotation view 24 | function appendLink(selection, data) { 25 | selection.append('a') 26 | .attr('href', data.href) 27 | .style('color', '#fff') 28 | .style('font-family', style.fontFamily) 29 | .style('font-size', style.fontSize) 30 | .style('margin', '0px') 31 | .style('padding', '0px') 32 | .text(data.text); 33 | } 34 | 35 | // Append a table to the annotation view 36 | function appendTable(selection, data) { 37 | var table = selection.append('table'), 38 | header = table.append('thead').append('tr'), 39 | body = table.append('tbody'); 40 | 41 | table.style({ 42 | 'border-collapse': 'collapse', 43 | 'border-bottom': '2px solid #ccc', 44 | 'border-top': '2px solid #ccc', 45 | 'margin-top': '3px' 46 | }); 47 | header.style('border-bottom', '1px solid #ccc'); 48 | header.selectAll('td') 49 | .data(data.header) 50 | .enter() 51 | .append('td') 52 | .style('color', '#fff') 53 | .style('font-family', style.fontFamily) 54 | .style('font-size', style.fontSize) 55 | .style('margin', '0px') 56 | .style('padding', '0 5px 2px 0') 57 | .text(function(d) { return d; }); 58 | 59 | var rows = body.selectAll('tr') 60 | .data(data.data) 61 | .enter() 62 | .append('tr'); 63 | 64 | var cells = rows.selectAll('td') 65 | .data(function(d) { return d; }) 66 | .enter() 67 | .append('td') 68 | .style('max-width', '115px') 69 | .style('padding', '0 3px 0 3px') 70 | .each(function(d) { 71 | if(typeof(d) === 'string') { 72 | appendText(d3.select(this), {text:d}); 73 | } else if (d.type === 'vote') { 74 | appendVote(d3.select(this), d); 75 | } else if (d.type === 'link') { 76 | appendLink(d3.select(this), d); 77 | } 78 | }); 79 | } 80 | 81 | // Append a voting counter 82 | function appendVote(selection, data) { 83 | var down = null, 84 | score = null, 85 | up = null; 86 | 87 | var defaultColor = 'rgb(255, 255, 255)', 88 | activeColor = 'rgb(255, 165, 0)'; 89 | 90 | function abstractVote(clickedArrow, otherArrow) { 91 | var upvote = clickedArrow == up, 92 | adjust = upvote ? 1 : -1; 93 | 94 | var scoreDatum = score.datum(); 95 | 96 | if (clickedArrow.style('color') == defaultColor) { 97 | // fix vote if the other vote direction active 98 | if (otherArrow.style('color') == activeColor) { 99 | score.text( parseInt(score.text()) + adjust ); 100 | } 101 | clickedArrow.style('color', activeColor); 102 | otherArrow.style('color', defaultColor); 103 | score.text(parseInt(score.text()) + adjust); 104 | scoreDatum.voted = upvote ? 'upVote' : 'downVote'; 105 | } else { 106 | clickedArrow.style('color', defaultColor); 107 | score.text(parseInt(score.text()) - adjust); 108 | scoreDatum.voted = 'none'; 109 | } 110 | 111 | scoreDatum.score = parseInt(score.text()); 112 | score.datum(scoreDatum); 113 | } // end abstractVote() 114 | 115 | 116 | function downVote(d) { 117 | abstractVote(down, up); 118 | 119 | if (votingFns.downVote != undefined) votingFns.downVote(d); 120 | } 121 | function upVote(d) { 122 | abstractVote(up, down); 123 | 124 | if (votingFns.upVote) votingFns.upVote(d); 125 | } 126 | 127 | 128 | var textStyle = { 129 | color: '#fff', 130 | display: 'inline-block', 131 | 'font-family': style.fontFamily, 132 | 'font-size': style.fontSize, 133 | margin: '0px', 134 | 135 | // prevent selection 136 | '-webkit-touch-callout': 'none', 137 | '-webkit-user-select': 'none', 138 | '-khtml-user-select': 'none', 139 | '-moz-user-select': 'none', 140 | '-ms-user-select': 'none', 141 | 'user-select': 'none' 142 | } 143 | down = selection.append('p') 144 | .style(textStyle) 145 | .style('padding', '0') 146 | .style('cursor', 'pointer') 147 | .text('▼') 148 | .on('click', downVote); 149 | score = selection.append('p') 150 | .style(textStyle) 151 | .style('padding', '0 1px 0 1px') 152 | .text(data.score); 153 | up = selection.append('p') 154 | .style(textStyle) 155 | .style('padding', '0') 156 | .style('cursor', 'pointer') 157 | .text('▲') 158 | .on('click', upVote); 159 | 160 | if (score.datum().voted != undefined) { 161 | if (score.datum().voted == 'upVote') up.style('color', activeColor); 162 | if (score.datum().voted == 'downVote') down.style('color', activeColor); 163 | } 164 | } 165 | 166 | 167 | // This function gets called whenever an element gets mouseovered 168 | function activate (d) { 169 | // Do nothing if no annotation data exists 170 | if (d.annotation == undefined && d.cell.annotation == undefined) { 171 | return; 172 | } 173 | 174 | // Update annotation globals 175 | target = target || d3.event.target; 176 | svg = target.tagName.toLowerCase() == 'svg' ? target : target.ownerSVGElement; 177 | if (d3.select(svg).select('SVGPoint').empty() == true) { 178 | point = svg.createSVGPoint(); 179 | } else { 180 | point = d3.select(svg).select('SVGPoint').node(); 181 | } 182 | 183 | 184 | var aData = d.annotation || d.cell.annotation, 185 | bbox = getScreenBBox(); 186 | 187 | // Remove any lingering tooltips that might exist 188 | d3.selectAll('.gd3AnnotationViewDiv').remove(); 189 | 190 | // Create the new tooltip 191 | var container = d3.select(document.createElement('div')); 192 | container.attr('class', 'gd3AnnotationViewDiv'); 193 | container.style({ 194 | background: 'rgba(0,0,0,.75)', 195 | 'border-radius': '3px', 196 | padding: '5px', 197 | position: 'absolute' 198 | }); 199 | 200 | for (var i in aData) { 201 | var aPart = aData[i], 202 | type = aPart.type; 203 | if (type == 'link') { 204 | appendLink(container, aPart); 205 | } else if (type == 'table') { 206 | appendTable(container, aPart); 207 | } else if (type == 'text') { 208 | appendText(container, aPart); 209 | } 210 | } 211 | 212 | document.body.appendChild(container.node()); 213 | 214 | // Determine positioning of the annotation 215 | var node = container.node(), 216 | scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft, 217 | scrollTop = document.documentElement.scrollTop || document.body.scrollTop, 218 | nodeL = bbox.s.x - node.offsetWidth / 2, 219 | nodeT = bbox.s.y; 220 | 221 | var offsetTop = nodeT + scrollTop + 2, 222 | offsetLeft = nodeL + scrollLeft; 223 | 224 | container.style('left', offsetLeft.toString() + 'px') 225 | .style('top', offsetTop.toString() + 'px'); 226 | 227 | // Add an "x-out" button for the annotation 228 | var xoutLeft = (node.offsetWidth - 10).toString() + 'px'; 229 | container.append('span') 230 | .text('☓') 231 | .on('click', function() { 232 | d3.selectAll('.gd3AnnotationViewDiv').remove(); 233 | }) 234 | .style({ 235 | color: '#000', 236 | cursor: 'pointer', 237 | display: 'inline', 238 | 'font-size': '10px', 239 | 'font-weight': 'bold', 240 | left: xoutLeft, 241 | 'line-height': 1, 242 | position: 'absolute', 243 | 'text-align': 'right', 244 | top: '-10px', 245 | width: '10px' 246 | }); 247 | 248 | // container.on('mouseout', function() { 249 | // d3.select(this).on('mouseout', null); // patch for mouseout behavior 250 | // document.body.removeChild(this); 251 | // }); 252 | } 253 | 254 | selection.on('mouseover', activate); 255 | } 256 | 257 | return view; 258 | } -------------------------------------------------------------------------------- /src/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import "tooltip"; 2 | 3 | import "tooltipElement"; 4 | 5 | import "tooltipImage"; 6 | import "tooltipText"; 7 | import "tooltipVote"; 8 | 9 | import "tooltipLink"; 10 | import "tooltipTable"; -------------------------------------------------------------------------------- /src/tooltip/tooltip-license.md: -------------------------------------------------------------------------------- 1 | The `gd3.tooltip()` component is based heavily on Justin Palmer's d3-tip add-on library (https://github.com/Caged/d3-tip), and as such we have included the MIT license specifically for this component. 2 | 3 | The MIT License (MIT) 4 | Copyright (c) 2013 Justin Palmer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/tooltip/tooltip.js: -------------------------------------------------------------------------------- 1 | import "tooltipStyle"; 2 | import "tooltipView"; 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // INFORMATION ABOUT GD3.TOOLTIP 6 | // 7 | // The gd3.tooltip() component is largely based on Justin Palmer's d3-tip 8 | // plug-in (https://github.com/Caged/d3-tip). Lots and lots of thanks, Justin! 9 | // 10 | // Differences: 11 | // 1. d3-tip is built such that only one tooltip can exist in a document, 12 | // we altered this behavior, such that each call of gd3.tooltip() creates 13 | // a brand new instance, meaning ∞ tooltips! 14 | // 2. to make making tooltips easier with complex data we've created a 15 | // primitive type language for tooltip data that in turn lead to content 16 | // factories that can automatically populate and format tooltips 17 | // 18 | 19 | gd3.tooltip = {}; 20 | 21 | gd3.tooltip.make = function(params) { 22 | var params = params || {}, 23 | style = tooltipStyle(params.style || {}), 24 | votingFns = params.votingFns || {}; 25 | 26 | // annotation functions as a partial application, binding the given variables 27 | // into the returned instance. 28 | return tooltipView(style); 29 | }; 30 | 31 | gd3.tooltip.data = function(ds) { 32 | return ds.map(function(d) { return gd3.tooltip.datum(d); }); 33 | } 34 | 35 | // tooltip datum convenience constructor 36 | gd3.tooltip.datum = function(d) { 37 | var elem = null; 38 | if(!d.type) elem = new gd3.tooltip.text(d.toString()); 39 | else if(d.type == 'image') elem = new gd3.tooltip.image(d.src, d.title); 40 | else if(d.type == 'link') elem = new gd3.tooltip.link(d.href, d.body); 41 | else if(d.type == 'table') elem = new gd3.tooltip.table(d.table); 42 | else if(d.type == 'text') elem = new gd3.tooltip.text(d.text); 43 | else if(d.type == 'vote') elem = new gd3.tooltip.vote(d.downvoteFn, d.upvoteFn, d.voteCountFn, d.voteDirectionFn); 44 | 45 | if (elem == null) return new gd3.tooltip.text(d.toString()); 46 | return elem.showSummary(d.defaultHidden); 47 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipElement.js: -------------------------------------------------------------------------------- 1 | gd3.tooltip.element = gd3_tooltipElement; 2 | 3 | function gd3_tooltipElement() { 4 | 5 | } 6 | 7 | gd3_tooltipElement.prototype.summaryElement = false; 8 | gd3_tooltipElement.prototype.showSummary = function(state) { 9 | state = state || false; 10 | this.summaryElement = state; 11 | return this; 12 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipImage.js: -------------------------------------------------------------------------------- 1 | import "tooltipElement"; 2 | 3 | gd3.tooltip.image = gd3_tooltipImage; 4 | 5 | function gd3_tooltipImage(src, title) { 6 | if (!this instanceof gd3_tooltipImage) return new gd3_tooltipImage(src, title); 7 | 8 | this.src = src; 9 | this.title = title; 10 | this.type = "link"; 11 | return this; 12 | } 13 | 14 | var gd3_tooltipImagePrototype = gd3_tooltipImage.prototype = new gd3_tooltipElement; 15 | 16 | gd3_tooltipImagePrototype.toString = function() { 17 | return this.title.toString(); 18 | }; 19 | 20 | gd3_tooltipImagePrototype.render = function(selection) { 21 | var thisTooltip = this; 22 | img = selection.append('img') 23 | .attr('src', this.src); 24 | if(this.title) img.attr('alt', this.title); 25 | 26 | img.attr('data-summaryElement', this.summaryElement); 27 | if(this.summaryElement) img.style('display', 'none'); 28 | return img; 29 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipLink.js: -------------------------------------------------------------------------------- 1 | import "tooltipElement"; 2 | 3 | gd3.tooltip.link = gd3_tooltipLink; 4 | 5 | function gd3_tooltipLink(href, body) { 6 | if (!this instanceof gd3_tooltipLink) return new gd3_tooltipLink(href, body); 7 | 8 | this.body = body; 9 | this.href = href; 10 | this.type = "link"; 11 | return this; 12 | } 13 | 14 | var gd3_tooltipLinkPrototype = gd3_tooltipLink.prototype = new gd3_tooltipElement; 15 | 16 | gd3_tooltipLinkPrototype.toString = function() { 17 | return this.body.toString(); 18 | }; 19 | 20 | gd3_tooltipLinkPrototype.render = function(selection) { 21 | var thisTooltip = this; 22 | a = selection.append('a') 23 | .attr('href', this.href); 24 | 25 | if (this.href[0] != '/') a.attr('target', '_new'); 26 | 27 | if(thisTooltip.body.render) thisTooltip.body.render(a); 28 | else a.text(thisTooltip.body.toString()); 29 | 30 | a.attr('data-summaryElement', this.summaryElement); 31 | if(this.summaryElement) a.style('display', 'none'); 32 | return a; 33 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipStyle.js: -------------------------------------------------------------------------------- 1 | function tooltipStyle(style) { 2 | return { 3 | background: style.background || 'rgba(0, 0, 0, 0.75)', 4 | border: style.border || '1px solid rgba(0,0,0,0.8)', 5 | borderRadius: style.borderRadius || '2px', 6 | fontColor: style.fontColor || '#ffffff', 7 | fontFamily: style.fontFamily || '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 8 | fontSize: '11px', 9 | height: style.height || 200, 10 | lineHeight: style.lineHeight || 1, 11 | padding: style.padding || '5px', 12 | voteActiveColor: style.voteActiveColor || '#ff0000', 13 | width: style.width || 500, 14 | }; 15 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipTable.js: -------------------------------------------------------------------------------- 1 | import "tooltipElement"; 2 | 3 | gd3.tooltip.table = gd3_tooltipTable; 4 | 5 | // Array should be a 2D array. 6 | function gd3_tooltipTable(array) { 7 | // TODO add a generator s.t. it makes 0-D array -> 1-D -> 2-D -> call gd3_tooltipTable(array) 8 | if (!this instanceof gd3_tooltipTable) return new gd3_tooltipTable(array); 9 | 10 | this.table = array; 11 | return this; 12 | } 13 | 14 | var gd3_tooltipTablePrototype = gd3_tooltipTable.prototype = new gd3_tooltipElement; 15 | 16 | gd3_tooltipTablePrototype.toString = function() { 17 | return this.body.toString(); 18 | }; 19 | 20 | gd3_tooltipTablePrototype.render = function(selection) { 21 | var thisTooltip = this; 22 | table = selection.append('table').attr('class', 'gd3-tooltip-table'), 23 | rows = table.selectAll('tr').data(thisTooltip.table).enter().append('tr'), 24 | cells = rows.selectAll('td').data(function(d) { return d; }).enter().append('td'); 25 | 26 | cells.each(function(d){ 27 | if(d.render) d.render(d3.select(this)); 28 | else d3.select(this).text(d.toString()); 29 | }); 30 | 31 | table.attr('data-summaryElement', this.summaryElement); 32 | if(this.summaryElement) table.style('display', 'none'); 33 | return table; 34 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipText.js: -------------------------------------------------------------------------------- 1 | import "tooltipElement"; 2 | 3 | gd3.tooltip.text = gd3_tooltipText; 4 | 5 | function gd3_tooltipText(text) { 6 | if (!this instanceof gd3_tooltipText) return new gd3_tooltipText(text); 7 | 8 | this.text = text; 9 | this.type = "text"; 10 | return this; 11 | } 12 | 13 | var gd3_tooltipTextPrototype = gd3_tooltipText.prototype = new gd3_tooltipElement; 14 | 15 | gd3_tooltipTextPrototype.toString = function() { 16 | return this.text; 17 | }; 18 | 19 | gd3_tooltipTextPrototype.render = function(selection) { 20 | var text = selection.append('span').text(this.text); 21 | text.attr('data-summaryElement', this.summaryElement); 22 | if(this.summaryElement) text.style('display', 'none'); 23 | else text.style('display', 'block'); 24 | return text; 25 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipView.js: -------------------------------------------------------------------------------- 1 | function tooltipView(style) { 2 | var clickCount = 0, 3 | clickEvents = {}, 4 | direction = d3_tip_direction, 5 | offset = d3_tip_offset, 6 | html = d3_tip_html, 7 | node = undefined, 8 | sticky = false, 9 | svg = null, 10 | point = null, 11 | target = null; 12 | 13 | 14 | function positionTooltip() { 15 | var args = Array.prototype.slice.call(arguments) 16 | 17 | // Obtain location information 18 | var poffset = offset.apply(this, args), 19 | coords, 20 | dir = direction.apply(this, args), 21 | i = directions.length, 22 | nodel = d3.select(node), 23 | scrollTop = document.documentElement.scrollTop || document.body.scrollTop, 24 | scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; 25 | 26 | while(i--) nodel.classed(directions[i], false); 27 | coords = direction_callbacks.get(dir).apply(this); 28 | nodel.classed(dir, true).style({ 29 | top: (coords.top + poffset[0]) + scrollTop + 'px', 30 | left: (coords.left + poffset[1]) + scrollLeft + 'px' 31 | }); 32 | } 33 | 34 | function view(selection) { 35 | svg = selection; // assumes selection is an SVG 36 | point = selection.node().createSVGPoint(); 37 | 38 | // node = d3.select(document.createElement('div')); 39 | node = d3.select('body').append('div'); 40 | node.attr('class','gd3-tooltip') 41 | node.style({ 42 | background: style.background, 43 | border: style.border, 44 | 'border-radius': style.borderRadius, 45 | color: style.fontColor, 46 | 'font-family': style.fontFamily, 47 | 'font-size': style.fontSize, 48 | 'line-height': style.lineHeight, 49 | overflow: 'hidden', 50 | position: 'absolute', 51 | top: 0, 52 | opacity: 0, 53 | 'pointer-events': 'none', 54 | 'box-sizing': 'border-box', 55 | padding: style.padding 56 | }); 57 | node = node.node(); 58 | 59 | // Create a listener for the body to close on keypress (ESC) 60 | var uniqueID = Date.now(); 61 | d3.select('body').on('keydown.gd3-tooltip-exit-'+uniqueID, function() { 62 | if (d3.event.keyCode == 27) { 63 | sticky = false; 64 | view.hide(); 65 | } 66 | }); 67 | 68 | var tipObjects = selection.selectAll('.gd3-tipobj') 69 | .on('click', function() { 70 | sticky = sticky ? false : true; 71 | if(sticky) { 72 | // view.render(); 73 | if(d3.event.type != 'click') return; 74 | d3.select(node).selectAll('*').each(function() { 75 | var thisEl = d3.select(this), 76 | isSummaryElement = thisEl.attr('data-summaryElement'); 77 | if(isSummaryElement) thisEl.style('display', 'block'); 78 | }); 79 | positionTooltip(); 80 | } 81 | else view.hide(); 82 | }) 83 | .on('mouseover', view.render ) 84 | .on('mouseout', view.hide ); 85 | } // end view 86 | 87 | view.render = function() { 88 | // if the node is sticky (i.e., has been clicked, or otherwise frozen) 89 | if (sticky) { 90 | // If the event isn't a click event, don't do anything 91 | if(d3.event.type != 'click') return; 92 | 93 | d3.select(node).selectAll('*').each(function() { 94 | var thisEl = d3.select(this), 95 | isSummaryElement = thisEl.attr('data-summaryElement'); 96 | if(isSummaryElement) thisEl.style('display', 'block'); 97 | }); 98 | positionTooltip(); 99 | return; 100 | } 101 | 102 | var args = Array.prototype.slice.call(arguments); 103 | if(args[args.length - 1] instanceof SVGElement) target = args.pop(); 104 | 105 | 106 | var content = html.apply(this, args), 107 | nodel = d3.select(node); 108 | 109 | var xout = 'X
'; 110 | nodel.html(xout); 111 | 112 | content.forEach(function(tipElem) { 113 | tipElem.render(nodel); 114 | }); 115 | nodel.style({ opacity: 1, 'pointer-events': 'all' }); 116 | 117 | nodel.select('.gd3-tooltip-xout') 118 | .on('click', function () { 119 | // Activate tipObejcts.on('click') and tipObjects.on('mouseover') 120 | sticky = sticky ? false : true; 121 | view.hide(); 122 | }); 123 | 124 | var clickEventObjs = nodel.selectAll('.clickEventObj'); 125 | if (clickEventObjs.empty() == false) { 126 | clickEventObjs.each(function() { 127 | var thisEl = d3.select(this), 128 | clickIndex = thisEl.attr('data-click'), 129 | clickEvent = clickEvents[clickIndex]; 130 | thisEl.on('click', clickEvent); 131 | }); 132 | } 133 | 134 | positionTooltip(); 135 | 136 | return view; 137 | } 138 | 139 | view.hide = function() { 140 | if (sticky) return; 141 | var nodel = d3.select(node); 142 | nodel.style({ opacity: 0, 'pointer-events': 'none' }); 143 | 144 | d3.select(node).selectAll('*').each(function() { 145 | var thisEl = d3.select(this), 146 | isSummaryElement = thisEl.attr('data-summaryElement'); 147 | if(isSummaryElement) thisEl.style('display', 'none'); 148 | }); 149 | return view; 150 | } 151 | 152 | view.attr = function(n, v) { 153 | if (arguments.length < 2 && typeof n === 'string') { 154 | return d3.select(node).attr(n) 155 | } else { 156 | var args = Array.prototype.slice.call(arguments) 157 | d3.selection.prototype.attr.apply(d3.select(node), args) 158 | } 159 | 160 | return view; 161 | } 162 | 163 | view.style = function(n, v) { 164 | if (arguments.length < 2 && typeof n === 'string') { 165 | return d3.select(node).style(n) 166 | } else { 167 | var args = Array.prototype.slice.call(arguments) 168 | d3.selection.prototype.style.apply(d3.select(node), args) 169 | } 170 | 171 | return view; 172 | } 173 | 174 | view.direction = function(v) { 175 | if (!arguments.length) return direction 176 | direction = v == null ? v : d3.functor(v) 177 | 178 | return view; 179 | } 180 | 181 | view.offset = function(v) { 182 | if (!arguments.length) return offset 183 | offset = v == null ? v : d3.functor(v) 184 | 185 | return view; 186 | } 187 | 188 | view.html = function(v) { 189 | if (!arguments.length) return html 190 | html = v == null ? v : d3.functor(v) 191 | 192 | return view; 193 | } 194 | 195 | // use the given data to generate an HTML string and proceed as normal 196 | view.useData = function(data) { 197 | function depth(d) { 198 | return Array.isArray(d) ? depth(d[0])+1 : 0; 199 | } 200 | 201 | var ghostNode = document.createElement('div'), 202 | nodel = d3.select(ghostNode); 203 | 204 | var dimensionality = depth(data); 205 | 206 | function registerClickEvent(selection) { 207 | if(selection.on('click')) { 208 | selection.attr('data-click', clickCount).classed('clickEventObj', true); 209 | clickEvents[clickCount] = selection.on('click'); 210 | clickCount = clickCount + 1; 211 | } 212 | } 213 | 214 | // Alter the rendering behavior based on the dimensionality of data 215 | if(dimensionality == 0) { 216 | html = d3.functor([data]); 217 | } else if (dimensionality == 1) { 218 | html = d3.functor(data); 219 | } else { 220 | html = d3.functor(function(d,i) {return data[i]; }); 221 | } 222 | 223 | return view; 224 | } 225 | 226 | ////////////////////////////////////////////////////////////////////////////////////////////////// 227 | // Private functions 228 | 229 | // Create functions for determining which direction the tip should render 230 | function d3_tip_direction() { return 'n' } 231 | function d3_tip_offset() { return [0, 0] } 232 | function d3_tip_html() { return ' ' } 233 | 234 | var direction_callbacks = d3.map({ 235 | n: direction_n, 236 | s: direction_s, 237 | e: direction_e, 238 | w: direction_w, 239 | nw: direction_nw, 240 | ne: direction_ne, 241 | sw: direction_sw, 242 | se: direction_se 243 | }), 244 | 245 | directions = direction_callbacks.keys(); 246 | 247 | function direction_n() { 248 | var bbox = getScreenBBox(); 249 | return { 250 | top: bbox.n.y - node.offsetHeight, 251 | left: bbox.n.x - node.offsetWidth / 2 252 | } 253 | } 254 | 255 | function direction_s() { 256 | var bbox = getScreenBBox(); 257 | return { 258 | top: bbox.s.y, 259 | left: bbox.s.x - node.offsetWidth / 2 260 | } 261 | } 262 | 263 | function direction_e() { 264 | var bbox = getScreenBBox(); 265 | return { 266 | top: bbox.e.y - node.offsetHeight / 2, 267 | left: bbox.e.x 268 | } 269 | } 270 | 271 | function direction_w() { 272 | var bbox = getScreenBBox(); 273 | return { 274 | top: bbox.w.y - node.offsetHeight / 2, 275 | left: bbox.w.x - node.offsetWidth 276 | } 277 | } 278 | 279 | function direction_nw() { 280 | var bbox = getScreenBBox(); 281 | return { 282 | top: bbox.nw.y - node.offsetHeight, 283 | left: bbox.nw.x - node.offsetWidth 284 | } 285 | } 286 | 287 | function direction_ne() { 288 | var bbox = getScreenBBox(); 289 | return { 290 | top: bbox.ne.y - node.offsetHeight, 291 | left: bbox.ne.x 292 | } 293 | } 294 | 295 | function direction_sw() { 296 | var bbox = getScreenBBox(); 297 | return { 298 | top: bbox.sw.y, 299 | left: bbox.sw.x - node.offsetWidth 300 | } 301 | } 302 | 303 | function direction_se() { 304 | var bbox = getScreenBBox(); 305 | return { 306 | top: bbox.se.y, 307 | left: bbox.e.x 308 | } 309 | } 310 | 311 | // Private - gets the screen coordinates of a shape 312 | // 313 | // Given a shape on the screen, will return an SVGPoint for the directions 314 | // n(north), s(south), e(east), w(west), ne(northeast), se(southeast), nw(northwest), 315 | // sw(southwest). 316 | // 317 | // +-+-+ 318 | // | | 319 | // + + 320 | // | | 321 | // +-+-+ 322 | // 323 | // Returns an Object {n, s, e, w, nw, sw, ne, se} 324 | function getScreenBBox() { 325 | var targetel = target || d3.event.target; 326 | 327 | while ('undefined' === typeof targetel.getScreenCTM && 'undefined' === targetel.parentNode) { 328 | targetel = targetel.parentNode; 329 | } 330 | 331 | var bbox = {}, 332 | matrix = targetel.getScreenCTM(), 333 | tbbox = targetel.getBBox(), 334 | width = tbbox.width, 335 | height = tbbox.height, 336 | x = tbbox.x, 337 | y = tbbox.y 338 | 339 | point.x = x 340 | point.y = y 341 | bbox.nw = point.matrixTransform(matrix) 342 | point.x += width 343 | bbox.ne = point.matrixTransform(matrix) 344 | point.y += height 345 | bbox.se = point.matrixTransform(matrix) 346 | point.x -= width 347 | bbox.sw = point.matrixTransform(matrix) 348 | point.y -= height / 2 349 | bbox.w = point.matrixTransform(matrix) 350 | point.x += width 351 | bbox.e = point.matrixTransform(matrix) 352 | point.x -= width / 2 353 | point.y -= height / 2 354 | bbox.n = point.matrixTransform(matrix) 355 | point.y += height 356 | bbox.s = point.matrixTransform(matrix) 357 | 358 | return bbox 359 | } 360 | 361 | // end private functions 362 | ////////////////////////////////////////////////////////////////////////////////////////////////// 363 | 364 | return view; 365 | } -------------------------------------------------------------------------------- /src/tooltip/tooltipVote.js: -------------------------------------------------------------------------------- 1 | import "tooltipElement"; 2 | 3 | gd3.tooltip.vote = gd3_tooltipVote; 4 | var gd3_tooltipVotePrototype = gd3_tooltipVote.prototype = new gd3_tooltipElement; 5 | 6 | function gd3_tooltipVote(downvoteFn, upvoteFn, voteCountFn, voteDirectionFn) { 7 | if (!this instanceof gd3_tooltipVote) { 8 | return new gd3_tooltipVote(downvoteFn, upvoteFn, voteCountFn, voteDirectionFn); 9 | } 10 | 11 | this.downvoteFn = downvoteFn; 12 | this.upvoteFn = upvoteFn; 13 | this.voteCountFn = voteCountFn; 14 | this.voteDirectionFn = voteDirectionFn; 15 | 16 | return this; 17 | } 18 | 19 | gd3_tooltipVotePrototype.toString = function() { 20 | if (typeof(this.voteCount) == 'function') return this.voteCount() + ' votes'; 21 | return this.voteCount + ' votes'; 22 | }; 23 | 24 | gd3_tooltipVotePrototype.render = function(selection) { 25 | var votingArea = selection.append('span').attr('class', 'gd3-tooltip-vote'), 26 | downVote = votingArea.append('span').text('▼').attr('class', 'gd3-tooltip-dvote'), 27 | upVote = votingArea.append('span').text('▲').attr('class', 'gd3-tooltip-uvote'), 28 | voteCount = votingArea.append('span') 29 | .attr('class', 'gd3-tooltip-votecount') 30 | .text(this.voteCountFn()); 31 | 32 | if(this.voteDirectionFn) { 33 | if (this.voteDirectionFn() == "down") { 34 | downVote.classed("gd3-vote-active", true); 35 | downVote.style("color", "goldenrod"); 36 | } else if (this.voteDirectionFn() == "up") { 37 | upVote.classed("gd3-vote-active", true); 38 | upVote.style("color", "goldenrod"); 39 | } 40 | } 41 | 42 | votingArea.style('display', 'block'); 43 | votingArea.selectAll('span').style({ 44 | display: 'inline-block' 45 | }); 46 | 47 | var downVoteFn = this.downVoteFn, 48 | thisVote = this; 49 | 50 | downVote.on('click', function(d) { 51 | // vote elements need to be redefined because of the way element rendering currently works 52 | var downVote = d3.select(this), 53 | upVote = d3.select(this.parentNode).select('.gd3-tooltip-uvote'), 54 | voteCount = d3.select(this.parentNode).select('.gd3-tooltip-votecount'); 55 | 56 | upVote.style('color', null); 57 | 58 | // Do nothing if the vote is already active 59 | if (downVote.classed('gd3-vote-active') == true) { 60 | downVote.classed('gd3-vote-active', false); 61 | downVote.style('color', null); 62 | } else { 63 | // Alter classes 64 | downVote.classed('gd3-vote-active', true); 65 | upVote.classed('gd3-vote-active', false); 66 | 67 | // Style downvote 68 | downVote.style('color', 'goldenrod'); 69 | } 70 | 71 | // Vote function always functions on click and passes current state 72 | var vote = thisVote.downvoteFn(d, downVote.classed('gd3-vote-active')); 73 | if (vote){ 74 | upVote.classed('gd3-vote-active', false); 75 | downVote.classed('gd3-vote-active', true); 76 | } 77 | voteCount.text( voteCount.datum().voteCountFn() ); 78 | }); 79 | 80 | 81 | upVote.on('click', function(d) { 82 | // vote elements need to be redefined because of the way element rendering currently works 83 | var downVote = d3.select(this.parentNode).select('.gd3-tooltip-dvote'), 84 | upVote = d3.select(this), 85 | voteCount = d3.select(this.parentNode).select('.gd3-tooltip-votecount'); 86 | 87 | downVote.style('color', null); 88 | 89 | if (upVote.classed('gd3-vote-active') == true) { 90 | upVote.classed('gd3-vote-active', false); 91 | upVote.style('color', null); 92 | } else { 93 | // Alter classes 94 | downVote.classed('gd3-vote-active', false); 95 | upVote.classed('gd3-vote-active', true); 96 | 97 | // Style upvote 98 | upVote.style('color', 'goldenrod'); 99 | } 100 | 101 | 102 | // Vote function always functions on click and passes current state 103 | var vote = thisVote.upvoteFn(d, upVote.classed('gd3-vote-active')); 104 | if (vote){ 105 | downVote.classed('gd3-vote-active', false); 106 | upVote.classed('gd3-vote-active', true); 107 | } 108 | voteCount.text( voteCount.datum().voteCountFn() ); 109 | }); 110 | 111 | 112 | var voteGlyphStyle = { 113 | cursor: 'pointer', 114 | '-moz-user-select': 'none', 115 | '-ms-user-select': 'none', 116 | '-o-user-select': 'none', 117 | '-webkit-user-select': 'none' 118 | } 119 | 120 | downVote.style(voteGlyphStyle); 121 | upVote.style(voteGlyphStyle); 122 | 123 | votingArea.attr('data-summaryElement', this.summaryElement); 124 | if(this.summaryElement) votingArea.style('display', 'none'); 125 | return votingArea; 126 | } -------------------------------------------------------------------------------- /src/transcript/index.js: -------------------------------------------------------------------------------- 1 | import "transcript"; -------------------------------------------------------------------------------- /src/transcript/transcript.js: -------------------------------------------------------------------------------- 1 | import "transcriptChart"; 2 | import "transcriptStyle"; 3 | 4 | gd3.transcript = function(params) { 5 | var params = params || {}, 6 | style = transcriptStyle(params.style || {}); 7 | 8 | // transcriptChart functions as a partial application, binding the given variables 9 | // into the returned instance. 10 | return transcriptChart(style); 11 | }; -------------------------------------------------------------------------------- /src/transcript/transcriptData.js: -------------------------------------------------------------------------------- 1 | function transcriptData(data) { 2 | function parseCancer(cdata) { 3 | var defaultInactivatingMutations = { 4 | "Nonsense_Mutation": true, 5 | "Frame_Shift_Del": true, 6 | "Frame_Shift_Ins":true, 7 | "Missense_Mutation": false, 8 | "Splice_Site": true, 9 | "In_Frame_Del": false, 10 | "In_Frame_Ins": false 11 | }; 12 | var defaultMutationTypesToSymbols = { 13 | "Nonsense_Mutation": 0, 14 | "Frame_Shift_Del": 1, 15 | "Frame_Shift_Ins": 1, 16 | "Missense_Mutation": 2, 17 | "Splice_Site": 3, 18 | "In_Frame_Del": 4, 19 | "In_Frame_Ins": 4 20 | }; 21 | 22 | // Select the initial protein domain db, going with the first one 23 | // if it's not provided. Then add the protein domain dbs to each of the domains 24 | // so we can easily toggle through the different databases 25 | var proteinDomainDB = cdata.proteinDomainDB || Object.keys(cdata.domains)[0] || ''; 26 | var domains = []; 27 | Object.keys(cdata.domains).forEach(function(db){ 28 | cdata.domains[db].forEach(function(d){ 29 | d.db = db; 30 | domains.push(d); 31 | }); 32 | }); 33 | 34 | var d = { 35 | geneName: cdata.gene, 36 | sequence: cdata.protein_sequence || null, 37 | sequence_annotations: cdata.sequence_annotations || [], 38 | inactivatingMutations: cdata.inactivatingMutations || defaultInactivatingMutations, 39 | length: cdata.length, 40 | mutationCategories: cdata.mutationCategories || [], 41 | mutations: cdata.mutations, 42 | mutationTypesToSymbols: cdata.mutationTypesToSymbols || defaultMutationTypesToSymbols, 43 | proteinDomainDB: proteinDomainDB, 44 | proteinDomains: domains || [] 45 | }; 46 | d.types = Object.keys(d.mutationTypesToSymbols); 47 | d.datasets = d3.set(cdata.mutations.map(function(m) { return m.dataset; })).values(); 48 | d.locusToAnnotations = {}; 49 | var seq_annotation_types = d3.set(); 50 | d.sequence_annotations.forEach(function(anno){ 51 | d.locusToAnnotations[anno.locus] = anno.annotation; 52 | seq_annotation_types.add(anno.annotation); 53 | }); 54 | d.seq_annotation_types = seq_annotation_types.values().sort(); 55 | 56 | // for (var mutation in d.mutations) { 57 | // var m = d.mutations[mutation]; 58 | 59 | // // create simulated annotation data if it does not exist. 60 | // if (m.annotation == undefined) { 61 | // var vote = { 62 | // type: 'vote', 63 | // score: 100 64 | // } 65 | // var link = { 66 | // type: 'link', 67 | // href: 'http://www.cs.brown.edu', 68 | // text: 'BrownCS' 69 | // } 70 | // m.annotation = [ 71 | // { 72 | // type: 'text', 73 | // title: 'Sample', 74 | // text: m.sample 75 | // }, 76 | // { 77 | // type: 'table', 78 | // header: ['Cancer', 'PMIDs', 'Votes'], 79 | // data: [ 80 | // ['1', link, vote], 81 | // ['4', link, vote] 82 | // ] 83 | // } 84 | // ]; 85 | // } // end simulated m.annotation 86 | // else { 87 | // console.log('defined annotation'); 88 | // } 89 | // } 90 | 91 | d.get = function(str) { 92 | if (str == 'length') return d.length; 93 | else if (str == 'datasets') return d.datasets; 94 | else if (str == 'mutations') return d.mutations; 95 | else if (str == 'mutationTypesToSymbols') return d.mutationTypesToSymbols; 96 | else if (str == 'proteinDomains') return d.proteinDomains; 97 | else if (str == 'sequence') return d.sequence; 98 | else return null; 99 | } 100 | d.isMutationInactivating = function(mut) { 101 | return d.inactivatingMutations[mut]; 102 | } 103 | // Find the first domain that includes the given locus 104 | d.domain = function(locus){ 105 | var loc = locus * 1; // convert to integer 106 | for (var i = 0; i < d.proteinDomains.length; i++){ 107 | if (d.proteinDomains[i].start < loc && d.proteinDomains[i].end > loc){ 108 | return d.proteinDomains[i].name; 109 | } 110 | } 111 | return null; 112 | } 113 | 114 | return d; 115 | } 116 | var tData = parseCancer(data); 117 | 118 | return tData; 119 | } 120 | -------------------------------------------------------------------------------- /src/transcript/transcriptStyle.js: -------------------------------------------------------------------------------- 1 | function transcriptStyle(style) { 2 | return { 3 | fontFamily: '"HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif', 4 | height: style.height || 250, 5 | numXTicks: style.numXTicks || 5, 6 | symbolWidth: style.symbolWidth || 20, 7 | transcriptBarHeight: style.transcriptBarHeight || 20, 8 | legendSymbolHeight: style.legendSymbolHeight || 14, 9 | width: style.width || 500, 10 | xTickPadding: style.xTickPadding || 1.25, 11 | scollbarWidth: style.scrollbarWidth || 15, 12 | legendTextWidth: style.legendTextWidth || 28, 13 | margin: style.margin || { left: 5, right: 15, top: 5, bottom: 0 }, 14 | seqAnnotationColors: {Phosphorylation: '#ff0000', Acetylation: '#00ff00', Ubiquitination: '#0000ff', Regulatory: 'rgb(44, 160, 44)', Methylation: 'rgb(255, 127, 14)', "Disease-associated": 'rgb(31, 119, 180)'} 15 | }; 16 | } -------------------------------------------------------------------------------- /test/placeholder.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raphael-group/gd3/44657c87ddc5a6b465617470ef58a01787fae85e/test/placeholder.md --------------------------------------------------------------------------------