├── .gitignore ├── Cakefile ├── examples ├── budgetForecast │ ├── annotation.js │ ├── data.js │ ├── index.html │ ├── timeline.js │ └── vis.js ├── habemusPapam │ ├── ellipsis.js │ ├── external-link.png │ └── index.html ├── lib │ ├── colorbrewer │ │ ├── LICENSE │ │ ├── colorbrewer.css │ │ └── colorbrewer.js │ ├── d3.v2.js │ ├── jquery-ui │ │ ├── LICENSE │ │ ├── images │ │ │ ├── ui-bg_diagonals-thick_18_b81900_40x40.png │ │ │ ├── ui-bg_diagonals-thick_20_666666_40x40.png │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ ├── ui-bg_flat_10_000000_40x100.png │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ ├── ui-bg_glass_100_f6f6f6_1x400.png │ │ │ ├── ui-bg_glass_100_fdf5ce_1x400.png │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ ├── ui-bg_gloss-wave_35_f6a828_500x100.png │ │ │ ├── ui-bg_highlight-soft_100_eeeeee_1x100.png │ │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ │ ├── ui-bg_highlight-soft_75_ffe45c_1x100.png │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_228ef1_256x240.png │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ ├── ui-icons_454545_256x240.png │ │ │ ├── ui-icons_888888_256x240.png │ │ │ ├── ui-icons_cd0a0a_256x240.png │ │ │ ├── ui-icons_ef8c08_256x240.png │ │ │ ├── ui-icons_ffd27a_256x240.png │ │ │ └── ui-icons_ffffff_256x240.png │ │ ├── jquery-ui.css │ │ └── jquery-ui.min.js │ ├── jquery │ │ ├── LICENSE │ │ ├── jquery.js │ │ └── jquery.min.js │ └── n3.js └── unemployment │ ├── annualRates.js │ ├── choropleth.js │ ├── data │ ├── annualData.json │ ├── counties-unemployment.json │ ├── county-ids.json │ ├── states-unemployment.json │ ├── us-counties.json │ ├── us-states.json │ └── validLocationIds.js │ ├── index.html │ ├── scenes.js │ └── unemployment.css ├── n3.js ├── package.json ├── src ├── annotation.coffee ├── scene.coffee ├── state.coffee ├── timeline.coffee ├── trigger.coffee ├── util.coffee ├── version.coffee └── vis.coffee ├── test ├── annotation.test.coffee ├── domTimingTest.html ├── scene.test.coffee ├── support │ ├── jasmine.yml │ ├── jasmine_config.rb │ └── jasmine_runner.rb ├── timeline.test.coffee ├── trigger.test.coffee └── vis.test.coffee └── ui ├── css ├── colorpicker.css ├── jquery-ui.css ├── n3-edit.css └── n3-play.css ├── imgs ├── accept.png ├── add-large.png ├── add.png ├── bind_data.png ├── blank.gif ├── colorpicker_background.png ├── colorpicker_hex.png ├── colorpicker_hsb_b.png ├── colorpicker_hsb_h.png ├── colorpicker_hsb_s.png ├── colorpicker_indic.gif ├── colorpicker_overlay.png ├── colorpicker_rgb_b.png ├── colorpicker_rgb_g.png ├── colorpicker_rgb_r.png ├── colorpicker_select.gif ├── colorpicker_submit.png ├── custom_background.png ├── custom_hex.png ├── custom_hsb_b.png ├── custom_hsb_h.png ├── custom_hsb_s.png ├── custom_indic.gif ├── custom_rgb_b.png ├── custom_rgb_g.png ├── custom_rgb_r.png ├── custom_submit.png ├── delete.png ├── drag-handle.png ├── draw-circle.png ├── draw-ellipse.png ├── draw-line.png ├── draw-rect.png ├── draw-text.png ├── edit.png ├── export.png ├── play.png ├── select.png ├── select2.png ├── slider.png ├── state.png ├── styles.png ├── trigger-empty.png ├── trigger.png ├── ui-bg_diagonals-thick_18_b81900_40x40.png ├── ui-bg_diagonals-thick_20_666666_40x40.png ├── ui-bg_flat_0_aaaaaa_40x100.png ├── ui-bg_flat_10_000000_40x100.png ├── ui-bg_flat_30_cccccc_40x100.png ├── ui-bg_flat_50_5c5c5c_40x100.png ├── ui-bg_flat_75_ffffff_40x100.png ├── ui-bg_glass_100_f6f6f6_1x400.png ├── ui-bg_glass_100_fdf5ce_1x400.png ├── ui-bg_glass_20_555555_1x400.png ├── ui-bg_glass_40_0078a3_1x400.png ├── ui-bg_glass_40_ffc73d_1x400.png ├── ui-bg_glass_55_fbf9ee_1x400.png ├── ui-bg_glass_65_ffffff_1x400.png ├── ui-bg_glass_75_dadada_1x400.png ├── ui-bg_glass_75_e6e6e6_1x400.png ├── ui-bg_glass_95_fef1ec_1x400.png ├── ui-bg_gloss-wave_25_333333_500x100.png ├── ui-bg_gloss-wave_35_f6a828_500x100.png ├── ui-bg_highlight-soft_100_eeeeee_1x100.png ├── ui-bg_highlight-soft_75_cccccc_1x100.png ├── ui-bg_highlight-soft_75_ffe45c_1x100.png ├── ui-bg_highlight-soft_80_eeeeee_1x100.png ├── ui-bg_inset-soft_25_000000_1x100.png ├── ui-bg_inset-soft_30_f58400_1x100.png ├── ui-icons_222222_256x240.png ├── ui-icons_228ef1_256x240.png ├── ui-icons_2e83ff_256x240.png ├── ui-icons_454545_256x240.png ├── ui-icons_4b8e0b_256x240.png ├── ui-icons_888888_256x240.png ├── ui-icons_a83300_256x240.png ├── ui-icons_cccccc_256x240.png ├── ui-icons_cd0a0a_256x240.png ├── ui-icons_ef8c08_256x240.png ├── ui-icons_ffd27a_256x240.png ├── ui-icons_ffffff_256x240.png └── widget.png ├── index.html ├── js ├── colorpicker.js ├── d3.min.js ├── jquery-ui.min.js ├── jquery.chromatable.js ├── jquery.getPath.js ├── jquery.json-2.3.min.js ├── jquery.min.js ├── n3-edit.js ├── n3-play.js └── n3.js └── play.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .jhw-cache -------------------------------------------------------------------------------- /Cakefile: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | {print} = require 'util' 3 | {spawn, exec} = require 'child_process' 4 | 5 | task 'build', 'compile coffee files and concatenate', -> 6 | files = ['version', 'util', 'state', 'vis', 'annotation', 'trigger', 7 | 'scene', 'timeline'] 8 | 9 | args = ['--compile', '--join', 'n3.js'] 10 | args.push "src/#{file}.coffee" for file in files 11 | 12 | coffee = spawn 'coffee', args 13 | coffee.stdout.on 'data', (data) -> print data.toString() 14 | coffee.stderr.on 'data', (data) -> print data.toString() 15 | 16 | 17 | task 'test', 'test against the specs', -> 18 | args = ['-c', '-j', 'test/support/jasmine.yml'] 19 | jasmine = spawn 'jasmine-headless-webkit', args 20 | jasmine.stdout.on 'data', (data) -> print data.toString() 21 | jasmine.stderr.on 'data', (data) -> print data.toString() -------------------------------------------------------------------------------- /examples/budgetForecast/annotation.js: -------------------------------------------------------------------------------- 1 | n3.annotation.def('highlightedPoint') 2 | .enter(function() { 3 | var vis = this.vis(); 4 | var sx = vis.const('sx')(); 5 | var sy = vis.const('sy')(); 6 | 7 | 8 | var point = vis.stage().selectAll('circle.point') 9 | .data(this.data(), function(d) { return d.budgetYear; }); 10 | 11 | point.enter() 12 | .append('svg:circle') 13 | .attr('class', 'point') 14 | .attr('cx', function(d) { return sx(d.forecastYear) }) 15 | .attr('cy', function(d) { return sy(d.value) }) 16 | .attr('r', 2.5); 17 | 18 | point.transition() 19 | .attr('cx', function(d) { return sx(d.forecastYear) }) 20 | .attr('cy', function(d) { return sy(d.value) }); 21 | 22 | point.exit().remove(); 23 | 24 | var annotCircle = vis.stage().selectAll('circle.annotation') 25 | .data(this.data()); 26 | 27 | annotCircle.enter() 28 | .append('svg:circle') 29 | .attr('class', 'annotation') 30 | .attr('cx', function(d) { return sx(d.forecastYear) }) 31 | .attr('cy', function(d) { return sy(d.value) }) 32 | .attr('r', 7); 33 | 34 | annotCircle.transition() 35 | .attr('cx', function(d) { return sx(d.forecastYear) }) 36 | .attr('cy', function(d) { return sy(d.value) }); 37 | 38 | annotCircle.exit().remove(); 39 | }) 40 | .exit(function() { 41 | var vis = this.vis(); 42 | vis.stage().selectAll('circle.point').remove(); 43 | vis.stage().selectAll('circle.annotation').remove(); 44 | }); 45 | 46 | -------------------------------------------------------------------------------- /examples/budgetForecast/data.js: -------------------------------------------------------------------------------- 1 | var fullData = [ 2 | {"budgetYear": 1980, "forecastYear": 1980, "value": -103}, 3 | {"budgetYear": 1980, "forecastYear": 1981, "value": -37}, 4 | {"budgetYear": 1980, "forecastYear": 1982, "value": 10}, 5 | {"budgetYear": 1980, "forecastYear": 1983, "value": 51}, 6 | {"budgetYear": 1981, "forecastYear": 1980, "value": -192}, 7 | {"budgetYear": 1981, "forecastYear": 1981, "value": -129}, 8 | {"budgetYear": 1981, "forecastYear": 1982, "value": -60}, 9 | {"budgetYear": 1981, "forecastYear": 1983, "value": -17}, 10 | {"budgetYear": 1981, "forecastYear": 1984, "value": 63}, 11 | {"budgetYear": 1982, "forecastYear": 1981, "value": -185}, 12 | {"budgetYear": 1982, "forecastYear": 1982, "value": -215}, 13 | {"budgetYear": 1982, "forecastYear": 1983, "value": -190}, 14 | {"budgetYear": 1982, "forecastYear": 1984, "value": -164}, 15 | {"budgetYear": 1982, "forecastYear": 1985, "value": -137}, 16 | {"budgetYear": 1983, "forecastYear": 1982, "value": -280}, 17 | {"budgetYear": 1983, "forecastYear": 1983, "value": -432}, 18 | {"budgetYear": 1983, "forecastYear": 1984, "value": -373}, 19 | {"budgetYear": 1983, "forecastYear": 1985, "value": -371}, 20 | {"budgetYear": 1983, "forecastYear": 1986, "value": -275}, 21 | {"budgetYear": 1984, "forecastYear": 1983, "value": -432}, 22 | {"budgetYear": 1984, "forecastYear": 1984, "value": -363}, 23 | {"budgetYear": 1984, "forecastYear": 1985, "value": -345}, 24 | {"budgetYear": 1984, "forecastYear": 1986, "value": -330}, 25 | {"budgetYear": 1984, "forecastYear": 1987, "value": -326}, 26 | {"budgetYear": 1984, "forecastYear": 1988, "value": -267}, 27 | {"budgetYear": 1984, "forecastYear": 1989, "value": -209}, 28 | {"budgetYear": 1985, "forecastYear": 1984, "value": -367}, 29 | {"budgetYear": 1985, "forecastYear": 1985, "value": -425}, 30 | {"budgetYear": 1985, "forecastYear": 1986, "value": -336}, 31 | {"budgetYear": 1985, "forecastYear": 1987, "value": -298}, 32 | {"budgetYear": 1985, "forecastYear": 1988, "value": -253}, 33 | {"budgetYear": 1985, "forecastYear": 1989, "value": -182}, 34 | {"budgetYear": 1985, "forecastYear": 1990, "value": -135}, 35 | {"budgetYear": 1986, "forecastYear": 1985, "value": -406}, 36 | {"budgetYear": 1986, "forecastYear": 1986, "value": -378}, 37 | {"budgetYear": 1986, "forecastYear": 1987, "value": -260}, 38 | {"budgetYear": 1986, "forecastYear": 1988, "value": -164}, 39 | {"budgetYear": 1986, "forecastYear": 1989, "value": -114}, 40 | {"budgetYear": 1987, "forecastYear": 1986, "value": -412}, 41 | {"budgetYear": 1987, "forecastYear": 1987, "value": -313}, 42 | {"budgetYear": 1987, "forecastYear": 1988, "value": -189}, 43 | {"budgetYear": 1987, "forecastYear": 1989, "value": -157}, 44 | {"budgetYear": 1987, "forecastYear": 1990, "value": -98}, 45 | {"budgetYear": 1987, "forecastYear": 1991, "value": -33}, 46 | {"budgetYear": 1987, "forecastYear": 1992, "value": 19}, 47 | {"budgetYear": 1988, "forecastYear": 1987, "value": -271}, 48 | {"budgetYear": 1988, "forecastYear": 1988, "value": -257}, 49 | {"budgetYear": 1988, "forecastYear": 1989, "value": -219}, 50 | {"budgetYear": 1988, "forecastYear": 1990, "value": -166}, 51 | {"budgetYear": 1988, "forecastYear": 1991, "value": -124}, 52 | {"budgetYear": 1988, "forecastYear": 1992, "value": -77}, 53 | {"budgetYear": 1988, "forecastYear": 1993, "value": -34}, 54 | {"budgetYear": 1989, "forecastYear": 1988, "value": -272}, 55 | {"budgetYear": 1989, "forecastYear": 1989, "value": -273}, 56 | {"budgetYear": 1989, "forecastYear": 1990, "value": -152}, 57 | {"budgetYear": 1989, "forecastYear": 1991, "value": -105}, 58 | {"budgetYear": 1989, "forecastYear": 1992, "value": -49}, 59 | {"budgetYear": 1989, "forecastYear": 1993, "value": -4}, 60 | {"budgetYear": 1989, "forecastYear": 1994, "value": 48}, 61 | {"budgetYear": 1990, "forecastYear": 1989, "value": -258}, 62 | {"budgetYear": 1990, "forecastYear": 1990, "value": -203}, 63 | {"budgetYear": 1990, "forecastYear": 1991, "value": -99}, 64 | {"budgetYear": 1990, "forecastYear": 1992, "value": -38}, 65 | {"budgetYear": 1990, "forecastYear": 1993, "value": 8}, 66 | {"budgetYear": 1990, "forecastYear": 1994, "value": 15}, 67 | {"budgetYear": 1990, "forecastYear": 1995, "value": 13}, 68 | {"budgetYear": 1991, "forecastYear": 1990, "value": -363}, 69 | {"budgetYear": 1991, "forecastYear": 1991, "value": -499}, 70 | {"budgetYear": 1991, "forecastYear": 1992, "value": -424}, 71 | {"budgetYear": 1991, "forecastYear": 1993, "value": -296}, 72 | {"budgetYear": 1991, "forecastYear": 1994, "value": -89}, 73 | {"budgetYear": 1991, "forecastYear": 1995, "value": 4}, 74 | {"budgetYear": 1991, "forecastYear": 1996, "value": 27}, 75 | {"budgetYear": 1992, "forecastYear": 1991, "value": -422}, 76 | {"budgetYear": 1992, "forecastYear": 1992, "value": -603}, 77 | {"budgetYear": 1992, "forecastYear": 1993, "value": -514}, 78 | {"budgetYear": 1992, "forecastYear": 1994, "value": -306}, 79 | {"budgetYear": 1992, "forecastYear": 1995, "value": -272}, 80 | {"budgetYear": 1992, "forecastYear": 1996, "value": -249}, 81 | {"budgetYear": 1992, "forecastYear": 1997, "value": -252}, 82 | {"budgetYear": 1993, "forecastYear": 1992, "value": -438}, 83 | {"budgetYear": 1993, "forecastYear": 1993, "value": -465}, 84 | {"budgetYear": 1993, "forecastYear": 1994, "value": -367}, 85 | {"budgetYear": 1993, "forecastYear": 1995, "value": -323}, 86 | {"budgetYear": 1993, "forecastYear": 1996, "value": -258}, 87 | {"budgetYear": 1993, "forecastYear": 1997, "value": -243}, 88 | {"budgetYear": 1994, "forecastYear": 1993, "value": -375}, 89 | {"budgetYear": 1994, "forecastYear": 1994, "value": -339}, 90 | {"budgetYear": 1994, "forecastYear": 1995, "value": -232}, 91 | {"budgetYear": 1994, "forecastYear": 1996, "value": -233}, 92 | {"budgetYear": 1994, "forecastYear": 1997, "value": -250}, 93 | {"budgetYear": 1994, "forecastYear": 1998, "value": -253}, 94 | {"budgetYear": 1994, "forecastYear": 1999, "value": -238}, 95 | {"budgetYear": 1995, "forecastYear": 1994, "value": -293}, 96 | {"budgetYear": 1995, "forecastYear": 1995, "value": -270}, 97 | {"budgetYear": 1995, "forecastYear": 1996, "value": -270}, 98 | {"budgetYear": 1995, "forecastYear": 1997, "value": -286}, 99 | {"budgetYear": 1995, "forecastYear": 1998, "value": -261}, 100 | {"budgetYear": 1995, "forecastYear": 1999, "value": -259}, 101 | {"budgetYear": 1995, "forecastYear": 2000, "value": -249}, 102 | {"budgetYear": 1996, "forecastYear": 1995, "value": -230}, 103 | {"budgetYear": 1996, "forecastYear": 1996, "value": -200}, 104 | {"budgetYear": 1996, "forecastYear": 1997, "value": -188}, 105 | {"budgetYear": 1996, "forecastYear": 1998, "value": -130}, 106 | {"budgetYear": 1996, "forecastYear": 1999, "value": -84}, 107 | {"budgetYear": 1996, "forecastYear": 2000, "value": -35}, 108 | {"budgetYear": 1996, "forecastYear": 2001, "value": 10}, 109 | {"budgetYear": 1996, "forecastYear": 2002, "value": 54}, 110 | {"budgetYear": 1997, "forecastYear": 1996, "value": -147}, 111 | {"budgetYear": 1997, "forecastYear": 1997, "value": -169}, 112 | {"budgetYear": 1997, "forecastYear": 1998, "value": -160}, 113 | {"budgetYear": 1997, "forecastYear": 1999, "value": -154}, 114 | {"budgetYear": 1997, "forecastYear": 2000, "value": -112}, 115 | {"budgetYear": 1997, "forecastYear": 2001, "value": -45}, 116 | {"budgetYear": 1997, "forecastYear": 2002, "value": 21}, 117 | {"budgetYear": 1998, "forecastYear": 1997, "value": -29}, 118 | {"budgetYear": 1998, "forecastYear": 1998, "value": -13}, 119 | {"budgetYear": 1998, "forecastYear": 1999, "value": 12}, 120 | {"budgetYear": 1998, "forecastYear": 2000, "value": 11}, 121 | {"budgetYear": 1998, "forecastYear": 2001, "value": 35}, 122 | {"budgetYear": 1998, "forecastYear": 2002, "value": 110}, 123 | {"budgetYear": 1998, "forecastYear": 2003, "value": 98}, 124 | {"budgetYear": 1998, "forecastYear": 2004, "value": 127}, 125 | {"budgetYear": 1998, "forecastYear": 2005, "value": 153}, 126 | {"budgetYear": 1998, "forecastYear": 2006, "value": 178}, 127 | {"budgetYear": 1998, "forecastYear": 2007, "value": 228}, 128 | {"budgetYear": 1999, "forecastYear": 1998, "value": 92}, 129 | {"budgetYear": 1999, "forecastYear": 1999, "value": 104}, 130 | {"budgetYear": 1999, "forecastYear": 2000, "value": 150}, 131 | {"budgetYear": 1999, "forecastYear": 2001, "value": 167}, 132 | {"budgetYear": 1999, "forecastYear": 2002, "value": 229}, 133 | {"budgetYear": 1999, "forecastYear": 2003, "value": 218}, 134 | {"budgetYear": 1999, "forecastYear": 2004, "value": 242}, 135 | {"budgetYear": 2000, "forecastYear": 1999, "value": 165}, 136 | {"budgetYear": 2000, "forecastYear": 2000, "value": 214}, 137 | {"budgetYear": 2000, "forecastYear": 2001, "value": 230}, 138 | {"budgetYear": 2000, "forecastYear": 2002, "value": 229}, 139 | {"budgetYear": 2000, "forecastYear": 2003, "value": 221}, 140 | {"budgetYear": 2000, "forecastYear": 2004, "value": 227}, 141 | {"budgetYear": 2000, "forecastYear": 2005, "value": 241}, 142 | {"budgetYear": 2000, "forecastYear": 2006, "value": 277}, 143 | {"budgetYear": 2000, "forecastYear": 2007, "value": 308}, 144 | {"budgetYear": 2000, "forecastYear": 2008, "value": 320}, 145 | {"budgetYear": 2000, "forecastYear": 2009, "value": 334}, 146 | {"budgetYear": 2000, "forecastYear": 2010, "value": 363}, 147 | {"budgetYear": 2001, "forecastYear": 2000, "value": 302}, 148 | {"budgetYear": 2001, "forecastYear": 2001, "value": 351}, 149 | {"budgetYear": 2001, "forecastYear": 2002, "value": 284}, 150 | {"budgetYear": 2001, "forecastYear": 2003, "value": 290}, 151 | {"budgetYear": 2001, "forecastYear": 2004, "value": 305}, 152 | {"budgetYear": 2001, "forecastYear": 2005, "value": 302}, 153 | {"budgetYear": 2001, "forecastYear": 2006, "value": 331}, 154 | {"budgetYear": 2001, "forecastYear": 2007, "value": 359}, 155 | {"budgetYear": 2001, "forecastYear": 2008, "value": 380}, 156 | {"budgetYear": 2001, "forecastYear": 2009, "value": 427}, 157 | {"budgetYear": 2001, "forecastYear": 2010, "value": 465}, 158 | {"budgetYear": 2001, "forecastYear": 2011, "value": 519}, 159 | {"budgetYear": 2002, "forecastYear": 2001, "value": 160}, 160 | {"budgetYear": 2002, "forecastYear": 2002, "value": -130}, 161 | {"budgetYear": 2002, "forecastYear": 2003, "value": -96}, 162 | {"budgetYear": 2002, "forecastYear": 2004, "value": -16}, 163 | {"budgetYear": 2002, "forecastYear": 2005, "value": 68}, 164 | {"budgetYear": 2002, "forecastYear": 2006, "value": 93}, 165 | {"budgetYear": 2002, "forecastYear": 2007, "value": 110}, 166 | {"budgetYear": 2003, "forecastYear": 2002, "value": -194}, 167 | {"budgetYear": 2003, "forecastYear": 2003, "value": -364}, 168 | {"budgetYear": 2003, "forecastYear": 2004, "value": -357}, 169 | {"budgetYear": 2003, "forecastYear": 2005, "value": -233}, 170 | {"budgetYear": 2003, "forecastYear": 2006, "value": -218}, 171 | {"budgetYear": 2003, "forecastYear": 2007, "value": -188}, 172 | {"budgetYear": 2003, "forecastYear": 2008, "value": -193}, 173 | {"budgetYear": 2004, "forecastYear": 2003, "value": -452}, 174 | {"budgetYear": 2004, "forecastYear": 2004, "value": -606}, 175 | {"budgetYear": 2004, "forecastYear": 2005, "value": -409}, 176 | {"budgetYear": 2004, "forecastYear": 2006, "value": -290}, 177 | {"budgetYear": 2004, "forecastYear": 2007, "value": -254}, 178 | {"budgetYear": 2004, "forecastYear": 2008, "value": -243}, 179 | {"budgetYear": 2004, "forecastYear": 2009, "value": -241}, 180 | {"budgetYear": 2005, "forecastYear": 2004, "value": -480}, 181 | {"budgetYear": 2005, "forecastYear": 2005, "value": -479}, 182 | {"budgetYear": 2005, "forecastYear": 2006, "value": -423}, 183 | {"budgetYear": 2005, "forecastYear": 2007, "value": -329}, 184 | {"budgetYear": 2005, "forecastYear": 2008, "value": -255}, 185 | {"budgetYear": 2005, "forecastYear": 2009, "value": -237}, 186 | {"budgetYear": 2005, "forecastYear": 2010, "value": -207}, 187 | {"budgetYear": 2006, "forecastYear": 2005, "value": -357}, 188 | {"budgetYear": 2006, "forecastYear": 2006, "value": -458}, 189 | {"budgetYear": 2006, "forecastYear": 2007, "value": -373}, 190 | {"budgetYear": 2006, "forecastYear": 2008, "value": -227}, 191 | {"budgetYear": 2006, "forecastYear": 2009, "value": -211}, 192 | {"budgetYear": 2006, "forecastYear": 2010, "value": -183}, 193 | {"budgetYear": 2006, "forecastYear": 2011, "value": -202}, 194 | {"budgetYear": 2007, "forecastYear": 2006, "value": -269}, 195 | {"budgetYear": 2007, "forecastYear": 2007, "value": -257}, 196 | {"budgetYear": 2007, "forecastYear": 2008, "value": -240}, 197 | {"budgetYear": 2007, "forecastYear": 2009, "value": -190}, 198 | {"budgetYear": 2007, "forecastYear": 2010, "value": -94}, 199 | {"budgetYear": 2007, "forecastYear": 2011, "value": -53}, 200 | {"budgetYear": 2007, "forecastYear": 2012, "value": 59}, 201 | {"budgetYear": 2008, "forecastYear": 2007, "value": -170}, 202 | {"budgetYear": 2008, "forecastYear": 2008, "value": -417}, 203 | {"budgetYear": 2008, "forecastYear": 2009, "value": -414}, 204 | {"budgetYear": 2008, "forecastYear": 2010, "value": -160}, 205 | {"budgetYear": 2008, "forecastYear": 2011, "value": -94}, 206 | {"budgetYear": 2008, "forecastYear": 2012, "value": 47}, 207 | {"budgetYear": 2008, "forecastYear": 2013, "value": 28}, 208 | {"budgetYear": 2009, "forecastYear": 2008, "value": -467}, 209 | {"budgetYear": 2009, "forecastYear": 2009, "value": -1781}, 210 | {"budgetYear": 2009, "forecastYear": 2010, "value": -1171}, 211 | {"budgetYear": 2009, "forecastYear": 2011, "value": -899}, 212 | {"budgetYear": 2009, "forecastYear": 2012, "value": -563}, 213 | {"budgetYear": 2009, "forecastYear": 2013, "value": -507}, 214 | {"budgetYear": 2009, "forecastYear": 2014, "value": -532}, 215 | {"budgetYear": 2009, "forecastYear": 2015, "value": -534}, 216 | {"budgetYear": 2009, "forecastYear": 2016, "value": -573}, 217 | {"budgetYear": 2009, "forecastYear": 2017, "value": -562}, 218 | {"budgetYear": 2009, "forecastYear": 2018, "value": -551}, 219 | {"budgetYear": 2009, "forecastYear": 2019, "value": -607}, 220 | {"budgetYear": 2010, "forecastYear": 2009, "value": -1436}, 221 | {"budgetYear": 2010, "forecastYear": 2010, "value": -1556}, 222 | {"budgetYear": 2010, "forecastYear": 2011, "value": -1250}, 223 | {"budgetYear": 2010, "forecastYear": 2012, "value": -803}, 224 | {"budgetYear": 2010, "forecastYear": 2013, "value": -692}, 225 | {"budgetYear": 2010, "forecastYear": 2014, "value": -659}, 226 | {"budgetYear": 2010, "forecastYear": 2015, "value": -689}, 227 | {"budgetYear": 2010, "forecastYear": 2016, "value": -700}, 228 | {"budgetYear": 2010, "forecastYear": 2017, "value": -688}, 229 | {"budgetYear": 2010, "forecastYear": 2018, "value": -682}, 230 | {"budgetYear": 2010, "forecastYear": 2019, "value": -775}, 231 | {"budgetYear": 2010, "forecastYear": 2020, "value": -841} 232 | ]; -------------------------------------------------------------------------------- /examples/budgetForecast/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NYTimes Porcupine Graph 5 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |
90 | 1 91 | 2 92 | 3 93 | 4 94 | 5 95 | 6 96 |
97 | 98 |
99 |
100 |

Falling Short

101 | 102 |

President Obama's budget proposal estimates a deficit of $1.6 trillion for the current fiscal year and $1.3 trillion in 2011.

103 |
104 | 105 | 110 | 111 | 117 | 118 | 123 | 124 | 129 | 130 | 136 |
137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /examples/budgetForecast/timeline.js: -------------------------------------------------------------------------------- 1 | vis.bind('year', function(val) { 2 | $('#slider').slider('value', val); 3 | }) 4 | 5 | n3.scene('scene_1') 6 | .set(vis, 'year', 2010) 7 | .set(vis, 'plotForecasts', false) 8 | 9 | .add(vis, function() { // Init jQuery slider 10 | $('#slider').slider({ 11 | min: 1980, 12 | max: 2010, 13 | step: 1, 14 | slide: function(e, ui) { 15 | vis.state('year', ui.value); 16 | } 17 | }); 18 | 19 | $('#slider').hide(); 20 | }) 21 | 22 | .add(vis, 23 | n3.annotation('highlightedPoint') 24 | .data([fullData[219], fullData[220]])) 25 | 26 | .add(vis, 27 | n3.annotation('label') 28 | .attr('id', 'lbl_2010') 29 | .html('

2010 estimate:
-$1.56 trillion

') 30 | .pos([540, 341])) 31 | 32 | .add(vis, 33 | n3.annotation('label') 34 | .attr('id', 'lbl_2011') 35 | .html('

2011 proposal:
-$1.27 trillion

') 36 | .pos([560, 280])); 37 | 38 | n3.scene('scene_2') 39 | .set(vis, 'year', 2010) 40 | .set(vis, 'plotForecasts', false) 41 | 42 | .add(vis, function() { 43 | var drawLineGraph = vis.const('drawLineGraph')(); 44 | var dataTransform = vis.const('dataTransform'); 45 | var year = 2009; 46 | var data = []; 47 | var fullData = vis.data(); 48 | 49 | for(var i in fullData) { 50 | var d = fullData[i]; 51 | if(d.budgetYear != year) 52 | continue; 53 | 54 | data[data.length] = d; 55 | } 56 | 57 | var forecast = vis.stage() 58 | .selectAll('path.forecast') 59 | .data([data]); 60 | 61 | forecast.enter() 62 | .append('svg:path') 63 | .attr('d', drawLineGraph) 64 | .attr('class', 'forecast'); 65 | 66 | forecast.exit().remove(); 67 | }) 68 | .add(vis, 69 | n3.annotation('line') 70 | .attr('id', 'lastLine') 71 | .start([600, 180]) 72 | .end([600, 160]) 73 | .style('stroke-width', 1) 74 | .style('stroke', 'grey')) 75 | 76 | .add(vis, 77 | n3.annotation('label') 78 | .attr('id', 'lastYearLbl') 79 | .html('

Last year\'s forecast

') 80 | .pos([560, 125])) 81 | 82 | .add(vis, 83 | n3.annotation('line') 84 | .attr('id', 'latestLine') 85 | .start([600, 210]) 86 | .end([600, 230]) 87 | .style('stroke-width', 1) 88 | .style('stroke', 'grey')) 89 | 90 | .add(vis, 91 | n3.annotation('label') 92 | .attr('id', 'latestLbl') 93 | .html('

Latest forecast

') 94 | .pos([565, 225])) 95 | 96 | n3.scene('scene_3') 97 | .subScene('scene_3a') 98 | .set(vis, 'year', 1980) 99 | .set(vis, 'plotForecasts', true) 100 | .set(vis, 'year', n3.util.iterate(1980, 1996, 1, 150)) 101 | 102 | .add(vis, 103 | n3.annotation('highlightedPoint') 104 | .data([fullData[98]]), 105 | n3.trigger(vis) 106 | .where('year') 107 | .gte(1995)) 108 | 109 | .add(vis, 110 | n3.annotation('line') 111 | .attr('id', 'line1995') 112 | .start([332.5, 145]) 113 | .end([332.5, 195]) 114 | .style('stroke-width', 1) 115 | .style('stroke', 'grey'), 116 | n3.trigger(vis) 117 | .where('year') 118 | .gte(1995)) 119 | 120 | .add(vis, n3.annotation('label') 121 | .attr('id', 'lbl1995') 122 | .html('

The 1995 forecast for 1999
did not predict a surplus...

') 123 | .pos([280, 195]), 124 | n3.trigger(vis) 125 | .where('year') 126 | .gte(1995)) 127 | 128 | n3.scene('scene_3') 129 | .subScene('scene_3b') 130 | .set(vis, 'plotForecasts', true) 131 | .set(vis, 'year', n3.util.iterate(1995, 2009, 1, 150)) 132 | 133 | .add(vis, n3.annotation('highlightedPoint') 134 | .data([fullData[98], fullData[204]]), 135 | n3.trigger(vis) 136 | .where('year') 137 | .gte(2008) 138 | ) 139 | 140 | .add(vis, n3.annotation('line') 141 | .attr('id', 'line2008') 142 | .start([560, 92]) 143 | .end([560, 142]) 144 | .style('stroke-width', 1) 145 | .style('stroke', 'grey'), 146 | 147 | n3.trigger(vis) 148 | .where('year') 149 | .gte(2008) 150 | ) 151 | 152 | .add(vis, n3.annotation('label') 153 | .attr('id', 'lbl2008') 154 | .html('

... but the 2008
forecast for 2012 did.

') 155 | .pos([520, 142]), 156 | 157 | n3.trigger(vis) 158 | .where('year') 159 | .gte(2008) 160 | ) 161 | 162 | n3.scene('scene_3') 163 | .subScene('scene_3c') 164 | .set(vis, 'year', 2008) 165 | .set(vis, 'plotForecasts', true) 166 | .set(vis, 'year', n3.util.iterate(2008, 2011, 1, 150)) 167 | 168 | n3.timeline.transition('*', '*', function(fromScene, toScene) { 169 | $('#' + fromScene.sceneId).hide(); 170 | $('#' + toScene.sceneId).show(); 171 | }); 172 | 173 | n3.timeline.transition(['scene_1', 'scene_2'], 174 | ['scene_3a', 'scene_3b', 'scene_3c', 'scene_4'], 175 | function(fromScene, toScene) { $('#slider').show(); }) 176 | 177 | n3.timeline.transition(['scene_3a', 'scene_3b', 'scene_3c', 'scene_4'], 178 | ['scene_1', 'scene_2'], 179 | function(fromScene, toScene) { $('#slider').hide(); }) 180 | 181 | n3.timeline.switchScene('scene_1'); -------------------------------------------------------------------------------- /examples/budgetForecast/vis.js: -------------------------------------------------------------------------------- 1 | var vis = n3.vis('budgetForecast') 2 | .data(fullData) 3 | 4 | .stage('#stage', 700, 400) 5 | 6 | .state('year', d3.range(1980, 2011)) 7 | .state('plotForecasts', [true, false]) 8 | 9 | .const('minYear', 1980) 10 | .const('maxYear', 2010) 11 | .const('maxYearForecast', 2020) 12 | .const('minValue', -1781) 13 | .const('maxValue', 519) 14 | 15 | .const('sx', function() { 16 | return d3.scale.linear() 17 | .domain([vis.const('minYear'), vis.const('maxYearForecast')]) 18 | .range([0, vis.width()]); 19 | }) 20 | 21 | .const('sy', function() { 22 | return d3.scale.linear() 23 | .domain([vis.const('minValue'), vis.const('maxValue')]) 24 | .range([vis.height(), 0]); 25 | }) 26 | 27 | .const('drawLineGraph', function() { 28 | return d3.svg.line() 29 | .x(function(d, i){ return vis.const('sx')()(d.forecastYear); }) 30 | .y(function(d, i){ return vis.const('sy')()(d.value); }); 31 | }) 32 | 33 | .render(function() { 34 | // First draw zero line 35 | drawAxes(this); 36 | 37 | // Plot forecasts 38 | drawForecasts(this); 39 | 40 | // Plot actual budget figures 41 | drawActual(this); 42 | }); 43 | 44 | function drawAxes(vis) { 45 | var sx = vis.const('sx')(); 46 | var sy = vis.const('sy')(); 47 | 48 | var formatNumber = function(nStr) { 49 | nStr += ''; 50 | x = nStr.split('.'); 51 | x1 = x[0]; 52 | x2 = x.length > 1 ? '.' + x[1] : ''; 53 | var rgx = /(\d+)(\d{3})/; 54 | while (rgx.test(x1)) { 55 | x1 = x1.replace(rgx, '$1' + '.' + '$2'); 56 | } 57 | 58 | var newStr = x1 + x2; 59 | if(newStr.indexOf('-') != -1) { 60 | newStr = '-$' + newStr.substring(1); 61 | } else { 62 | newStr = '$' + newStr; 63 | } 64 | 65 | return newStr; 66 | } 67 | 68 | var xAxis = d3.range(vis.const('minYear'), vis.const('maxYearForecast'), 2); 69 | var yAxis = d3.range(vis.const('minValue'), vis.const('maxValue'), 200); 70 | 71 | vis.stage() 72 | .selectAll('line.xAxis') 73 | .data(xAxis) 74 | .enter() 75 | .append("svg:line") 76 | .attr("x1", function(d) { return sx(d); }) 77 | .attr("y1", function() { return vis.height() + 20 }) 78 | .attr("x2", function(d) { return sx(d); }) 79 | .attr("y2", 0) 80 | .attr("stroke", "#fff") 81 | .attr("class", "xAxis"); 82 | 83 | vis.stage() 84 | .selectAll("line.yAxis") 85 | .data(yAxis) 86 | .enter() 87 | .append("svg:line") 88 | .attr("x1", -20) 89 | .attr("y1", function(d) { return sy(d); }) 90 | .attr("x2", function(d) { return vis.width() + 20 }) 91 | .attr("y2", function(d) { return sy(d); }) 92 | .attr("stroke", "#fff") 93 | .attr("class", "yAxis"); 94 | 95 | vis.stage() 96 | .selectAll("text.xAxisLabels") 97 | .data(xAxis) 98 | .enter() 99 | .append("svg:text") 100 | .text(function(d) { 101 | if(d == vis.const('minYear') || d == vis.const('maxYearForecast')) 102 | return; 103 | 104 | var fullYear = d + ''; 105 | return "'" + fullYear.substring(2); 106 | }) 107 | .attr("x", function(d) { return sx(d); }) 108 | .attr("dx", "10") 109 | .attr("y", function(d) { return sy(0); }) 110 | .attr("dy", "-5") 111 | .attr("class", "xAxisLabels") 112 | .attr("fill", "#aaa") 113 | .attr("text-anchor", "end"); 114 | 115 | vis.stage() 116 | .selectAll("text.yAxisLabels") 117 | .data(yAxis) 118 | .enter() 119 | .append("svg:text") 120 | .text(function(d) { 121 | if(d != 19 && d != vis.const('minValue')) 122 | return formatNumber(Math.round(d)) + ((Math.abs(d) > 1000) ? ' trillion' : ' billion'); 123 | }) 124 | .attr("x", 75) 125 | .attr("dx", function(d) { return Math.abs(d) > 1000 ? 15 : 0 }) 126 | .attr("y", function(d) { return sy(d); }) 127 | .attr("dy", "4") 128 | .attr("class", "yAxisLabels") 129 | .attr("fill", "#aaa") 130 | .attr("text-anchor", "end"); 131 | 132 | var zeroData = []; 133 | for(var i = vis.const('minYear'); i <= vis.const('maxYearForecast'); i++) 134 | zeroData[zeroData.length] = {'budgetYear': i, 'forecastYear': i, 'value': 0}; 135 | 136 | var path = vis.stage().selectAll('path#zero') 137 | .data([zeroData]); 138 | 139 | path.enter() 140 | .append('svg:path') 141 | .attr('id', 'zero') 142 | .attr('d', vis.const('drawLineGraph')()); 143 | } 144 | 145 | function drawForecasts(vis) { 146 | var plot = vis.state('plotForecasts'); 147 | var year = vis.state('year'); 148 | var drawLineGraph = vis.const('drawLineGraph')(); 149 | var dataTransform = vis.const('dataTransform'); 150 | var fullData = vis.data(); 151 | 152 | var data = []; 153 | 154 | if(plot == true) { 155 | var years = d3.range(vis.const('minYear'), vis.const('maxYear')+1); 156 | for(var i = 0; i < years.length; i++) { 157 | if(years[i] > year) 158 | break; 159 | 160 | var forecastData = []; 161 | for(var j = 0; j < fullData.length; j++) { 162 | if(fullData[j].budgetYear != years[i]) 163 | continue; 164 | 165 | forecastData[forecastData.length] = fullData[j]; 166 | } 167 | 168 | data[data.length] = forecastData; 169 | } 170 | } 171 | 172 | var forecast = vis.stage() 173 | .selectAll('path.forecast') 174 | .data(data); 175 | 176 | forecast.enter() 177 | .append('svg:path') 178 | .attr('d', drawLineGraph) 179 | .attr('class', 'forecast'); 180 | 181 | forecast.transition() 182 | .attr('d', drawLineGraph) 183 | 184 | forecast.exit() 185 | .remove(); 186 | } 187 | 188 | function drawActual(vis) { 189 | var year = vis.state('year'); 190 | var drawLineGraph = vis.const('drawLineGraph')(); 191 | var dataTransform = vis.const('dataTransform'); 192 | var fullData = vis.data(); 193 | 194 | var data = []; 195 | 196 | for(var i = 0; i < fullData.length; i++) { 197 | var d = fullData[i]; 198 | if(d.budgetYear > year) 199 | continue; 200 | 201 | if(d.forecastYear != d.budgetYear - 1) 202 | continue; 203 | 204 | data[data.length] = d; 205 | } 206 | 207 | var actual = vis.stage().selectAll('path#actual') 208 | .data([data]); 209 | 210 | actual.enter() 211 | .append('svg:path') 212 | .attr('d', drawLineGraph) 213 | .attr('id', 'actual'); 214 | 215 | actual.transition().attr('d', drawLineGraph); 216 | 217 | actual.exit().remove(); 218 | 219 | // 2010 projection 220 | var projectedData = []; 221 | if(year >= 2010) { 222 | for(var i = 0; i < fullData.length; i++) { 223 | var d = fullData[i]; 224 | if(d.budgetYear != 2010) 225 | continue; 226 | 227 | projectedData[projectedData.length] = d; 228 | } 229 | } 230 | 231 | var projected = vis.stage() 232 | .selectAll('path#projected') 233 | .data([projectedData]); 234 | 235 | projected.enter() 236 | .append('svg:path') 237 | .attr('d', drawLineGraph) 238 | .attr('id', 'projected'); 239 | 240 | projected.transition().attr('d', drawLineGraph); 241 | 242 | projected.exit().remove(); 243 | } -------------------------------------------------------------------------------- /examples/habemusPapam/ellipsis.js: -------------------------------------------------------------------------------- 1 | var elVis = n3.vis('habemusPapam') 2 | .stage('#tableauViz', 1000, 475) 3 | 4 | .state('country', ['Afghanistan', 'Albania', 'Algeria', 'American Samoa', 'Andorra', 'Angola', 'Anguilla', 'Antigua and Barbuda', 'Argentina', 'Armenia', 'Aruba', 'Australia', 'Austria', 'Azerbaijan', 'Bahamas', 'Bahrain', 'Bangladesh', 'Barbados', 'Belarus', 'Belgium', 'Belize', 'Benin', 'Bermuda', 'Bhutan', 'Bolivia', 'Bosnia-Herzegovina', 'Botswana', 'Brazil', 'British Virgin Islands', 'Brunei', 'Bulgaria', 'Burkina Faso', 'Burma (Myanmar)', 'Burundi', 'Cambodia', 'Cameroon', 'Canada', 'Cape Verde', 'Cayman Islands', 'Central African Republic', 'Chad', 'Channel Islands', 'Chile', 'China', 'Colombia', 'Comoros', 'Cook Islands', 'Costa Rica', 'Croatia', 'Cuba', 'Cyprus', 'Czech Republic', 'Denmark', 'Djibouti', 'Dominica', 'Dominican Republic', 'DRC', 'Ecuador', 'Egypt', 'El Salvador', 'Equatorial Guinea', 'Eritrea', 'Estonia', 'Ethiopia', 'Faeroe Islands', 'Falkland Islands', 'Fiji', 'Finland', 'France', 'French Guiana', 'French Polynesia', 'Gabon', 'Gambia', 'Georgia', 'Germany', 'Ghana', 'Gibraltar', 'Greece', 'Greenland', 'Grenada', 'Guadeloupe', 'Guam', 'Guatemala', 'Guinea', 'Guinea Bissau', 'Guyana', 'Haiti', 'Honduras', 'Hong Kong', 'Hungary', 'Iceland', 'India', 'Indonesia', 'Iran', 'Iraq', 'Ireland', 'Isle of Man', 'Israel', 'Italy', 'Ivory Coast', 'Jamaica', 'Japan', 'Jordan', 'Kazakhstan', 'Kenya', 'Kiribati', 'Kosovo', 'Kuwait', 'Kyrgyzstan', 'Laos', 'Latvia', 'Lebanon', 'Lesotho', 'Liberia', 'Libya', 'Liechtenstein', 'Lithuania', 'Luxembourg', 'Macau', 'Madagascar', 'Malawi', 'Malaysia', 'Maldives', 'Mali', 'Malta', 'Marshall Islands', 'Martinique', 'Mauritania', 'Mauritius', 'Mayotte', 'Mexico', 'Micronesia', 'Moldova', 'Monaco', 'Mongolia', 'Montenegro', 'Montserrat', 'Morocco', 'Mozambique', 'Namibia', 'Nauru', 'Nepal', 'Netherlands', 'Netherlands Antilles', 'New Caledonia', 'New Zealand', 'Nicaragua', 'Niger', 'Nigeria', 'Niue', 'North Korea', 'Northern Mariana Islands', 'Norway', 'Oman', 'Pakistan', 'Palau', 'Palestinian territories', 'Panama', 'Papua New Guinea', 'Paraguay', 'Peru', 'Philippines', 'Poland', 'Portugal', 'Puerto Rico', 'Qatar', 'Republic of Macedonia', 'Republic of the Congo', 'Reunion', 'Romania', 'Russia', 'Rwanda', 'Samoa', 'San Marino', 'Sao Tome and Principe', 'Saudi Arabia', 'Senegal', 'Serbia', 'Seychelles', 'Sierra Leone', 'Singapore', 'Slovakia', 'Slovenia', 'Solomon Islands', 'Somalia', 'South Africa', 'South Korea', 'South Sudan', 'Spain', 'Sri Lanka', 'St. Helena', 'St. Kitts and Nevis', 'St. Lucia', 'St. Pierre and Miquelon', 'St. Vincent and the Grenadines', 'Sudan', 'Suriname', 'Swaziland', 'Sweden', 'Switzerland', 'Syria', 'Taiwan', 'Tajikistan', 'Tanzania', 'Thailand', 'Timor-Leste', 'Togo', 'Tokelau', 'Tonga', 'Trinidad and Tobago', 'Tunisia', 'Turkey', 'Turkmenistan', 'Turks and Caicos', 'Tuvalu', 'U.S. Virgin Islands', 'Uganda', 'Ukraine', 'United Arab Emirates', 'United Kingdom', 'United States', 'Uruguay', 'Uzbekistan', 'Vanuatu', 'Vatican City', 'Venezuela', 'Vietnam', 'Wallis and Futuna', 'Western Sahara', 'Yemen', 'Zambia', 'Zimbabwe']) 5 | .render(function() { 6 | var countries = elVis.state('country').split(', '); 7 | if(countries.length == 1 && countries[0] == '') 8 | map.clearSelectedMarksAsync() 9 | else 10 | map.selectMarksAsync('Country', countries, tableauSoftware.SelectionUpdateType.REPLACE); 11 | }); 12 | 13 | 14 | n3.scene('overview') 15 | .add(elVis, 16 | n3.annotation('label') 17 | .attr('id', 'cardinals') 18 | .attr('class', 'label') 19 | .html('The College of Cardinals') 20 | .pos([327, 125])) 21 | 22 | .add(elVis, 23 | n3.annotation('label') 24 | .attr('id', 'pope') 25 | .attr('class', 'label') 26 | .html('Pope Francis') 27 | .pos([150, 417])) 28 | 29 | n3.scene('overview').clone('cardinals') 30 | .set(elVis, 'country', '') 31 | 32 | .add(elVis, 33 | n3.annotation('label') 34 | .html("

The College of Cardinals

" + 35 | "

115 cardinals participated in the 2013 Conclave. " + 36 | "However, most of them were from Europe. " + 37 | "This is in spite of the church's shifting demographics " + 38 | "(click for more info):

" + 39 | "" + 40 | "" + 41 | "" + 42 | "" + 43 | "" + 44 | "" + 45 | "" + 46 | "" + 47 | "" + 48 | "
 % Total Catholics% of Cardinals
Europe2662
Latin America3319
North America1614
Africa1311
Asia1111
Oceania11
" + 49 | "
x
") 50 | .pos([702, 34]) 51 | .attr('id', 'label_1364620620916') 52 | .attr('class', 'annotation') 53 | .style('width', '305px')) 54 | 55 | n3.scene('cardinals').clone('europe') 56 | .set('habemusPapam', 'country', 'Albania, Andorra, Armenia, Austria, Belarus, Belgium, Bosnia-Herzegovina, Bulgaria, Channel Islands, Croatia, Cyprus, Czech Republic, Denmark, Estonia, Faeroe Islands, Finland, France, Georgia, Germany, Gibraltar, Greece, Greenland, Hungary, Iceland, Ireland, Isle of Man, Italy, Kosovo, Latvia, Liechtenstein, Lithuania, Luxembourg, Malta, Moldova, Monaco, Montenegro, Netherlands, Norway, Poland, Portugal, Republic of Macedonia, Romania, Russia, San Marino, Serbia, Slovakia, Slovenia, Spain, Sweden, Switzerland, Ukraine, United Kingdom, Uzbekistan, Vatican City') 57 | 58 | n3.scene('cardinals').clone('asia') 59 | .set('habemusPapam', 'country', 'Afghanistan, Azerbaijan, Bahrain, Bangladesh, Bhutan, Brunei, Burma (Myanmar), Cambodia, China, Hong Kong, India, Indonesia, Iran, Iraq, Israel, Japan, Jordan, Kazakhstan, Kuwait, Kyrgyzstan, Laos, Lebanon, Macau, Malaysia, Maldives, Mongolia, Nepal, North Korea, Oman, Pakistan, Palestinian territories, Philippines, Qatar, Saudi Arabia, Singapore, South Korea, Sri Lanka, Syria, Taiwan, Tajikistan, Thailand, Timor-Leste, Turkey, Turkmenistan, United Arab Emirates, Vietnam, Yemen') 60 | 61 | n3.scene('cardinals').clone('africa') 62 | .set('habemusPapam', 'country', 'Algeria, Angola, Benin, Botswana, Burkina Faso, Burundi, Cameroon, Cape Verde, Central African Republic, Chad, Comoros, Djibouti, DRC, Egypt, Equatorial Guinea, Eritrea, Ethiopia, Gabon, Gambia, Ghana, Guinea, Guinea Bissau, Ivory Coast, Kenya, Lesotho, Liberia, Libya, Madagascar, Malawi, Mali, Mauritania, Mauritius, Mayotte, Morocco, Mozambique, Namibia, Niger, Nigeria, Republic of the Congo, Reunion, Rwanda, Sao Tome and Principe, Senegal, Seychelles, Sierra Leone, Somalia, South Africa, South Sudan, Sudan, Swaziland, Tanzania, Togo, Tunisia, Uganda, Western Sahara, Zambia, Zimbabwe') 63 | 64 | .add('habemusPapam', 65 | n3.annotation('label') 66 | .html('

Catholicism’s growth in Africa has accelerated in recent decades. There was a groundswell after the '+ 67 | 'Second Vatican Council of 1962-65, which authorised the use of vernacular at mass and delegated more power to locals.'+ 68 | 'The church’s indigenisation proceeded. African song and dance were incorporated into services. Young African priests, '+ 69 | 'not European missionaries, took charge.

' + 70 | '

More information at The Economist

' + 71 | "
x
") 72 | .pos([702, 256]) 73 | .attr('id', 'label_1364760621063') 74 | .attr('class', 'annotation') 75 | .style('width', '305px')) 76 | 77 | n3.scene('cardinals').clone('north_america') 78 | .set('habemusPapam', 'country', 'Canada, United States') 79 | 80 | n3.scene('cardinals').clone('latin_america') 81 | .set('habemusPapam', 'country', 'Anguilla, Antigua and Barbuda, Argentina, Aruba, Bahamas, Barbados, Belize, Bermuda, Bolivia, Brazil, British Virgin Islands, Cayman Islands, Chile, Colombia, Costa Rica, Cuba, Dominica, Dominican Republic, Ecuador, El Salvador, Falkland Islands, French Guiana, Grenada, Guadeloupe, Guatemala, Guyana, Haiti, Honduras, Jamaica, Martinique, Mexico, Montserrat, Netherlands Antilles, Nicaragua, Panama, Paraguay, Peru, Puerto Rico, St. Helena, St. Kitts and Nevis, St. Lucia, St. Pierre and Miquelon, St. Vincent and the Grenadines, Suriname, Trinidad and Tobago, Turks and Caicos, U.S. Virgin Islands, Uruguay, Venezuela') 82 | 83 | .add('habemusPapam', 84 | n3.annotation('label') 85 | .html('

Although the Vatican had once seen the area as a “continent of hope,”' + 86 | " it now thought of it as a “continent of concern.”

" + 87 | "

According to Brazil’s 2010 census, 65 percent of the population is Catholic, down from" + 88 | "over 90 percent in 1970. Similarly, between 2000 and 2010, the percentage of Mexicans that identify" + 89 | "as Catholic dropped from 88 to less than 83 -- the largest fall recorded to date. If these trends persist, " + 90 | 'by 2025 about 50 percent of all Latin Americans will be Catholic, down from approximately 70 percent today".

' + 91 | '

More information at Foreign Affairs

' + 92 | "
x
") 93 | .pos([702, 256]) 94 | .attr('id', 'label_1364760621063') 95 | .attr('class', 'annotation') 96 | .style('width', '305px')) 97 | 98 | n3.scene('cardinals').clone('oceania') 99 | .set('habemusPapam', 'country', 'American Samoa, Australia, Cook Islands, Fiji, French Polynesia, Guam, Kiribati, Marshall Islands, Micronesia, Nauru, New Caledonia, New Zealand, Niue, Northern Mariana Islands, Palau, Papua New Guinea, Samoa, Solomon Islands, Tokelau, Tonga, Tuvalu, Vanuatu, Wallis and Futuna') 100 | 101 | n3.scene('overview').clone('francis') 102 | .set(elVis, 'country', 'Argentina, Philippines, Hungary, Ghana, Sri Lanka, Brazil, Italy, Canada, Honduras, United States, Austria, Vatican') 103 | 104 | .add(elVis, 105 | n3.annotation('label') 106 | .html("

Pope Francis

" + 107 | "

Perhaps the shift in the Catholic demographic weighed " + 108 | "on the cardinals? Pope Francis, formerly Cardinal " + 109 | "Bergoglio of Argentina, is the first non-European pope " + 110 | "in over 1,200 years, and the first Jesuit.

" + 111 | "

Other reported frontrunners included:

" + 112 | "" + 113 | '' + 114 | '' + 115 | '' + 116 | '' + 117 | '' + 118 | '' + 119 | '' + 120 | '' + 121 | '' + 122 | '' + 123 | '' + 124 | '' + 125 | "
Luis TagalePhilippinesMore Info
Peter ErdoHungaryMore Info
Peter TurksonGhanaMore Info
Malcolm RanjithSri LankaMore Info
Odilo SchererBrazilMore Info
Angelo ScolaMilanMore Info
Marc OuelletCanadaMore Info
Leonardo SandriArgentinaMore Info
Andres MaradiagaHondurasMore Info
Timothy DolanNew YorkMore Info
Christoph SchoenbornAustriaMore Info
Gianfranco RavasiVaticanMore Info
" + 126 | "
x
") 127 | .pos([700, 8]) 128 | .attr('id', 'label_1364624771223') 129 | .attr('class', 'annotation') 130 | .style('width', '275px')) -------------------------------------------------------------------------------- /examples/habemusPapam/external-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/habemusPapam/external-link.png -------------------------------------------------------------------------------- /examples/habemusPapam/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 82 | 83 | 84 | 85 | 86 | 109 | 110 | Habemus Papam! 111 | 112 | 113 |
114 | 115 | -------------------------------------------------------------------------------- /examples/lib/colorbrewer/LICENSE: -------------------------------------------------------------------------------- 1 | Apache-Style Software License for ColorBrewer software and ColorBrewer Color 2 | Schemes 3 | 4 | Copyright (c) 2002 Cynthia Brewer, Mark Harrower, and The Pennsylvania State 5 | University. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 | use this file except in compliance with the License. You may obtain a copy of 9 | the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 16 | License for the specific language governing permissions and limitations under 17 | the License. 18 | 19 | Redistribution and use in source and binary forms, with or without 20 | modification, are permitted provided that the following conditions are met: 21 | 22 | 1. Redistributions as source code must retain the above copyright notice, this 23 | list of conditions and the following disclaimer. 24 | 25 | 2. The end-user documentation included with the redistribution, if any, must 26 | include the following acknowledgment: "This product includes color 27 | specifications and designs developed by Cynthia Brewer 28 | (http://colorbrewer.org/)." Alternately, this acknowledgment may appear in the 29 | software itself, if and wherever such third-party acknowledgments normally 30 | appear. 31 | 32 | 4. The name "ColorBrewer" must not be used to endorse or promote products 33 | derived from this software without prior written permission. For written 34 | permission, please contact Cynthia Brewer at cbrewer@psu.edu. 35 | 36 | 5. Products derived from this software may not be called "ColorBrewer", nor 37 | may "ColorBrewer" appear in their name, without prior written permission of 38 | Cynthia Brewer. 39 | -------------------------------------------------------------------------------- /examples/lib/d3.v2.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/d3/d3.v2.js -------------------------------------------------------------------------------- /examples/lib/jquery-ui/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Paul Bakaus, http://jqueryui.com/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals (AUTHORS.txt, http://jqueryui.com/about) For exact 5 | contribution history, see the revision history and logs, available 6 | at http://jquery-ui.googlecode.com/svn/ 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery-ui/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/examples/lib/jquery-ui/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /examples/lib/jquery/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 John Resig, http://jquery.com/ 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/lib/n3.js: -------------------------------------------------------------------------------- 1 | ../../n3.js -------------------------------------------------------------------------------- /examples/unemployment/annualRates.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var width = 500; 3 | var height = 300; 4 | 5 | var minYear = 2004; 6 | var maxYear = 2011; 7 | 8 | var annualDataHash = {}; 9 | 10 | var vis = n3.vis('annualRates') 11 | .stage('#annual_rates_stage', width, height) 12 | 13 | .state('year', d3.range(minYear, maxYear+1)) 14 | .state('locationId', validLocationIds) 15 | 16 | .const('minYear', minYear) 17 | .const('maxYear', maxYear) 18 | .const('minRate', minRate) 19 | .const('maxRate', maxRate) 20 | 21 | .const('scales', getScales) 22 | .const('line', getLinePlotter) 23 | 24 | .render(function() { 25 | if(!vis.state('year') || !vis.state('locationId')) 26 | return; 27 | 28 | drawAxes(this); 29 | 30 | plotLines(this); 31 | }); 32 | 33 | d3.select("#annual_rates") 34 | .append("svg") 35 | .attr('id', 'annual_rates_stage'); 36 | 37 | var title = d3.select('#annual_rates') 38 | .append('p') 39 | .attr('id', 'annual_rates_title'); 40 | 41 | d3.json("data/annualData.json", function(json) { 42 | parseData(json, 0, json.length); 43 | }); 44 | 45 | // Data is extremely large, so process it in chunks of 100 46 | function parseData(json, offset, total) { 47 | var length = (total - offset > 100) ? offset + 100 : total; 48 | for(var i = offset; i < length; i++) { 49 | var d = json[i]; 50 | annualDataHash[d.id] || (annualDataHash[d.id] = []); 51 | 52 | annualDataHash[d.id].push(d); 53 | } 54 | vis.data(annualDataHash); 55 | // console.log('done ' + i + ' ' + length + ' ' + offset); 56 | if(length != total) 57 | window.setTimeout(function() { 58 | parseData(json, (offset + 100), total) 59 | }, 100); 60 | } 61 | 62 | function minRate() { 63 | var natlData = annualDataHash['usa']; 64 | var locData = annualDataHash[vis.state('locationId')]; 65 | 66 | var findMin = function(arr) { 67 | var min = Infinity; 68 | for(var i = 0; i < arr.length; i++) 69 | if(arr[i].rate < min) 70 | min = arr[i].rate; 71 | 72 | return min; 73 | } 74 | 75 | var minNatl = findMin(natlData); 76 | var minLoc = findMin(locData); 77 | 78 | return minNatl <= minLoc ? minNatl : minLoc; 79 | } 80 | 81 | function maxRate() { 82 | var natlData = annualDataHash['usa']; 83 | var locData = annualDataHash[vis.state('locationId')]; 84 | 85 | var findMax = function(arr) { 86 | var max = -Infinity; 87 | for(var i = 0; i < arr.length; i++) 88 | if(arr[i].rate > max) 89 | max = arr[i].rate; 90 | 91 | return max; 92 | } 93 | 94 | var maxNatl = maxNatl = findMax(natlData); 95 | var maxLoc = maxLoc = findMax(locData); 96 | 97 | return maxNatl >= maxLoc ? maxNatl : maxLoc; 98 | } 99 | 100 | function getScales() { 101 | var sx = d3.scale.linear() 102 | .domain([minYear, maxYear]) 103 | .range([0, width]); 104 | 105 | var sy = d3.scale.linear() 106 | .domain([minRate(), maxRate()]) 107 | .range([height, 0]); 108 | 109 | return [sx, sy]; 110 | } 111 | 112 | function getLinePlotter() { 113 | var scales = getScales(); 114 | var sx = scales[0], sy = scales[1]; 115 | 116 | var line = d3.svg.line() 117 | .x(function(d, i){ return sx(d.year); }) 118 | .y(function(d, i){ return sy(d.rate); }); 119 | 120 | return line; 121 | } 122 | 123 | function drawAxes(vis) { 124 | var xAxis = d3.range(minYear, maxYear+1); 125 | var yAxis = d3.range(Math.round(minRate()), 126 | Math.round(maxRate())); 127 | var scales = getScales(); 128 | var sx = scales[0], sy = scales[1]; 129 | 130 | var svg = vis.stage(); 131 | 132 | var xGrid = svg.selectAll('line.xGrid') 133 | .data(xAxis); 134 | 135 | xGrid.enter().append("svg:line"); 136 | 137 | xGrid.transition() 138 | .attr("x1", function(d) { return sx(d); }) 139 | .attr("y1", function() { return height + 25 }) 140 | .attr("x2", function(d) { return sx(d); }) 141 | .attr("y2", 0) 142 | .attr("class", "xGrid"); 143 | 144 | xGrid.exit().remove(); 145 | 146 | var yGrid = svg.selectAll("line.yGrid") 147 | .data(yAxis); 148 | 149 | yGrid.enter().append("svg:line"); 150 | 151 | yGrid.transition() 152 | .attr("x1", -10) 153 | .attr("y1", function(d) { return sy(d); }) 154 | .attr("x2", function(d) { return width + 10 }) 155 | .attr("y2", function(d) { return sy(d); }) 156 | .attr("class", "yGrid"); 157 | 158 | yGrid.exit().remove(); 159 | 160 | var xLbls = svg.selectAll("text.xLbls") 161 | .data(xAxis); 162 | 163 | xLbls.enter().append("svg:text"); 164 | 165 | xLbls.transition() 166 | .text(function(d) { 167 | return "'" + (d+'').substring(2); 168 | }) 169 | .attr("x", function(d) { return sx(d); }) 170 | .attr("dx", 10) 171 | .attr("y", function(d) { return height; }) 172 | .attr("dy", 40) 173 | .attr("class", "xLbls") 174 | .attr("text-anchor", "end"); 175 | 176 | xLbls.exit().remove(); 177 | 178 | var yLbls = svg.selectAll("text.yLbls") 179 | .data(yAxis); 180 | 181 | yLbls.enter().append("svg:text"); 182 | 183 | yLbls.transition() 184 | .text(function(d) { 185 | return d % 2 == 0 ? d + '%' : ''; 186 | }) 187 | .attr("x", -15) 188 | .attr("y", function(d) { return sy(d); }) 189 | .attr("dy", "4") 190 | .attr("class", "yLbls") 191 | .attr("text-anchor", "end"); 192 | 193 | yLbls.exit().remove(); 194 | } 195 | 196 | function plotLines(vis) { 197 | var svg = vis.stage(); 198 | var scales = getScales(); 199 | var sx = scales[0], sy = scales[1]; 200 | var line = getLinePlotter(); 201 | var data = [[], []]; 202 | var natlData = annualDataHash['usa']; 203 | var locData = annualDataHash[vis.state('locationId')]; 204 | 205 | for(var i = 0; i < natlData.length; i++) 206 | if(natlData[i].year <= vis.state('year')) 207 | data[0].push(natlData[i]); 208 | 209 | for(var i = 0; i < locData.length; i++) 210 | if(locData[i].year <= vis.state('year')) 211 | data[1].push(locData[i]); 212 | 213 | var p = svg.selectAll('path') 214 | .data(data, function(d) { return d.id; }); 215 | 216 | p.enter().append('svg:path'); 217 | 218 | p.transition() 219 | .attr('id', function(d) { return d.length > 0 ? d[0].id : ''; }) 220 | .attr('d', line); 221 | 222 | p.exit().remove(); 223 | 224 | if(data[1].length > 0) 225 | title.html('National Unemployment Rates vs ' + 226 | '' + data[1][0]['name'] + ''); 227 | } 228 | }).call(this); -------------------------------------------------------------------------------- /examples/unemployment/choropleth.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var zoomExtent = [1, 15]; 3 | var width = 960; 4 | var height = 500; 5 | var path = d3.geo.path(); 6 | 7 | var statesUnemployment, countiesUnemployment; // Loaded async 8 | 9 | var vis = n3.vis('choropleth') 10 | .data({}) 11 | .stage('#choropleth_stage g', width, height) 12 | 13 | .state('zoom', zoomExtent, true) 14 | .bind('zoom', function(val) { 15 | $( "#zoom_slider" ).slider('value', val); 16 | }) 17 | 18 | .state('translateX', [-Infinity, Infinity], true) 19 | .state('translateY', [-Infinity, Infinity], true) 20 | 21 | .const('zoomExtent', zoomExtent) 22 | .const('path', path) 23 | .const('quantize', quantize) 24 | 25 | .render(redraw); 26 | 27 | // Slider is part of visualization, not story! 28 | $("#zoom_slider").slider({ 29 | orientation: "vertical", 30 | min: zoomExtent[0], 31 | max: zoomExtent[1], 32 | value: 1, 33 | step: 0.5, 34 | start: function(event, ui) { 35 | $(this).slider('option', 'oldVal', ui.value); 36 | }, 37 | slide: function( event, ui ) { 38 | var oldVal = $(this).slider('option', 'oldVal'); 39 | var newVal = ui.value; 40 | $(this).slider('option', 'oldVal', ui.value); 41 | 42 | fireZoomEvent(oldVal, newVal); 43 | } 44 | }); 45 | 46 | function fireZoomEvent(oldVal, newVal) { 47 | // void initWebKitWheelEvent(int wheelDeltaX, int wheelDeltaY, DOMWindow 48 | // view, int screenX, int screenY, int clientX, int clientY, bool ctrlKey, 49 | // bool altKey, bool shiftKey, bool metaKey); 50 | 51 | var diff = newVal - oldVal; 52 | var wheelDeltaY = Math.pow(1, Math.abs(diff)); 53 | wheelDeltaY *= (newVal < oldVal) ? -1 : 1; 54 | 55 | var evt = document.createEvent("WheelEvent"); 56 | evt.initWebKitWheelEvent(0, wheelDeltaY, window, 57 | width/2, height/2, width/2, height/2, 58 | false, false, false, false); 59 | 60 | document.getElementById('choropleth_stage').dispatchEvent(evt); 61 | } 62 | 63 | // Slightly modified version of D3 Choropleth examples 64 | // Capped the zoom extents and have counties appear at 65 | // a certain zoom level. 66 | var svg = d3.select("#choropleth") 67 | .append("svg") 68 | .attr('id', 'choropleth_stage') 69 | .call(d3.behavior.zoom().scaleExtent(zoomExtent) 70 | .on("zoom", onZoom)) 71 | .append("g"); 72 | 73 | var counties = svg.append("g") 74 | .attr("id", "counties") 75 | .attr("class", "Blues"); 76 | 77 | var states = svg.append("g") 78 | .attr("id", "states") 79 | .attr("class", "Blues"); 80 | 81 | d3.json("data/states-unemployment.json", function(json) { 82 | statesUnemployment = json; 83 | }) 84 | 85 | d3.json("data/us-states.json", function(json) { 86 | var visData = vis.data(); 87 | visData['states'] = json; 88 | 89 | states.selectAll("path") 90 | .data(json.features) 91 | .enter().append("path") 92 | .attr("class", function(d) { 93 | return quantize(true, d); 94 | }) 95 | .attr("d", path); 96 | }); 97 | 98 | d3.json("data/counties-unemployment.json", function(json) { 99 | countiesUnemployment = json; 100 | }) 101 | 102 | d3.json("data/us-counties.json", function(json) { 103 | var visData = vis.data(); 104 | visData['counties'] = json; 105 | 106 | counties.selectAll("path") 107 | .data(json.features) 108 | .enter().append("path") 109 | .attr("class", function(d) { 110 | return quantize(false, d); 111 | }) 112 | .attr("d", path); 113 | }); 114 | 115 | function quantize(s, d) { 116 | var val = s ? statesUnemployment[d.id] : countiesUnemployment[d.id]; 117 | return "q" + Math.min(8, ~~(val * 9 / 12)) + "-9"; 118 | } 119 | 120 | function onZoom() { 121 | vis.state('zoom', d3.event.scale); 122 | vis.state('translateX', d3.event.translate[0]); 123 | vis.state('translateY', d3.event.translate[1]); 124 | } 125 | 126 | function redraw() { 127 | svg.attr("transform", "translate(" + 128 | (vis.state('translateX') || 0) + "," + 129 | (vis.state('translateY') || 0) + ")" + 130 | "scale(" + vis.state('zoom') + ")"); 131 | 132 | // If we're at zoom level > 2, show counties by making states 133 | // transparent 134 | states.selectAll('path') 135 | .style('fill', vis.state('zoom') >= 2 ? 'none' : null); 136 | } 137 | }).call(this); 138 | -------------------------------------------------------------------------------- /examples/unemployment/data/states-unemployment.json: -------------------------------------------------------------------------------- 1 | { 2 | "01": 9.0, 3 | "02": 7.6, 4 | "04": 9.5, 5 | "05": 8.0, 6 | "06": 11.7, 7 | "08": 8.3, 8 | "09": 8.8, 9 | "10": 7.3, 10 | "11": 10.2, 11 | "12": 10.5, 12 | "13": 9.8, 13 | "15": 6.7, 14 | "16": 8.7, 15 | "17": 9.8, 16 | "18": 9.0, 17 | "19": 5.9, 18 | "20": 6.7, 19 | "21": 9.5, 20 | "22": 7.3, 21 | "23": 7.5, 22 | "24": 7.0, 23 | "25": 7.4, 24 | "26": 10.3, 25 | "27": 6.4, 26 | "28": 10.7, 27 | "29": 8.6, 28 | "30": 6.8, 29 | "31": 4.4, 30 | "32": 13.5, 31 | "33": 5.4, 32 | "34": 9.3, 33 | "35": 7.4, 34 | "36": 8.2, 35 | "37": 10.5, 36 | "38": 3.5, 37 | "39": 8.6, 38 | "40": 6.2, 39 | "41": 9.5, 40 | "42": 7.9, 41 | "44": 11.3, 42 | "45": 10.3, 43 | "46": 4.7, 44 | "47": 9.2, 45 | "48": 7.9, 46 | "49": 6.7, 47 | "50": 5.6, 48 | "51": 6.2, 49 | "53": 9.2, 50 | "54": 8.0, 51 | "55": 7.5, 52 | "56": 6.0, 53 | "72": 15.7 54 | } -------------------------------------------------------------------------------- /examples/unemployment/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | U.S. Unemployment by County 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/unemployment/unemployment.css: -------------------------------------------------------------------------------- 1 | @import url("../lib/jquery-ui/jquery-ui.css"); 2 | 3 | body { 4 | font-family: Verdana, sans-serif; 5 | font-size: 12px; 6 | } 7 | 8 | #choropleth svg { 9 | background: #eee; 10 | width: 960px; 11 | height: 500px; 12 | } 13 | 14 | #annual_rates svg { 15 | background: #fff; 16 | width: 500px; 17 | height: 300px; 18 | border: 3px solid orange; 19 | padding: 10px 20px 50px 50px; 20 | } 21 | 22 | #counties path { 23 | stroke: #fff; 24 | stroke-width: .25px; 25 | } 26 | 27 | #states path { 28 | stroke: #fff; 29 | stroke-width: 1.5px; 30 | } 31 | 32 | #choropleth circle { 33 | fill: orange; 34 | cursor: pointer; 35 | } 36 | 37 | #choropleth line.annotation { 38 | stroke: orange; 39 | stroke-width: 3px; 40 | } 41 | 42 | #annual_rates path { 43 | stroke: forestgreen; 44 | stroke-width: 5px; 45 | fill: none; 46 | } 47 | 48 | path#usa { stroke: steelblue !important; } 49 | 50 | #annual_rates line.xGrid, #annual_rates line.yGrid { 51 | stroke: #eee; 52 | stroke-width: 1px; 53 | shape-rendering: crispEdges; 54 | } 55 | 56 | #annual_rates line.annotation { 57 | stroke: black; 58 | stroke-width: 1px; 59 | } 60 | 61 | #annual_rates text.xLbls, #annual_rates text.yLbls { 62 | fill: #ccc; 63 | } 64 | 65 | #annual_rates div.annotation { 66 | background: #fff; 67 | padding: 7px; 68 | } 69 | 70 | #annual_rates div.annotation a { 71 | background: #ddd; 72 | padding: 0 3px 3px 3px; 73 | font-weight: bold; 74 | text-decoration: none; 75 | color: #000; 76 | } 77 | 78 | #annual_rates div.annotation a:hover { background: #eee; } 79 | 80 | #annual_rates_title { 81 | position: absolute; 82 | top: 5px; 83 | left: 60px; 84 | width: 225px; 85 | background: #fff; 86 | font-weight: bold; 87 | } 88 | 89 | #annual_rates_title span.usa { color: steelblue; } 90 | #annual_rates_title span.location { color: forestgreen; } 91 | 92 | #zoom_slider { 93 | float: left; 94 | margin-right: 20px; 95 | height: 150px; 96 | } 97 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "n3", 3 | "version": "0.9.0", 4 | "description": "A small, free JavaScript library for telling stories through visualizations.", 5 | "keywords": [ 6 | "visualization", 7 | "story", 8 | "storytelling", 9 | "narrative" 10 | ], 11 | "author": "Arvind Satyanarayan (http://arvindsatya.com/)", 12 | "repository": { 13 | "type": "git", 14 | "url": "http://github.com/StanfordHCI/s3.git" 15 | }, 16 | "main": "n3.js", 17 | "dependencies": { 18 | "d3": "2.8.x" 19 | }, 20 | "devDependencies": { 21 | "uglify-js": "1.2.3", 22 | "coffee-script": "1.2.x", 23 | "jasmine-node": "1.0.x" 24 | }, 25 | "scripts": { 26 | "build": "cake build", 27 | "test": "cake test" 28 | } 29 | } -------------------------------------------------------------------------------- /src/annotation.coffee: -------------------------------------------------------------------------------- 1 | class N3Annotation 2 | @types = 3 | circle: 4 | enterFn: (r, [cx, cy]) -> 5 | selector = n3.util.getSelector('circle', @attrs) 6 | stage = if @vis()? then @vis().stage() else d3 7 | 8 | c = stage.selectAll(selector) 9 | .data(if @data()? then @data() else [0]) 10 | 11 | c.enter() 12 | .append('svg:circle') 13 | .attr('r', r) 14 | .attr('cx', cx) 15 | .attr('cy', cy) 16 | 17 | c.transition() 18 | .attr('r', r) 19 | .attr('cx', cx) 20 | .attr('cy', cy) 21 | 22 | @applyAttrs c 23 | @applyStyles c 24 | 25 | true 26 | 27 | exitFn: (r, [cx, cy]) -> 28 | selector = n3.util.getSelector('circle', @attrs) 29 | stage = if @vis()? then @vis().stage() else d3 30 | 31 | stage.selectAll(selector).remove() 32 | 33 | true 34 | 35 | ellipse: 36 | enterFn: ([rx, ry], [cx, cy]) -> 37 | selector = n3.util.getSelector('ellipse', @attrs) 38 | stage = if @vis()? then @vis().stage() else d3 39 | 40 | e = stage.selectAll(selector) 41 | .data(if @data()? then @data() else [0]) 42 | 43 | e.enter() 44 | .append('svg:ellipse') 45 | .attr('rx', rx) 46 | .attr('ry', ry) 47 | .attr('cx', cx) 48 | .attr('cy', cy) 49 | 50 | e.transition() 51 | .attr('rx', rx) 52 | .attr('ry', ry) 53 | .attr('cx', cx) 54 | .attr('cy', cy) 55 | 56 | @applyAttrs e 57 | @applyStyles e 58 | 59 | true 60 | 61 | exitFn: ([rx, ry], [cx, cy]) -> 62 | selector = n3.util.getSelector('ellipse', @attrs) 63 | stage = if @vis()? then @vis().stage() else d3 64 | 65 | stage.selectAll(selector).remove() 66 | 67 | true 68 | 69 | line: 70 | enterFn: ([x1, y1], arrow1, [x2, y2], arrow2) -> 71 | # TODO: add arrowheads 72 | selector = n3.util.getSelector('line', @attrs) 73 | stage = if @vis()? then @vis().stage() else d3 74 | 75 | l = stage.selectAll(selector) 76 | .data(if @data()? then @data() else [0]) 77 | 78 | l.enter() 79 | .append('svg:line') 80 | .attr('x1', x1) 81 | .attr('y1', y1) 82 | .attr('x2', x2) 83 | .attr('y2', y2) 84 | 85 | l.transition() 86 | .attr('x1', x1) 87 | .attr('y1', y1) 88 | .attr('x2', x2) 89 | .attr('y2', y2) 90 | 91 | @applyAttrs l 92 | @applyStyles l 93 | 94 | true 95 | 96 | exitFn: ([x1, y1], arrow1, [x2, y2], arrow2) -> 97 | selector = n3.util.getSelector('line', @attrs) 98 | stage = if @vis()? then @vis().stage() else d3 99 | 100 | stage.selectAll(selector).remove() 101 | 102 | true 103 | 104 | rectangle: 105 | enterFn: ([w, h], [x, y]) -> 106 | selector = n3.util.getSelector('rect', @attrs) 107 | stage = if @vis()? then @vis().stage() else d3 108 | 109 | r = stage.selectAll(selector) 110 | .data(if @data()? then @data() else [0]) 111 | 112 | r.enter() 113 | .append('svg:rect') 114 | .attr('x', x) 115 | .attr('y', y) 116 | .attr('width', w) 117 | .attr('height', h) 118 | 119 | r.transition() 120 | .attr('x', x) 121 | .attr('y', y) 122 | .attr('width', w) 123 | .attr('height', h) 124 | 125 | @applyAttrs r 126 | @applyStyles r 127 | 128 | true 129 | 130 | exitFn: ([w, h], [x, y]) -> 131 | selector = n3.util.getSelector('rect', @attrs) 132 | stage = if @vis()? then @vis().stage() else d3 133 | 134 | stage.selectAll(selector).remove() 135 | 136 | true 137 | 138 | label: 139 | enterFn: (text, html, [x, y]) -> 140 | selector = n3.util.getSelector('div', @attrs) 141 | stage = if @vis()? then @vis().stage() else d3 142 | 143 | # position the div absolutely 144 | @styles['position'] = 'absolute' 145 | @styles['left'] = (stage.property('offsetLeft') + x) + 'px' 146 | @styles['top'] = (stage.property('offsetTop') + y) + 'px' 147 | 148 | d = d3.select(stage[0][0].parentNode).selectAll(selector) 149 | .data(if @data()? then @data() else [0]) 150 | 151 | d.enter() 152 | .append('div') 153 | .text(text) 154 | .html(html) 155 | 156 | @applyAttrs d 157 | @applyStyles d 158 | 159 | true 160 | 161 | exitFn: (text, html, [x, y]) -> 162 | selector = n3.util.getSelector('div', @attrs) 163 | stage = if @vis()? then @vis().stage() else d3 164 | 165 | d3.selectAll(selector).remove() 166 | 167 | true 168 | 169 | constructor: (@type) -> 170 | @enterFn = N3Annotation.types[@type]?.enterFn 171 | @exitFn = N3Annotation.types[@type]?.exitFn 172 | 173 | @annotId = @type + "" + new Date().getTime() 174 | @autoExitFlag = true 175 | @arguments = [] 176 | @attrs = {} 177 | @styles = {} 178 | 179 | return this 180 | 181 | enter: (@enterFn) -> 182 | N3Annotation.types[@type] or= {} 183 | N3Annotation.types[@type].enterFn = enterFn 184 | 185 | return this 186 | 187 | exit: (@exitFn) -> 188 | N3Annotation.types[@type] or= {} 189 | N3Annotation.types[@type].exitFn = exitFn 190 | 191 | return this 192 | 193 | autoExit: (@autoExitFlag) -> 194 | return this 195 | 196 | data: (data) -> 197 | if arguments.length == 1 198 | @dataObj = data 199 | 200 | return this 201 | else 202 | return if typeof @dataObj == 'function' then @dataObj() else @dataObj 203 | 204 | vis: (vis) -> 205 | if arguments.length == 1 206 | vis = vis.visId if typeof vis == 'object' 207 | @visId = vis 208 | 209 | return this 210 | else 211 | N3Vis.lookup[@visId] 212 | 213 | add: -> 214 | @enterFn @arguments... 215 | 216 | remove: -> 217 | @exitFn @arguments... 218 | 219 | args: (@arguments...) -> 220 | return this 221 | 222 | attr: (key, value) -> 223 | if arguments.length == 2 224 | @attrs[key] = value 225 | 226 | return this 227 | else 228 | @attrs[key] 229 | 230 | applyAttrs: (selection) -> 231 | true unless selection? 232 | selection.attr(key, value) for key, value of @attrs 233 | true 234 | 235 | style: (key, value) -> 236 | if arguments.length == 2 237 | @styles[key] = value 238 | 239 | return this 240 | else 241 | @styles[key] 242 | 243 | applyStyles: (selection) -> 244 | true unless selection? 245 | selection.style(key, value) for key, value of @styles 246 | true 247 | 248 | # For built in types, expose arguments as methods. These are only setters. 249 | radius: (r) -> 250 | throw 'not an ellipse/circle' unless \ 251 | @type == 'circle' or @type == 'ellipse' 252 | 253 | @arguments[0] = r 254 | 255 | return this 256 | 257 | center: ([cx, cy]) -> 258 | throw 'not an ellipse/circle' unless \ 259 | @type == 'circle' or @type == 'ellipse' 260 | 261 | @arguments[1] = [cx, cy] 262 | 263 | return this 264 | 265 | start: ([x, y], arrow) -> 266 | throw 'not a line' unless @type == 'line' 267 | 268 | @arguments[0] = [x, y] 269 | @arguments[1] = arrow 270 | 271 | return this 272 | 273 | end: ([x, y], arrow) -> 274 | throw 'not a line' unless @type == 'line' 275 | 276 | @arguments[2] = [x, y] 277 | @arguments[3] = arrow 278 | 279 | return this 280 | 281 | size: ([x, y]) -> 282 | throw 'not a rectangle' unless @type == 'rectangle' 283 | 284 | @arguments[0] = [x, y] 285 | 286 | return this 287 | 288 | pos: ([x, y]) -> 289 | throw 'not a label/rectangle' unless \ 290 | @type == 'rectangle' or @type == 'label' 291 | 292 | @arguments[if @type == 'rectangle' then 1 else 2] = [x, y] 293 | 294 | return this 295 | 296 | text: (str) -> 297 | throw 'not a label' unless @type == 'label' 298 | 299 | @arguments[0] = str 300 | 301 | return this 302 | 303 | html: (html) -> 304 | throw 'not a label' unless @type == 'label' 305 | 306 | @arguments[1] = html 307 | 308 | return this 309 | 310 | n3.annotation = (typeId) -> 311 | new N3Annotation(typeId) 312 | 313 | n3.annotation.def = (typeId) -> 314 | new N3Annotation(typeId) -------------------------------------------------------------------------------- /src/scene.coffee: -------------------------------------------------------------------------------- 1 | class N3Scene 2 | @scenes = {} 3 | 4 | constructor: (@sceneId) -> 5 | # members = [ 6 | # { 7 | # visId: visId, 8 | # state: { 9 | # id: stateId 10 | # value: value 11 | # }, 12 | # member: annotation || function 13 | # trigger: triggerObj 14 | # 15 | # } 16 | # ] 17 | @members = [] 18 | @subScenes = 19 | order: '' 20 | 21 | return this 22 | 23 | set: (vis, stateId, val, triggerObj) -> 24 | vis = vis.visId if typeof vis == 'object' 25 | 26 | member = 27 | visId: vis 28 | state: 29 | id: stateId 30 | value: val 31 | trigger: triggerObj 32 | 33 | @members.push member 34 | 35 | return this 36 | 37 | add: (vis, memberObj, triggerObj) -> 38 | vis = vis.visId if typeof vis == 'object' 39 | 40 | member = 41 | visId: vis 42 | member: memberObj 43 | trigger: triggerObj 44 | 45 | @members.push member 46 | 47 | return this 48 | 49 | # 1-index 50 | member: (memberIndex) -> 51 | return @members[memberIndex + 1]?.member 52 | 53 | clone: (sceneID) -> 54 | newScene = n3.scene(sceneID) 55 | newScene.members = n3.util.clone @members 56 | newScene.subScenes = n3.util.clone @subScenes 57 | 58 | return newScene 59 | 60 | subScene: (subSceneId) -> 61 | if @subScenes[subSceneId]? 62 | return @subScenes[subSceneId] 63 | else 64 | subScene = new N3Scene(subSceneId) 65 | subScene.parent = this 66 | 67 | @subScenes[subSceneId] = subScene 68 | 69 | return subScene 70 | 71 | evalMember: (memberIndex) -> 72 | m = @members[memberIndex] 73 | return true unless m? 74 | 75 | vis = N3Vis.lookup[m.visId] 76 | 77 | if m.state? 78 | val = m.state.value 79 | 80 | # To allow for some fun, allow states to be set my a function. If the fn returns 81 | # false, assume that it'll be setting the state itself. 82 | if typeof val == 'function' 83 | val = val(vis, m.state.id) 84 | vis?.state(m.state.id, val) unless val == false 85 | else 86 | vis?.state(m.state.id, m.state.value) 87 | else 88 | if typeof m.member == 'function' 89 | m.member(vis) # call the function, pass vis as arg 90 | else if m.member?.annotId? # check for N3Annotation 91 | m.member.vis(m.visId) 92 | m.member.add() 93 | 94 | N3Trigger.notify(N3Trigger.TYPES.DELAY, N3Trigger.WHERE.DELAY + memberIndex, 1) 95 | 96 | true 97 | 98 | n3.scene = (sceneId) -> 99 | N3Scene.scenes[sceneId] or= new N3Scene(sceneId) -------------------------------------------------------------------------------- /src/state.coffee: -------------------------------------------------------------------------------- 1 | class N3State 2 | constructor: (@visId, @stateId, @validValues, @continuous) -> 3 | @bindings = [] 4 | 5 | vis: -> 6 | N3Vis.lookup[@visId] 7 | 8 | get: -> 9 | @val 10 | 11 | set: (val) -> 12 | @prevVal = @val 13 | valid = if @continuous then (val >= @validValues[0] && val <= @validValues[1]) \ 14 | else (@validValues.indexOf(val) != -1) 15 | 16 | # throw "#{val} not in the list of valid values: #{@validValues}" \ 17 | # unless valid 18 | 19 | @val = val 20 | 21 | @notify() 22 | 23 | bind: (funcPtr) -> 24 | @bindings.push funcPtr 25 | 26 | notify: -> 27 | @vis()?.renderFn?(); 28 | N3Trigger.notify(N3Trigger.TYPES.VIS, [@visId, @stateId], @val) 29 | 30 | binding.call(@vis(), @val) for binding in @bindings -------------------------------------------------------------------------------- /src/timeline.coffee: -------------------------------------------------------------------------------- 1 | class N3Timeline 2 | @triggers = {} 3 | @deferredTriggers = {} 4 | @transitions = {} 5 | @startTime = 0 6 | @elapsedTime = 0 7 | @paused = false 8 | 9 | @switchScene: (sceneId) -> 10 | @prevSceneId = @currSceneId 11 | @prevParentId = @currParentId 12 | prevScene = if @prevParentId? then \ 13 | N3Scene.scenes[@prevParentId].subScenes[@prevSceneId] else \ 14 | N3Scene.scenes[@prevSceneId] 15 | 16 | if sceneId.indexOf('>') != -1 # switching to subscene 17 | @currParentId = sceneId.split('>')[0].trim() 18 | currParent = N3Scene.scenes[@currParentId] 19 | @currSceneId = sceneId.split('>')[1].trim() 20 | currentScene = currParent.subScenes[@currSceneId] 21 | else 22 | @currParentId = undefined 23 | currParent = undefined 24 | @currSceneId = sceneId 25 | currentScene = N3Scene.scenes[@currSceneId] 26 | 27 | # We want to remove annotations only if prevScene and currentScene 28 | # aren't subscenes of the same parent scene 29 | if prevScene? 30 | unless prevScene.parent? and currentScene.parent? and \ 31 | prevScene.parent.sceneId == currentScene.parent.sceneId 32 | 33 | members = [] 34 | members.push m for m in prevScene.members 35 | if prevScene.parent? 36 | for subSceneId, subScene of prevScene.parent.subScenes 37 | continue unless subScene.members? 38 | members.push m for m in subScene.members 39 | 40 | for m in members 41 | @deregisterTrigger m.trigger 42 | 43 | continue if m.state? 44 | continue unless m.member?.annotId? # check for N3Annotation 45 | 46 | m.member.vis(m.visId) # just in case 47 | m.member.remove() if m.member.autoExitFlag 48 | 49 | # Run any transitions 50 | if @transitions[@prevSceneId]?[@currSceneId]? 51 | for transFunc in @transitions[@prevSceneId][@currSceneId] 52 | transFunc(prevScene, currentScene) 53 | 54 | # TODO: TRANSITIONS for Parents/Subscenes 55 | 56 | # if prevParent? or currParent? # Transition parsing for subscenes 57 | # 58 | # 59 | # 60 | # if @transitions[@prevSceneId]?[currParentId]? # Transitions for parent scenes 61 | # for transFunc in @transitions[@prevSceneId][currParentId] 62 | # transFunc(prevScene, currParent) 63 | # 64 | # if @transitions[@prevSceneId]?[sceneId]? # Transitions defined for parent > child 65 | # for transFunc in @transitions[@prevSceneId][sceneId] 66 | # transFunc(prevScene, currentScene) 67 | 68 | # Start the timer for the current scene after the transition is complete 69 | @start(true) 70 | 71 | evaluateMembers = []; 72 | 73 | if currentScene? 74 | for m, i in currentScene.members 75 | evaluateMembers[i] = false; 76 | 77 | if m.trigger? 78 | # If we see a trigger, feed it ambient values, to see 79 | # if the trigger conditions have already been met. If it has, 80 | # evaluate the member. If not, register the trigger and skip 81 | # evaluation. 82 | # ACTUALLY -- since we register triggers first, don't check ambience. 83 | # currentValue = null 84 | # 85 | # if m.trigger.type == N3Trigger.TYPES.VIS 86 | # visId = m.trigger.test[0] 87 | # stateId = m.trigger.test[1] 88 | # 89 | # currentValue = N3Vis.lookup[visId]?.state(stateId) 90 | # 91 | # if m.trigger.type == N3Trigger.TYPES.DOM or \ 92 | # m.trigger.type == N3Trigger.TYPES.DELAY or \ 93 | # m.trigger.evaluate(m.trigger.test, currentValue) == false 94 | 95 | @registerTrigger(m.trigger, i) 96 | continue 97 | 98 | evaluateMembers[i] = true; 99 | 100 | # In case the first member has a delay trigger 101 | N3Trigger.notify(N3Trigger.TYPES.DELAY, N3Trigger.WHERE.DELAY + '-1', 1) 102 | 103 | for m, i in currentScene.members 104 | currentScene.evalMember(i) if evaluateMembers[i] 105 | 106 | # Now that all members have been added, see if we can register 107 | # some of our defered triggers. 108 | for id, t of @deferredTriggers 109 | @registerTrigger(t.trigger, t.memberIndex) 110 | 111 | true 112 | 113 | @registerTrigger: (trigger, memberIndex) -> 114 | return true unless trigger? 115 | 116 | bindDelay = (trigger) -> 117 | # If it's a delay trigger, automatically bind it to the prev 118 | # member's index 119 | if trigger.type == N3Trigger.TYPES.DELAY 120 | trigger.where(N3Trigger.WHERE.DELAY + (memberIndex - 1)) 121 | 122 | # For OR or AND triggers, recursively apply this. 123 | if trigger.triggers? 124 | trigger.triggers = (bindDelay(t) for t in trigger.triggers) 125 | 126 | return trigger 127 | 128 | trigger = bindDelay(trigger); 129 | 130 | @triggers[trigger.triggerId] = 131 | sceneId: @currSceneId 132 | parentId: @currParentId 133 | memberIndex: memberIndex 134 | 135 | success = N3Trigger.register(trigger) 136 | if success 137 | delete @deferredTriggers[trigger.triggerId] 138 | else 139 | @deferredTriggers[trigger.triggerId] = 140 | trigger: trigger 141 | memberIndex: memberIndex 142 | 143 | true 144 | 145 | @deregisterTrigger: (trigger) -> 146 | return true unless @triggers[trigger?.triggerId]? 147 | 148 | delete @triggers[trigger.triggerId] 149 | delete @deferredTriggers[trigger.triggerId] 150 | N3Trigger.deregister(trigger) 151 | 152 | true 153 | 154 | @notifyTrigger: (trigger, evaluated) -> 155 | if @triggers[trigger.triggerId]? 156 | t = @triggers[trigger.triggerId] 157 | 158 | if t['eval'] == evaluated # If the trigger has already been evaluated 159 | return; # to this value, do nothing 160 | 161 | scene = if t.parentId? then \ 162 | N3Scene.scenes[t.parentId].subScenes[t.sceneId] \ 163 | else N3Scene.scenes[t.sceneId] 164 | 165 | if evaluated == true 166 | if t.memberIndex? # Member triggers 167 | scene?.evalMember(t.memberIndex) 168 | # else # Scene triggers 169 | # if t.parentId? then @switchScenes(t.parentId + '>' + t.sceneId) else @switchScenes(t.sceneId) 170 | else 171 | m = if t.memberIndex? then scene.members[t.memberIndex] else null 172 | if m?.member?.annotId? 173 | m.member.vis(m.visId) # just in case 174 | m?.member.remove() 175 | 176 | # See if this trigger has caused some member to be added such that 177 | # we can now register some of our defered triggers. 178 | for id, t of @deferredTriggers 179 | @registerTrigger(t.trigger, t.memberIndex) 180 | 181 | @triggers[trigger.triggerId]['eval'] = evaluated 182 | 183 | # Deregister a timeline trigger once it has fired because we can't go back in time 184 | @deregisterTrigger trigger if trigger.type == N3Trigger.TYPES.TIMELINE && evaluated != false 185 | true 186 | 187 | @parseTransSyntax: (transQ) -> 188 | return transQ if transQ instanceof Array 189 | 190 | scenes = [] 191 | 192 | if transQ == '*' 193 | for sceneId of N3Scene.scenes 194 | scenes.push sceneId 195 | 196 | scene = N3Scene.scenes[sceneId] 197 | for subSceneId of scene.subScenes 198 | scenes.push subSceneId 199 | else if transQ.indexOf('>') != -1 # Subscening 200 | scenes.push (id.trim() for id in transQ.split('>')).join('>') 201 | else # To allow individual sceneIds just to be passed without being arrays 202 | scenes.push transQ 203 | 204 | return scenes 205 | 206 | @transition: (fromScenes, toScenes, func) -> 207 | fromScenes = @parseTransSyntax fromScenes 208 | toScenes = @parseTransSyntax toScenes 209 | 210 | for fromScene in fromScenes 211 | # Allow people to pass in scene objs too 212 | fromSceneId = if typeof fromScene == 'object' then fromScene.sceneId else fromScene 213 | 214 | @transitions[fromSceneId] or= {} 215 | 216 | for toScene in toScenes 217 | # Allow people to pass in scene objs too 218 | toSceneId = if typeof toScene == 'object' then toScene.sceneId else toScene 219 | 220 | @transitions[fromSceneId][toSceneId] or= [] 221 | @transitions[fromSceneId][toSceneId].push func 222 | 223 | return this 224 | 225 | 226 | @start: (reset) -> 227 | if reset 228 | @startTime = Date.now() 229 | @elapsedTime = 0 230 | 231 | @paused = false 232 | d3.timer(=> @incrementTime()) 233 | 234 | @incrementTime: -> 235 | @elapsedTime = Date.now() - @startTime 236 | N3Trigger.notify(N3Trigger.TYPES.TIMELINE, N3Trigger.WHERE.ELAPSED, @elapsedTime) 237 | 238 | return @paused 239 | 240 | @pause: -> 241 | @paused = true 242 | 243 | n3.timeline or= {} 244 | n3.timeline.switchScene = (sceneId) -> 245 | N3Timeline.switchScene(sceneId) 246 | 247 | n3.timeline.pause = -> 248 | N3Timeline.pause() 249 | 250 | n3.timeline.resume = -> 251 | N3Timeline.start(false) 252 | 253 | n3.timeline.elapsedTime = -> 254 | N3Timeline.elapsedTime 255 | 256 | n3.timeline.transition = (fromScenes, toScenes, func) -> 257 | N3Timeline.transition(fromScenes, toScenes, func) -------------------------------------------------------------------------------- /src/trigger.coffee: -------------------------------------------------------------------------------- 1 | class N3Trigger 2 | @TYPES = 3 | VIS: 'vis' 4 | TIMELINE: 'timeline' 5 | DELAY: 'delay' 6 | DOM: 'dom' 7 | OR: 'or' 8 | AND: 'and' 9 | 10 | @CONDITIONS = 11 | IS: 'is' 12 | NOT: 'not' 13 | GT: 'gt' 14 | LT: 'lt' 15 | GTE: 'gte' 16 | LTE: 'lte' 17 | 18 | @WHERE = 19 | ELAPSED: 'elapsed' 20 | DELAY: 'delay_' 21 | 22 | @registered = {} 23 | 24 | @register = (trigger) -> 25 | success = true 26 | 27 | @registered[trigger.type] or= {} 28 | @registered[trigger.type][trigger.test] or= {} 29 | @registered[trigger.type][trigger.test][trigger.triggerId] = trigger 30 | 31 | # If we're a OR or AND trigger, register the individual sub-triggers 32 | # for fast look up, but have them point to the parent OR/AND trigger 33 | if trigger.type == @TYPES.OR or trigger.type == @TYPES.AND 34 | if trigger.triggers? 35 | for t in trigger.triggers 36 | @registered[t.type] or= {} 37 | @registered[t.type][t.test] or= {} 38 | @registered[t.type][t.test][t.triggerId] = trigger 39 | 40 | if t.type == @TYPES.DOM 41 | success = @bindDomTrigger(t) 42 | 43 | # If we're listening for a DOM event, use d3 to add an event listener 44 | # to the DOM node 45 | if trigger.type == @TYPES.DOM 46 | success = @bindDomTrigger(trigger) 47 | 48 | return success 49 | 50 | @deregister = (trigger) -> 51 | [type, test, triggerId] = [trigger.type, trigger.test, trigger.triggerId] 52 | 53 | trigger = @registered[type][test]?[triggerId] 54 | 55 | # If the trigger was a DOM event, use d3 to remove the event listener 56 | if type == @TYPES.DOM 57 | d3.select(trigger.test) 58 | .on(trigger.value, null) 59 | 60 | delete @registered[type][test]?[triggerId] 61 | 62 | true 63 | 64 | @bindDomTrigger = (trigger) -> 65 | elems = d3.select(trigger.test) 66 | return false unless elems[0][0]? # If elem doesn't exist, defer registration 67 | 68 | d3.select(trigger.test) 69 | .on(trigger.value, => 70 | return n3.trigger.notify(@TYPES.DOM, trigger.test, trigger.value) 71 | ) 72 | 73 | true 74 | 75 | @notify = (type, test, value) -> 76 | if @registered[type]?[test]? 77 | for triggerId, trigger of @registered[type][test] 78 | eval = trigger.evaluate(test, value); 79 | N3Timeline.notifyTrigger(trigger, eval) 80 | 81 | true; 82 | 83 | 84 | constructor: (binding, triggers...) -> 85 | if arguments.length == 1 # Only a single trigger 86 | if typeof binding == 'object' # could be an n3.vis or n3.timeline 87 | if binding.visId? 88 | @type = N3Trigger.TYPES.VIS 89 | @test or= [binding.visId, undefined] 90 | else 91 | @type = N3Trigger.TYPES.TIMELINE 92 | else if typeof binding == 'string' # could be a visID or a dom selector 93 | if N3Vis.lookup[binding]? 94 | @type = N3Trigger.TYPES.VIS 95 | @test or= [binding, undefined] 96 | else 97 | @type = N3Trigger.TYPES.DOM 98 | @test = binding 99 | else # n3.trigger.or / n3.trigger.and 100 | @type = binding 101 | @triggers = triggers 102 | 103 | @triggerId = @type + @test + '' + Date.now() + (Math.random() * 11) 104 | 105 | return this 106 | 107 | where: (test) -> 108 | if @type == N3Trigger.TYPES.VIS 109 | @test[1] = test 110 | else 111 | @test = test 112 | 113 | return this 114 | 115 | is: (@value) -> 116 | @condition = N3Trigger.CONDITIONS.IS 117 | 118 | return this 119 | 120 | not: (@value) -> 121 | @condition = N3Trigger.CONDITIONS.NOT 122 | 123 | return this 124 | 125 | gt: (@value) -> 126 | @condition = N3Trigger.CONDITIONS.GT 127 | 128 | return this 129 | 130 | greaterThan: (@value) -> 131 | @gt @value 132 | 133 | lt: (@value) -> 134 | @condition = N3Trigger.CONDITIONS.LT 135 | 136 | return this 137 | 138 | lessThan: (@value) -> 139 | @lt @value 140 | 141 | gte: (@value) -> 142 | @condition = N3Trigger.CONDITIONS.GTE 143 | 144 | return this 145 | 146 | greaterThanOrEqual: (@value) -> 147 | @gte @value 148 | 149 | lte: (@value) -> 150 | @condition = N3Trigger.CONDITIONS.LTE 151 | 152 | return this 153 | 154 | lessThanOrEqual: (@value) -> 155 | @lte @value 156 | 157 | on: (@value) -> 158 | return this 159 | 160 | # Delay triggers always evaluate as false but a d3.timer registers that calls 161 | # this function to manually notify the timeline. 162 | fireDelay: () -> 163 | N3Timeline.notifyTrigger(this, true) 164 | return true 165 | 166 | evaluate: (notifiedTest, notifiedVal, parent) -> 167 | if @type == N3Trigger.TYPES.DOM 168 | return true 169 | else if @type == N3Trigger.TYPES.OR 170 | for trigger in @triggers 171 | result = if (trigger.test + "") == (notifiedTest + "") then trigger.evaluate(notifiedTest, notifiedVal, this) else false 172 | return true if result == true # If at least one is true, then return 173 | # If we've made it through all triggers without 174 | return false # returning, then none of them were true 175 | else if @type == N3Trigger.TYPES.AND 176 | for trigger in @triggers 177 | result = if (trigger.test + "") == (notifiedTest + "") then trigger.evaluate(notifiedTest, notifiedVal, this) else false 178 | 179 | # In AND triggers, we want to check the ambient value of states 180 | # because they may have been triggered previously 181 | if result == false and trigger.type == N3Trigger.TYPES.VIS 182 | result = trigger.evaluate(trigger.test, N3Vis.lookup[trigger.test[0]]?.state(trigger.test[1])) 183 | 184 | return false if result == false # If at least one is false, then return 185 | # If we've made it through all triggers without 186 | return true # returning, then none of them were false 187 | else if @type == N3Trigger.TYPES.DELAY # If it's a delay, register a timer 188 | c = => return if parent? then parent.fireDelay() else @fireDelay() 189 | d3.timer(c, @value) # and evaluate this trigger as false 190 | return false 191 | else 192 | return true if (@type == N3Trigger.TYPES.DOM) or 193 | (@condition == N3Trigger.CONDITIONS.IS and notifiedVal == @value) or \ 194 | (@condition == N3Trigger.CONDITIONS.NOT and notifiedVal != @value) or \ 195 | (@condition == N3Trigger.CONDITIONS.GT and notifiedVal > @value) or \ 196 | (@condition == N3Trigger.CONDITIONS.LT and notifiedVal < @value) or \ 197 | (@condition == N3Trigger.CONDITIONS.GTE and notifiedVal >= @value) or \ 198 | (@condition == N3Trigger.CONDITIONS.LTE and notifiedVal <= @value) 199 | 200 | return false 201 | 202 | n3.trigger = (binding) -> 203 | new N3Trigger(binding) 204 | 205 | n3.trigger.or = (triggers...) -> 206 | new N3Trigger(N3Trigger.TYPES.OR, triggers...) 207 | 208 | n3.trigger.and = (triggers...) -> 209 | new N3Trigger(N3Trigger.TYPES.AND, triggers...) 210 | 211 | n3.trigger.notify = (type, test, value) -> 212 | N3Trigger.notify(type, test, value) 213 | 214 | n3.trigger.afterPrev = (delay) -> 215 | t = new N3Trigger(N3Trigger.TYPES.DELAY, N3Trigger.WHERE.DELAY) 216 | t.gte(delay) -------------------------------------------------------------------------------- /src/util.coffee: -------------------------------------------------------------------------------- 1 | n3.util = {} 2 | 3 | n3.util.getSelector = (selector, attrs) -> 4 | if attrs?.id? 5 | selector + '#' + attrs['id'] 6 | else if attrs?.class? 7 | selector + '.' + attrs['class'].split(' ').join('.') 8 | else 9 | selector 10 | 11 | n3.util.clone = (obj) -> 12 | return obj unless obj? and typeof obj == 'object' 13 | 14 | if obj instanceof Array 15 | copy = (n3.util.clone elem for elem in obj) 16 | 17 | else if obj instanceof Object 18 | copy = {} 19 | copy[key] = n3.util.clone val for key, val of obj 20 | 21 | return copy 22 | 23 | n3.util.iterate = (args...) -> 24 | arr = [] 25 | step = args[args.length - 2] 26 | delay = args[args.length - 1] 27 | 28 | if arguments.length == 3 29 | arr = args[0] 30 | else if arguments.length == 4 31 | arr = d3.range(args[0], args[1], step) 32 | 33 | return (vis, stateId) -> 34 | currIndex = 0 35 | 36 | callback = -> 37 | vis.state(stateId, arr[currIndex++]) 38 | window.clearInterval(interval) if currIndex >= arr.length 39 | 40 | interval = window.setInterval(callback, delay) 41 | false -------------------------------------------------------------------------------- /src/version.coffee: -------------------------------------------------------------------------------- 1 | window.n3 = 2 | version: '0.9.0' -------------------------------------------------------------------------------- /src/vis.coffee: -------------------------------------------------------------------------------- 1 | class N3Vis 2 | @lookup = {} # static lookup table visId -> N3Vis 3 | 4 | constructor: (@visId) -> 5 | @states = {} # lookup table stateId -> N3State 6 | @consts = {} # lookup table constId -> constVal 7 | 8 | return this 9 | 10 | id: -> 11 | @visId 12 | 13 | stage: (sel, w, h) -> 14 | if arguments.length == 3 15 | @stageSelector = sel 16 | @stageWidth = w 17 | @stageHeight = h 18 | 19 | return this 20 | else 21 | d3.select(@stageSelector) 22 | 23 | width: (width) -> 24 | if arguments.length == 1 25 | @stageWidth = width 26 | 27 | return this 28 | else 29 | @stageWidth 30 | 31 | height: (height) -> 32 | if arguments.length == 1 33 | @stageHeight = height 34 | 35 | return this 36 | else 37 | @stageHeight 38 | 39 | data: (data) -> 40 | if arguments.length == 1 41 | @dataObj = data 42 | 43 | return this 44 | else 45 | return if typeof @dataObj == 'function' then @dataObj() else @dataObj 46 | 47 | state: (stateId, arg2) -> 48 | if arguments.length >= 2 # state can be a setter 49 | if arg2 instanceof Array # or a definition of a new state 50 | @states[stateId] = new N3State(@visId, stateId, arg2, arguments[2]) 51 | else 52 | throw "no such state '#{stateId}'" unless @states[stateId]? 53 | @states[stateId]?.set(arg2) 54 | 55 | return this 56 | else 57 | throw "no such state '#{stateId}'" unless @states[stateId]? 58 | @states[stateId]?.get() 59 | 60 | const: (constId, value) -> 61 | if arguments.length == 2 62 | # consts are read only, so only add them if they haven't already 63 | # been defined 64 | @consts[constId] = value unless constId of @consts 65 | 66 | return this 67 | else 68 | @consts[constId] 69 | # We don't actually want to call the function because people are probably 70 | # expecting a fn ptr returned. 71 | # if typeof constVal == 'function' then constVal.apply(this) else constVal 72 | 73 | bind: (stateId, funcPtr) -> 74 | throw "no such state '#{stateId}'" unless @states[stateId]? 75 | 76 | @states[stateId]?.bind(funcPtr) 77 | 78 | return this 79 | 80 | render: (@renderFn) -> 81 | return this 82 | 83 | n3.vis = (visId) -> 84 | N3Vis.lookup[visId] or= new N3Vis(visId) -------------------------------------------------------------------------------- /test/annotation.test.coffee: -------------------------------------------------------------------------------- 1 | describe 'annotation', -> 2 | vis = null 3 | 4 | beforeEach -> 5 | d3.select('body') 6 | .append('svg:svg') 7 | .attr('id', 'stage') 8 | .attr('width', 500) 9 | .attr('height', 600) 10 | 11 | vis = n3.vis('annotation_test') 12 | .stage('#stage', 500, 600) 13 | 14 | afterEach -> 15 | vis = null 16 | d3.selectAll('#stage').remove() 17 | 18 | it 'sets/gets the vis', -> 19 | a = n3.annotation('custom').vis(vis) 20 | 21 | expect(a.vis().stage()).toEqual d3.select('#stage') 22 | expect(a.vis().width()).toBe 500 23 | expect(a.vis().height()).toBe 600 24 | 25 | it 'sets/gets data', -> 26 | d = ['do', 're', 'mi', 'fa', 'so'] 27 | 28 | # Testing if .data() sets and is chainable 29 | expect(n3.annotation('custom').data(d).data()).toBe d 30 | 31 | # Testing if .data() supports different data types 32 | d1 = {Jagger: 'Rock', Elvis: 'Roll'} 33 | expect(n3.annotation('custom').data(d1).data()).toBe d1 34 | 35 | d2 = 36 | brother: 37 | name: 'Max' 38 | age: 11 39 | sister: 40 | name: 'Ida' 41 | age: 9 42 | 43 | expect(n3.annotation('custom').data(d2).data()).toBe d2 44 | 45 | # Tests that .data is not set for another vis 46 | expect(n3.annotation('custom2').data()).toBeUndefined 47 | 48 | it 'is a custom annotation', -> 49 | f = (arg1, arg2) -> 50 | console.log('Arg1: ' + arg1 + ' Arg2: ' + arg2) 51 | 52 | f1 = (arg1, arg2) -> 53 | console.log('F1Arg1: ' + arg1 + ' F1Arg2: ' + arg2) 54 | 55 | a = n3.annotation('custom') 56 | expect(a.enterFn).toBeUndefined 57 | 58 | a.enter(f) 59 | .exit(f1) 60 | .args('hello', [1, 2, 3], { 'foo': 'bar' }, 'world') 61 | 62 | expect(a.enterFn).toBe f 63 | expect(a.exitFn).toBe f1 64 | 65 | spyOn(a, 'enterFn') 66 | a.add() 67 | expect(a.enterFn).toHaveBeenCalledWith('hello', [1, 2, 3], { 'foo': 'bar' }, 'world') 68 | 69 | spyOn(a, 'exitFn') 70 | a.remove() 71 | expect(a.exitFn).toHaveBeenCalledWith('hello', [1, 2, 3], { 'foo': 'bar' }, 'world') 72 | 73 | it 'is a circle', -> 74 | a = n3.annotation('circle') 75 | .center([15, 45]) 76 | .radius(5) 77 | .data([1]) # to test chaining 78 | 79 | spyOn(a, 'enterFn') 80 | a.add() 81 | expect(a.enterFn).toHaveBeenCalledWith(5, [15, 45]) 82 | 83 | spyOn(a, 'exitFn') 84 | a.remove() 85 | expect(a.exitFn).toHaveBeenCalledWith(5, [15, 45]) 86 | 87 | it 'draws a circle', -> 88 | a = n3.annotation('circle') 89 | .center([15, 45]) 90 | .radius(5) 91 | .vis(vis) 92 | 93 | a.add() 94 | 95 | c = vis.stage().selectAll('circle') 96 | expect(c.attr('r')).toBe '5' 97 | expect(c.attr('cx')).toBe '15' 98 | expect(c.attr('cy')).toBe '45' 99 | 100 | it 'is an ellipse', -> 101 | a = n3.annotation('ellipse') 102 | .radius([3, 7]) 103 | .center([11, 21]) 104 | .data([2]) # to test chaining 105 | 106 | spyOn(a, 'enterFn') 107 | a.add() 108 | expect(a.enterFn).toHaveBeenCalledWith([3, 7], [11, 21]) 109 | 110 | spyOn(a, 'exitFn') 111 | a.remove() 112 | expect(a.exitFn).toHaveBeenCalledWith([3, 7], [11, 21]) 113 | 114 | it 'draws an ellipse', -> 115 | a = n3.annotation('ellipse') 116 | .radius([3, 7]) 117 | .center([11, 21]) 118 | .vis(vis) 119 | 120 | a.add() 121 | 122 | e = vis.stage().selectAll('ellipse') 123 | expect(e.attr('rx')).toBe '3' 124 | expect(e.attr('ry')).toBe '7' 125 | expect(e.attr('cx')).toBe '11' 126 | expect(e.attr('cy')).toBe '21' 127 | 128 | it 'is a line', -> 129 | a = n3.annotation('line') 130 | .start([6, 8], true) 131 | .end([32, 12], false) 132 | .data([3]) # to test chaining 133 | 134 | spyOn(a, 'enterFn') 135 | a.add() 136 | expect(a.enterFn).toHaveBeenCalledWith([6, 8], true, [32, 12], false) 137 | 138 | spyOn(a, 'exitFn') 139 | a.remove() 140 | expect(a.exitFn).toHaveBeenCalledWith([6, 8], true, [32, 12], false) 141 | 142 | it 'draws a line', -> 143 | a = n3.annotation('line') 144 | .start([6, 8], true) 145 | .end([32, 12], false) 146 | .vis(vis) 147 | 148 | a.add() 149 | 150 | l = vis.stage().selectAll('line') 151 | expect(l.attr('x1')).toBe '6' 152 | expect(l.attr('y1')).toBe '8' 153 | expect(l.attr('x2')).toBe '32' 154 | expect(l.attr('y2')).toBe '12' 155 | 156 | it 'is a rectangle', -> 157 | a = n3.annotation('rectangle') 158 | .pos([17, 14]) 159 | .size([42, 91]) 160 | .data([4]) # to test chaining 161 | 162 | spyOn(a, 'enterFn') 163 | a.add() 164 | expect(a.enterFn).toHaveBeenCalledWith([42, 91], [17, 14]) 165 | 166 | spyOn(a, 'exitFn') 167 | a.remove() 168 | expect(a.exitFn).toHaveBeenCalledWith([42, 91], [17, 14]) 169 | 170 | it 'draws a rectangle', -> 171 | a = n3.annotation('rectangle') 172 | .pos([17, 14]) 173 | .size([42, 91]) 174 | .vis(vis) 175 | 176 | a.add() 177 | 178 | r = vis.stage().selectAll('rect') 179 | expect(r.attr('width')).toBe '42' 180 | expect(r.attr('height')).toBe '91' 181 | expect(r.attr('x')).toBe '17' 182 | expect(r.attr('y')).toBe '14' 183 | 184 | it 'is a label', -> 185 | a = n3.annotation('label') 186 | .html('

Hello

') 187 | .text('world') 188 | .pos([3, 7]) 189 | .data([4]) # to test chaining 190 | 191 | spyOn(a, 'enterFn') 192 | a.add() 193 | expect(a.enterFn).toHaveBeenCalledWith('world', '

Hello

', [3, 7]) 194 | 195 | spyOn(a, 'exitFn') 196 | a.remove() 197 | expect(a.exitFn).toHaveBeenCalledWith('world', '

Hello

', [3, 7]) 198 | 199 | it 'draws a label', -> 200 | a = n3.annotation('label') 201 | .html('

Hello

') 202 | .text('world') 203 | .pos([3, 7]) 204 | .attr('id', 'label') 205 | .vis(vis) 206 | 207 | a.add() 208 | 209 | d = d3.selectAll('div#label') 210 | 211 | expect(d.style('position')).toBe 'absolute' 212 | expect(d.style('left')).toBe '11px' 213 | expect(d.style('top')).toBe '15px' 214 | expect(d.html()).toBe '

Hello

' 215 | 216 | it 'sets/gets attrs', -> 217 | a = n3.annotation('circle') 218 | .center([15, 45]) 219 | .radius(5) 220 | .attr('foo', 'baz') 221 | .attr('r', '15') 222 | .style('position', 'absolute') 223 | .vis(vis) 224 | 225 | a.add() 226 | 227 | c = vis.stage().selectAll('circle') 228 | expect(c.attr('foo')).toBe 'baz' 229 | expect(c.attr('r')).toBe '15' 230 | 231 | it 'sets/gets styles', -> 232 | a = n3.annotation('circle') 233 | .center([15, 45]) 234 | .radius(5) 235 | .style('display', 'none') 236 | .style('fill', '#ffffff') 237 | .vis(vis) 238 | 239 | a.add() 240 | 241 | c = vis.stage().selectAll('circle') 242 | expect(c.style('display')).toBe 'none' 243 | expect(c.style('fill')).toBe '#FFFFFF' 244 | -------------------------------------------------------------------------------- /test/domTimingTest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | N3 System Test 5 | 6 | 7 | 35 | 36 | 37 | 42 | 43 |
44 | 45 | 46 |
47 |
48 |

Scene One

49 | 50 |

Hong Kong Phooey, number one super guy. Hong Kong Phooey, quicker than the human eye. He's got style, a groovy style, and a car that just won't stop. When the going gets tough, he's really rough, with a Hong Kong Phooey chop (Hi-Ya!). Hong Kong Phooey, number one super guy. Hong Kong Phooey, quicker than the human eye. Hong Kong Phooey, he's fan-riffic!

51 |
52 | 53 | 58 | 59 | 64 |
65 | 66 | 156 | -------------------------------------------------------------------------------- /test/scene.test.coffee: -------------------------------------------------------------------------------- 1 | describe "scene", -> 2 | vis = null 3 | scene = null 4 | f = null 5 | a = null 6 | 7 | beforeEach -> 8 | vis = n3.vis('sceneTest') 9 | .state('state_1', ['valid', 'values']) 10 | .state('state_2', [true, false]) 11 | .render(-> 12 | console.log('render!'); 13 | ) 14 | 15 | f = -> 16 | console.log('hello') 17 | 18 | a = n3.annotation('f') 19 | .enter(f) 20 | 21 | it "sets the state", -> 22 | scene = n3.scene('scene_1') 23 | .set(vis, 'state_1', 'valid') 24 | .set('sceneTest', 'state_2', false) 25 | 26 | expect(scene.members[0].visId).toBe vis.visId 27 | expect(scene.members[0].state.id).toBe 'state_1' 28 | expect(scene.members[0].state.value).toBe 'valid' 29 | 30 | expect(scene.members[1].visId).toBe vis.visId 31 | expect(scene.members[1].state.id).toBe 'state_2' 32 | expect(scene.members[1].state.value).toBe false 33 | 34 | it "adds members", -> 35 | scene = n3.scene('scene_2') 36 | .add(vis, f) 37 | .add(vis, a) 38 | 39 | expect(scene.members[0].visId).toBe vis.visId 40 | expect(scene.members[0].member).toBe f 41 | 42 | expect(scene.members[1].visId).toBe vis.visId 43 | expect(scene.members[1].member).toBe a 44 | 45 | it "evaluates members", -> 46 | scene = n3.scene('scene_3') 47 | .set(vis, 'state_1', 'values') 48 | .add(vis, f) 49 | .set(vis, 'state_2', true) 50 | .add(vis, a) 51 | 52 | spyOn(vis, 'renderFn') 53 | scene.evalMember(0) 54 | expect(vis.state('state_1')).toBe 'values' 55 | expect(vis.renderFn).toHaveBeenCalledWith() 56 | 57 | spyOn(scene.members[1], 'member') 58 | scene.evalMember(1) 59 | expect(vis.renderFn.callCount).toBe 1 # adding an annotation shouldn't re-render 60 | expect(scene.members[1].member).toHaveBeenCalledWith(vis) 61 | 62 | scene.evalMember(2) 63 | expect(vis.state('state_2')).toBe true 64 | expect(vis.renderFn).toHaveBeenCalledWith() 65 | expect(vis.renderFn.callCount).toBe 2 66 | 67 | spyOn(a, 'enterFn') 68 | scene.evalMember(3) 69 | expect(a.enterFn).toHaveBeenCalledWith() 70 | 71 | 72 | it "clones a scene", -> 73 | scene = n3.scene('scene_4') 74 | .set(vis, 'state_1', 'values') 75 | .add(vis, f) 76 | .set(vis, 'state_2', true) 77 | 78 | scene2 = scene.clone('scene_5') 79 | .add('sceneTest', a) 80 | 81 | expect(scene2.members[0].visId).toBe vis.visId 82 | expect(scene2.members[0].state.id).toBe 'state_1' 83 | expect(scene2.members[0].state.value).toBe 'values' 84 | 85 | spyOn(vis, 'renderFn') 86 | scene2.evalMember(0) 87 | expect(vis.state('state_1')).toBe 'values' 88 | expect(vis.renderFn).toHaveBeenCalledWith() 89 | 90 | expect(scene2.members[1].visId).toBe vis.visId 91 | expect(scene2.members[1].member).toBe f 92 | 93 | spyOn(scene2.members[1], 'member') 94 | scene2.evalMember(1) 95 | expect(vis.renderFn.callCount).toBe 1 # adding an annotation shouldn't re-render 96 | expect(scene2.members[1].member).toHaveBeenCalledWith(vis) 97 | 98 | expect(scene2.members[2].visId).toBe vis.visId 99 | expect(scene2.members[2].state.id).toBe 'state_2' 100 | expect(scene2.members[2].state.value).toBe true 101 | 102 | scene2.evalMember(2) 103 | expect(vis.state('state_2')).toBe true 104 | expect(vis.renderFn).toHaveBeenCalledWith() 105 | expect(vis.renderFn.callCount).toBe 2 106 | 107 | expect(scene2.members[3].visId).toBe vis.visId 108 | expect(scene2.members[3].member).toBe a 109 | 110 | spyOn(a, 'enterFn') 111 | scene2.evalMember(3) 112 | expect(a.enterFn).toHaveBeenCalledWith() 113 | 114 | it "is a subscene", -> 115 | scene = n3.scene('scene_6') 116 | .set(vis, 'state_1', 'value') 117 | .add(vis, f) 118 | 119 | subScene = scene.subScene('scene_6a') 120 | .set(vis, 'state_2', true) 121 | .add(vis, a) 122 | 123 | expect(subScene.parent).toBe scene 124 | 125 | expect(subScene.members[0].visId).toBe vis.visId 126 | expect(subScene.members[0].state.id).toBe 'state_2' 127 | expect(subScene.members[0].state.value).toBe true 128 | 129 | spyOn(vis, 'renderFn') 130 | subScene.evalMember(0) 131 | expect(vis.state('state_2')).toBe true 132 | expect(vis.renderFn).toHaveBeenCalledWith() 133 | expect(vis.renderFn.callCount).toBe 1 134 | 135 | expect(subScene.members[1].visId).toBe vis.visId 136 | expect(subScene.members[1].member).toBe a 137 | 138 | spyOn(a, 'enterFn') 139 | subScene.evalMember(1) 140 | expect(a.enterFn).toHaveBeenCalledWith() -------------------------------------------------------------------------------- /test/support/jasmine.yml: -------------------------------------------------------------------------------- 1 | # src_files 2 | # 3 | # Return an array of filepaths relative to src_dir to include before jasmine specs. 4 | # Default: [] 5 | # 6 | # EXAMPLE: 7 | # 8 | # src_files: 9 | # - lib/source1.js 10 | # - lib/source2.js 11 | # - dist/**/*.js 12 | # 13 | src_files: 14 | - n3.js 15 | 16 | # stylesheets 17 | # 18 | # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. 19 | # Default: [] 20 | # 21 | # EXAMPLE: 22 | # 23 | # stylesheets: 24 | # - css/style.css 25 | # - stylesheets/*.css 26 | # 27 | stylesheets: 28 | 29 | # helpers 30 | # 31 | # Return an array of filepaths relative to spec_dir to include before jasmine specs. 32 | # Default: ["helpers/**/*.js"] 33 | # 34 | # EXAMPLE: 35 | # 36 | # helpers: 37 | # - helpers/**/*.js 38 | # 39 | helpers: 40 | - ../node_modules/d3/d3.v2.js 41 | 42 | # spec_files 43 | # 44 | # Return an array of filepaths relative to spec_dir to include. 45 | # Default: ["**/*[sS]pec.js"] 46 | # 47 | # EXAMPLE: 48 | # 49 | # spec_files: 50 | # - **/*[sS]pec.js 51 | # 52 | spec_files: 53 | - *.test.coffee 54 | 55 | # src_dir 56 | # 57 | # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. 58 | # Default: project root 59 | # 60 | # EXAMPLE: 61 | # 62 | # src_dir: public 63 | # 64 | src_dir: 65 | 66 | # spec_dir 67 | # 68 | # Spec directory path. Your spec_files must be returned relative to this path. 69 | # Default: spec/javascripts 70 | # 71 | # EXAMPLE: 72 | # 73 | # spec_dir: spec/javascripts 74 | # 75 | spec_dir: test 76 | -------------------------------------------------------------------------------- /test/support/jasmine_config.rb: -------------------------------------------------------------------------------- 1 | module Jasmine 2 | class Config 3 | 4 | # Add your overrides or custom config code here 5 | 6 | end 7 | end 8 | 9 | 10 | # Note - this is necessary for rspec2, which has removed the backtrace 11 | module Jasmine 12 | class SpecBuilder 13 | def declare_spec(parent, spec) 14 | me = self 15 | example_name = spec["name"] 16 | @spec_ids << spec["id"] 17 | backtrace = @example_locations[parent.description + " " + example_name] 18 | parent.it example_name, {} do 19 | me.report_spec(spec["id"]) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/support/jasmine_runner.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(ENV['JASMINE_GEM_PATH']) if ENV['JASMINE_GEM_PATH'] # for gem testing purposes 2 | 3 | require 'rubygems' 4 | require 'jasmine' 5 | jasmine_config_overrides = File.expand_path(File.join(File.dirname(__FILE__), 'jasmine_config.rb')) 6 | require jasmine_config_overrides if File.exist?(jasmine_config_overrides) 7 | if Jasmine::Dependencies.rspec2? 8 | require 'rspec' 9 | else 10 | require 'spec' 11 | end 12 | 13 | jasmine_config = Jasmine::Config.new 14 | spec_builder = Jasmine::SpecBuilder.new(jasmine_config) 15 | 16 | should_stop = false 17 | 18 | if Jasmine::Dependencies.rspec2? 19 | RSpec.configuration.after(:suite) do 20 | spec_builder.stop if should_stop 21 | end 22 | else 23 | Spec::Runner.configure do |config| 24 | config.after(:suite) do 25 | spec_builder.stop if should_stop 26 | end 27 | end 28 | end 29 | 30 | spec_builder.start 31 | should_stop = true 32 | spec_builder.declare_suites 33 | -------------------------------------------------------------------------------- /test/timeline.test.coffee: -------------------------------------------------------------------------------- 1 | describe "timeline", -> 2 | scene1 = null 3 | scene2 = null 4 | vis = null 5 | 6 | beforeEach -> 7 | d3.select('body') 8 | .append('svg:svg') 9 | .attr('id', 'stage') 10 | .attr('width', 500) 11 | .attr('height', 600) 12 | 13 | vis = n3.vis('visTimelineTest') 14 | .stage('#stage', 500, 600) 15 | .state('state_1', ['valid', 'values']) 16 | .state('state_2', [true, false]) 17 | .render(-> 18 | console.log('timelineTest render'); 19 | ) 20 | 21 | afterEach -> 22 | vis = null 23 | d3.selectAll('#stage').remove() 24 | 25 | # Simple scene switching, tests that members are added/removed 26 | it "switches simple scenes", -> 27 | scene1 = n3.scene('timelineScene_1') 28 | .set('visTimelineTest', 'state_1', 'valid') 29 | .add('visTimelineTest', n3.annotation('circle') 30 | .radius(5) 31 | .center([1, 2]) 32 | .attr('id', 'hello')) 33 | .set('visTimelineTest', 'state_2', true) 34 | .add('visTimelineTest', n3.annotation('circle') 35 | .radius(7) 36 | .center([8, 4]) 37 | .attr('id', 'goodbye') 38 | .autoExit(false)) 39 | 40 | scene2 = n3.scene('timelineScene_2') 41 | .add('visTimelineTest', n3.annotation('rectangle') 42 | .size([5, 15]) 43 | .pos([1, 11])) 44 | .set('visTimelineTest', 'state_1', 'values') 45 | .set('visTimelineTest', 'state_2', false) 46 | 47 | n3.timeline.switchScene('timelineScene_1') 48 | expect(vis.state('state_1')).toBe 'valid' 49 | expect(vis.state('state_2')).toBe true 50 | expect(d3.selectAll('circle')[0].length).toBe 2 51 | 52 | n3.timeline.switchScene('timelineScene_2') 53 | expect(vis.state('state_1')).toBe 'values' 54 | expect(vis.state('state_2')).toBe false 55 | expect(d3.selectAll('circle')[0].length).toBe 1 56 | expect(d3.selectAll('rect')[0].length).toBe 1 57 | 58 | it "registers and fires triggers", -> 59 | n3.timeline.switchScene('timelineScene_1') 60 | n3.timeline.switchScene('timelineScene_2') 61 | 62 | scene3 = n3.scene('timelineScene_3') 63 | .add(vis, 64 | n3.annotation('ellipse') 65 | .attr('id', 'foo') 66 | .radius([1, 2]) 67 | .center([3, 4]), 68 | n3.trigger(vis) 69 | .where('state_1') 70 | .is('valid')) 71 | .add(vis, 72 | n3.annotation('line') 73 | .attr('id', 'bar') 74 | .start([1, 2]) 75 | .end([3, 4]), 76 | n3.trigger(vis) 77 | .where('state_2') 78 | .is(true)) 79 | 80 | n3.timeline.switchScene('timelineScene_3') 81 | expect(vis.state('state_1')).toBe 'values' 82 | expect(vis.state('state_2')).toBe false 83 | vis.state('state_2', true) 84 | expect(d3.selectAll('circle')[0].length).toBe 1 85 | expect(d3.selectAll('rect')[0].length).toBe 0 86 | expect(d3.selectAll('ellipse')[0].length).toBe 0 87 | expect(d3.selectAll('line')[0].length).toBe 1 88 | 89 | vis.state('state_1', 'valid') 90 | expect(d3.selectAll('ellipse')[0].length).toBe 1 91 | 92 | it "delay triggers", -> 93 | vis.state('state_1', 'valid') 94 | vis.state('state_2', true) 95 | 96 | n3.timeline.switchScene('timelineScene_1') 97 | 98 | expect(d3.selectAll('circle')[0].length).toBe 2 99 | expect(d3.selectAll('rect')[0].length).toBe 0 100 | expect(d3.selectAll('ellipse')[0].length).toBe 0 101 | expect(d3.selectAll('line')[0].length).toBe 0 102 | 103 | expect(vis.state('state_1')).toBe 'valid' 104 | expect(vis.state('state_2')).toBe true 105 | 106 | scene4 = n3.scene('timelineScene_4') 107 | .add(vis, 108 | n3.annotation('ellipse') 109 | .attr('id', 'foo') 110 | .radius([1, 2]) 111 | .center([3, 4]), 112 | n3.trigger(vis) 113 | .where('state_1') 114 | .is('values')) 115 | .add(vis, 116 | n3.annotation('line') 117 | .attr('id', 'bar') 118 | .start([1, 2]) 119 | .end([3, 4]), 120 | n3.trigger.afterPrev(1)) 121 | 122 | n3.timeline.switchScene('timelineScene_4') 123 | expect(d3.selectAll('ellipse')[0].length).toBe 0 124 | expect(d3.selectAll('line')[0].length).toBe 0 125 | 126 | vis.state('state_2', false) 127 | expect(vis.state('state_2')).toBe false 128 | expect(d3.selectAll('circle')[0].length).toBe 1 129 | expect(d3.selectAll('rect')[0].length).toBe 0 130 | expect(d3.selectAll('ellipse')[0].length).toBe 0 131 | expect(d3.selectAll('line')[0].length).toBe 0 132 | 133 | vis.state('state_1', 'values') 134 | expect(d3.selectAll('ellipse')[0].length).toBe 1 135 | # expect(d3.selectAll('line')[0].length).toBe 1 136 | 137 | it "transitions scenes", -> 138 | n3.timeline.switchScene('timelineScene_1') 139 | 140 | t1 = (fromScene, toScene) -> 141 | console.log('t1: ' + fromScene.sceneId + ' -> ' + toScene.sceneId) 142 | 143 | t2 = (fromScene, toScene) -> 144 | console.log('t2: ' + fromScene.sceneId + ' -> ' + toScene.sceneId) 145 | 146 | t3 = (fromScene, toScene) -> 147 | console.log('t3: ' + fromScene.sceneId + ' -> ' + toScene.sceneId) 148 | 149 | n3.timeline.transition('*', '*', t1) 150 | .transition('timelineScene_3', 'timelineScene_1', t2) 151 | .transition(['timelineScene_3'], ['timelineScene_2', 'timelineScene_4'], t3) 152 | 153 | n3.timeline.switchScene('timelineScene_2') 154 | n3.timeline.switchScene('timelineScene_4') 155 | n3.timeline.switchScene('timelineScene_3') 156 | n3.timeline.switchScene('timelineScene_1') 157 | 158 | n3.timeline.switchScene('timelineScene_3') 159 | n3.timeline.switchScene('timelineScene_2') 160 | n3.timeline.switchScene('timelineScene_1') 161 | 162 | n3.timeline.switchScene('timelineScene_3') 163 | n3.timeline.switchScene('timelineScene_4') 164 | n3.timeline.switchScene('timelineScene_2') 165 | 166 | n3.timeline.switchScene('timelineScene_3') 167 | n3.timeline.switchScene('timelineScene_1') -------------------------------------------------------------------------------- /test/trigger.test.coffee: -------------------------------------------------------------------------------- 1 | describe "trigger", -> 2 | vis = null 3 | 4 | beforeEach -> 5 | vis = n3.vis('triggerTest') 6 | .state('state_1', ['valid', 'value']) 7 | .state('state_2', [1, 2, 3, 4, 5, 6]) 8 | .render(-> 9 | console.log('triggerTest render'); 10 | ) 11 | 12 | it "is equal", -> 13 | t = n3.trigger(vis) 14 | .where('state_1') 15 | .is('value') 16 | 17 | expect(t.evaluate([vis.visId, 'state_1'], 'valid')).toBe false 18 | expect(t.evaluate([vis.visId, 'state_1'], 'value')).toBe true 19 | 20 | it "is not", -> 21 | t = n3.trigger(vis) 22 | .where('state_1') 23 | .not('value') 24 | 25 | expect(t.evaluate([vis.visId, 'state_1'], 'valid')).toBe true 26 | expect(t.evaluate([vis.visId, 'state_1'], 'value')).toBe false 27 | 28 | it "is greater than", -> 29 | t = n3.trigger(vis) 30 | .where('state_2') 31 | .gt(3) 32 | 33 | expect(t.evaluate([vis.visId, 'state_2'], 1)).toBe false 34 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe false 35 | expect(t.evaluate([vis.visId, 'state_2'], 5)).toBe true 36 | 37 | it "is greater than or equal to", -> 38 | t = n3.trigger(vis) 39 | .where('state_2') 40 | .gte(3) 41 | 42 | expect(t.evaluate([vis.visId, 'state_2'], 1)).toBe false 43 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe true 44 | expect(t.evaluate([vis.visId, 'state_2'], 5)).toBe true 45 | 46 | it "is less than", -> 47 | t = n3.trigger(vis) 48 | .where('state_2') 49 | .lt(3) 50 | 51 | expect(t.evaluate([vis.visId, 'state_2'], 1)).toBe true 52 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe false 53 | expect(t.evaluate([vis.visId, 'state_2'], 5)).toBe false 54 | 55 | it "is less than", -> 56 | t = n3.trigger(vis) 57 | .where('state_2') 58 | .lte(3) 59 | 60 | expect(t.evaluate([vis.visId, 'state_2'], 1)).toBe true 61 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe true 62 | expect(t.evaluate([vis.visId, 'state_2'], 5)).toBe false 63 | 64 | it "is and trigger", -> 65 | t = n3.trigger.and( 66 | n3.trigger(vis) 67 | .where('state_1') 68 | .is('value'), 69 | 70 | n3.trigger(vis) 71 | .where('state_2') 72 | .is(3) 73 | ) 74 | 75 | vis.state('state_1', 'valid') 76 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe false 77 | expect(t.evaluate([vis.visId, 'state_1'], 'value')).toBe false 78 | 79 | vis.state('state_1', 'value') 80 | expect(t.evaluate([vis.visId, 'state_2'], 4)).toBe false 81 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe true 82 | 83 | t1 = n3.trigger.and( 84 | n3.trigger(vis) 85 | .where('state_1') 86 | .is('value'), 87 | 88 | n3.trigger(vis) 89 | .where('state_2') 90 | .gt(3) 91 | ) 92 | 93 | vis.state('state_1', 'valid') 94 | vis.state('state_2', 1) 95 | expect(t1.evaluate([vis.visId, 'state_1'], 'value')).toBe false 96 | expect(t1.evaluate([vis.visId, 'state_2'], 4)).toBe false 97 | 98 | vis.state('state_2', 5) 99 | expect(t1.evaluate([vis.visId, 'state_1'], 'valid')).toBe false 100 | expect(t1.evaluate([vis.visId, 'state_1'], 'value')).toBe true 101 | 102 | it "is or", -> 103 | t = n3.trigger.or( 104 | n3.trigger(vis) 105 | .where('state_1') 106 | .is('value'), 107 | 108 | n3.trigger(vis) 109 | .where('state_2') 110 | .is(3) 111 | ) 112 | 113 | expect(t.evaluate([vis.visId, 'state_1'], 'valid')).toBe false 114 | expect(t.evaluate([vis.visId, 'state_1'], 'value')).toBe true 115 | expect(t.evaluate([vis.visId, 'state_2'], 5)).toBe false 116 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe true 117 | 118 | t = n3.trigger.or( 119 | n3.trigger(vis) 120 | .where('state_1') 121 | .is(5), 122 | 123 | n3.trigger(vis) 124 | .where('state_2') 125 | .lt(3) 126 | ) 127 | 128 | expect(t.evaluate([vis.visId, 'state_1'], 5)).toBe true 129 | expect(t.evaluate([vis.visId, 'state_2'], 4)).toBe false 130 | expect(t.evaluate([vis.visId, 'state_2'], 5)).toBe false 131 | expect(t.evaluate([vis.visId, 'state_2'], 3)).toBe false 132 | expect(t.evaluate([vis.visId, 'state_2'], 2)).toBe true 133 | 134 | # it "registers and is notified by state change", -> 135 | # t = n3.trigger(vis) 136 | # .where('state_1') 137 | # .is('value') 138 | # 139 | # t1 = n3.trigger(vis) 140 | # .where('state_2') 141 | # .lte(3) 142 | # 143 | # n3.trigger.register(t) 144 | # n3.trigger.register(t1) 145 | # 146 | # 147 | # spyOn(t, 'evaluate') 148 | # spyOn(t1, 'evaluate') 149 | # 150 | # vis.state('state_1', 'valid') 151 | # expect(t.evaluate).toHaveBeenCalledWith('valid') 152 | -------------------------------------------------------------------------------- /test/vis.test.coffee: -------------------------------------------------------------------------------- 1 | describe "vis", -> 2 | vis = null 3 | vis2 = null 4 | 5 | beforeEach -> 6 | vis = n3.vis('visTest') 7 | vis2 = n3.vis('visTest2') 8 | 9 | it "sets/gets the stage", -> 10 | stage = d3.select('body') 11 | .append('div') 12 | .attr('id', 'stage') 13 | 14 | # Testing if .stage() sets all properties and is chainable 15 | expect(vis.stage('#stage', 100, 200).width()).toBe 100 16 | expect(vis.height()).toBe 200 17 | expect(vis.stage()).toEqual d3.select('#stage') 18 | 19 | # Tests .width() setter/getter/chaining 20 | expect(vis.width(150).height()).toBe 200 21 | expect(vis.width()).toBe 150 22 | 23 | # Tests .height() setter/getter/chaining 24 | expect(vis.height(250).width()).toBe 150 25 | expect(vis.height()).toBe 250 26 | 27 | # Tests that the stage has not been set for any other vis 28 | expect(vis2.width()).toBeUndefined 29 | 30 | it "sets/gets readOnly consts", -> 31 | # Testing if .const() sets and is chainable 32 | expect(vis.const('foo', 'bar').const('foo')).toBe 'bar' 33 | 34 | # Testing to ensure consts are readOnly 35 | expect(vis.const('foo', 'baz').const('foo')).toBe 'bar' 36 | 37 | # Testing setting multiple consts 38 | expect(vis.const('hello', 'world').const('top', 'cat').const('foo')).toBe 'bar' 39 | expect(vis.const('hello')).toBe 'world' 40 | expect(vis.const('top')).toBe 'cat' 41 | 42 | # Testing to ensure consts not set on any other vis 43 | expect(vis2.const('foo')).toBeUndefined 44 | expect(vis2.const('hello')).toBeUndefined 45 | expect(vis2.const('top')).toBeUndefined 46 | 47 | it "sets/gets the data", -> 48 | d = ["do", "re", "mi", "fa", "so"] 49 | 50 | # Testing if .data() sets and is chainable 51 | expect(vis.data(d).data()).toBe d 52 | 53 | # Testing if .data() supports different data types 54 | d1 = {Jagger: "Rock", Elvis: "Roll"} 55 | expect(vis.data(d1).data()).toBe d1 56 | 57 | d2 = 58 | brother: 59 | name: "Max" 60 | age: 11 61 | sister: 62 | name: "Ida" 63 | age: 9 64 | 65 | expect(vis.data(d2).data()).toBe d2 66 | 67 | # Tests that .data is not set for another vis 68 | expect(vis2.data()).toBeUndefined 69 | 70 | it "sets/gets the state", -> 71 | vis.state('state1', ['va1', 'va2']) 72 | .state('state2', ['foo', 'bar', 'hello', 'world']) 73 | .bind('state1', (val) -> console.log("state1 = #{val}")) 74 | .bind('state2', (val) -> console.log("state2 = #{val}")) 75 | .render(-> 76 | console.log('hello'); 77 | ) 78 | 79 | expect(vis.states['state1'].visId).toBe 'visTest' 80 | expect(vis.states['state1'].validValues).toEqual ['va1', 'va2'] 81 | expect(vis.states['state2'].validValues).toEqual ['foo', 'bar', 'hello', 'world'] 82 | 83 | spyOn(vis, 'renderFn') 84 | 85 | vis.states['state1'].set('va1') 86 | expect(vis.renderFn).toHaveBeenCalled 87 | 88 | vis.state('state2', 'foo') 89 | expect(vis.renderFn).toHaveBeenCalled 90 | -------------------------------------------------------------------------------- /ui/css/colorpicker.css: -------------------------------------------------------------------------------- 1 | .colorpicker { 2 | width: 356px; 3 | height: 176px; 4 | overflow: hidden; 5 | position: absolute; 6 | background: url(../imgs/colorpicker_background.png); 7 | font-family: Arial, Helvetica, sans-serif; 8 | display: none; 9 | z-index: 10000; 10 | } 11 | .colorpicker_color { 12 | width: 150px; 13 | height: 150px; 14 | left: 14px; 15 | top: 13px; 16 | position: absolute; 17 | background: #f00; 18 | overflow: hidden; 19 | cursor: crosshair; 20 | } 21 | .colorpicker_color div { 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | width: 150px; 26 | height: 150px; 27 | background: url(../imgs/colorpicker_overlay.png); 28 | } 29 | .colorpicker_color div div { 30 | position: absolute; 31 | top: 0; 32 | left: 0; 33 | width: 11px; 34 | height: 11px; 35 | overflow: hidden; 36 | background: url(../imgs/colorpicker_select.gif); 37 | margin: -5px 0 0 -5px; 38 | } 39 | .colorpicker_hue { 40 | position: absolute; 41 | top: 13px; 42 | left: 171px; 43 | width: 35px; 44 | height: 150px; 45 | cursor: n-resize; 46 | } 47 | .colorpicker_hue div { 48 | position: absolute; 49 | width: 35px; 50 | height: 9px; 51 | overflow: hidden; 52 | background: url(../imgs/colorpicker_indic.gif) left top; 53 | margin: -4px 0 0 0; 54 | left: 0px; 55 | } 56 | .colorpicker_new_color { 57 | position: absolute; 58 | width: 60px; 59 | height: 30px; 60 | left: 213px; 61 | top: 13px; 62 | background: #f00; 63 | } 64 | .colorpicker_current_color { 65 | position: absolute; 66 | width: 60px; 67 | height: 30px; 68 | left: 283px; 69 | top: 13px; 70 | background: #f00; 71 | } 72 | .colorpicker input { 73 | background-color: transparent; 74 | border: 1px solid transparent; 75 | position: absolute; 76 | font-size: 10px; 77 | font-family: Arial, Helvetica, sans-serif; 78 | color: #898989; 79 | top: 4px; 80 | right: 11px; 81 | text-align: right; 82 | margin: 0; 83 | padding: 0; 84 | height: 11px; 85 | } 86 | .colorpicker_hex { 87 | position: absolute; 88 | width: 72px; 89 | height: 22px; 90 | background: url(../imgs/colorpicker_hex.png) top; 91 | left: 212px; 92 | top: 142px; 93 | } 94 | .colorpicker_hex input { 95 | right: 6px; 96 | } 97 | .colorpicker_field { 98 | height: 22px; 99 | width: 62px; 100 | background-position: top; 101 | position: absolute; 102 | } 103 | .colorpicker_field span { 104 | position: absolute; 105 | width: 12px; 106 | height: 22px; 107 | overflow: hidden; 108 | top: 0; 109 | right: 0; 110 | cursor: n-resize; 111 | } 112 | .colorpicker_rgb_r { 113 | background-image: url(../imgs/colorpicker_rgb_r.png); 114 | top: 52px; 115 | left: 212px; 116 | } 117 | .colorpicker_rgb_g { 118 | background-image: url(../imgs/colorpicker_rgb_g.png); 119 | top: 82px; 120 | left: 212px; 121 | } 122 | .colorpicker_rgb_b { 123 | background-image: url(../imgs/colorpicker_rgb_b.png); 124 | top: 112px; 125 | left: 212px; 126 | } 127 | .colorpicker_hsb_h { 128 | background-image: url(../imgs/colorpicker_hsb_h.png); 129 | top: 52px; 130 | left: 282px; 131 | } 132 | .colorpicker_hsb_s { 133 | background-image: url(../imgs/colorpicker_hsb_s.png); 134 | top: 82px; 135 | left: 282px; 136 | } 137 | .colorpicker_hsb_b { 138 | background-image: url(../imgs/colorpicker_hsb_b.png); 139 | top: 112px; 140 | left: 282px; 141 | } 142 | .colorpicker_submit { 143 | position: absolute; 144 | width: 22px; 145 | height: 22px; 146 | background: url(../imgs/colorpicker_submit.png) top; 147 | left: 322px; 148 | top: 142px; 149 | overflow: hidden; 150 | } 151 | .colorpicker_focus { 152 | background-position: center; 153 | } 154 | .colorpicker_hex.colorpicker_focus { 155 | background-position: bottom; 156 | } 157 | .colorpicker_submit.colorpicker_focus { 158 | background-position: bottom; 159 | } 160 | .colorpicker_slider { 161 | background-position: bottom; 162 | } 163 | -------------------------------------------------------------------------------- /ui/css/n3-edit.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Verdana, sans-serif; 3 | font-size: 12px; 4 | } 5 | 6 | #n3-ui_visDialog textarea, #n3-ui_exportDialog textarea { 7 | width: 450px; 8 | height: 200px; 9 | font-family: monospace; 10 | } 11 | 12 | .n3-vis_stage { 13 | float: left; 14 | margin: 15px; 15 | background: #fff; 16 | border: 1px solid #333; 17 | } 18 | 19 | #n3-ui_stage { 20 | position: fixed; 21 | top: 0px; 22 | left: 0; 23 | height: 90%; 24 | overflow: auto; 25 | } 26 | 27 | #n3-ui_stage div.hover { outline: 3px solid firebrick; } 28 | 29 | #n3-ui_stage svg { padding: 0px 10px; } 30 | 31 | #n3-ui_stage .draw svg { cursor: crosshair; } 32 | 33 | #n3-ui_stage svg .hover { 34 | stroke: firebrick; 35 | stroke-width: 3; 36 | } 37 | 38 | p.hover { outline: 2px solid firebrick; } 39 | 40 | #n3-ui_stage .infobar, #n3-ui_widget_panel .header { 41 | clear: both; 42 | display: block; 43 | background: #333; 44 | color: #fff; 45 | padding: 5px 10px; 46 | } 47 | 48 | #n3-ui_stage .infobar p { 49 | float: left; 50 | margin: 5px 20px 5px 0; 51 | background: #333; 52 | } 53 | 54 | #n3-ui_bottom_panel { 55 | position: fixed; 56 | bottom: 0; 57 | left: 0; 58 | width: 100%; 59 | padding: 5px; 60 | background: black url(../imgs/ui-bg_inset-soft_25_000000_1x100.png) 50% bottom repeat-x 61 | } 62 | 63 | #n3-ui_bottom_panel .button { 64 | float: right; 65 | margin-right: 15px; 66 | background-color: #444; 67 | background-repeat: no-repeat; 68 | background-position: 5px 50%; 69 | border: 1px solid #ddd; 70 | height: 40px; 71 | padding: 5px 5px 5px 45px; 72 | color: #fff; 73 | cursor: pointer; 74 | } 75 | 76 | #n3-ui_bottom_panel .button-left { float: left !important; } 77 | #n3-ui_bottom_panel .button:hover { background-color: #555; } 78 | 79 | #n3-ui_bottom_panel .new { background-image: url(../imgs/add-large.png);} 80 | #n3-ui_bottom_panel .end { background-image: url(../imgs/accept.png);} 81 | #n3-ui_bottom_panel .play { background-image: url(../imgs/play.png); } 82 | #n3-ui_bottom_panel .export { background-image: url(../imgs/export.png); } 83 | #n3-ui_bottom_panel .widget { background-image: url(../imgs/widget.png); } 84 | 85 | #n3-ui_palette a { 86 | float: left; 87 | margin-left: 30px; 88 | padding: 0 5px; 89 | width: 32px; 90 | height: 40px; 91 | text-indent: -999em; 92 | background-repeat: no-repeat; 93 | background-position: 50% 50%; 94 | } 95 | 96 | #n3-ui_palette a.selected { background-color: #eaf2ff; } 97 | #n3-ui_palette a:hover { opacity: 0.5; } 98 | 99 | #n3-ui_palette a.circle { background-image: url(../imgs/draw-circle.png); } 100 | #n3-ui_palette a.rect { background-image: url(../imgs/draw-rect.png); } 101 | #n3-ui_palette a.line { background-image: url(../imgs/draw-line.png); } 102 | #n3-ui_palette a.text { background-image: url(../imgs/draw-text.png); } 103 | 104 | #n3-ui_widget_panel { 105 | position: fixed; 106 | top: 15px; 107 | right: 330px; 108 | width: 250px; 109 | height: 90%; 110 | overflow: auto; 111 | } 112 | 113 | #n3-ui_widget_panel .widgets { 114 | border: 1px solid #333; 115 | padding: 5px; 116 | font-weight: normal !important; 117 | } 118 | 119 | #n3-ui_side_panel { 120 | position: fixed; 121 | top: 15px; 122 | right: 15px; 123 | width: 300px; 124 | height: 90%; 125 | overflow: auto; 126 | } 127 | 128 | #n3-ui_side_panel .scene, #n3-ui_widget_panel .widgets { 129 | margin-bottom: 10px; 130 | background-color: #ddd; 131 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ddd), to(#bbb)); 132 | background-image: -webkit-linear-gradient(top, #ddd, #bbb); 133 | background-image: -moz-linear-gradient(top, #ddd, #bbb); 134 | background-image: -ms-linear-gradient(top, #ddd, #bbb); 135 | background-image: -o-linear-gradient(top, #ddd, #bbb); 136 | background-image: linear-gradient(to bottom, #ddd, #bbb); 137 | font-weight:bold; 138 | color:#333; 139 | border-radius: 0px !important; 140 | } 141 | 142 | #n3-ui_side_panel .scene-header { 143 | background-image: none !important; 144 | padding: 5px; 145 | cursor: move; 146 | border-radius: 0px !important; 147 | color: #fff; 148 | } 149 | 150 | /*.member { 151 | width: 275px; 152 | margin: 0 auto 5px auto; 153 | } 154 | .member-header, .member-content { padding: 5px; }*/ 155 | #n3-ui_side_panel .members { padding-left: 0px !important; } 156 | 157 | #n3-ui_side_panel .member { 158 | list-style-type: none !important; 159 | background: none !important; 160 | color: #333 !important; 161 | border: 0 !important; 162 | height: 32px !important; 163 | margin-bottom: 15px !important; 164 | padding: 5px !important; 165 | } 166 | 167 | #n3-ui_side_panel .members .hover { background: #ddd !important; } 168 | 169 | .ui-icon-edit, .ui-icon-plusthick, .ui-icon-minusthick { 170 | float: right; 171 | margin-left: 15px; 172 | cursor: pointer; 173 | } 174 | 175 | .ui-icon-draggable, .ui-icon-trigger, .ui-icon-style, .ui-icon-delete { 176 | display: inline-block !important; 177 | float: left !important; 178 | height: 32px !important; 179 | margin-right: 15px !important; 180 | cursor: pointer !important; 181 | } 182 | 183 | .ui-icon-draggable { 184 | background: url(../imgs/drag-handle.png) no-repeat 0px 7px !important; 185 | cursor: move !important; 186 | } 187 | 188 | .ui-icon-edit { background: url(../imgs/edit.png) no-repeat !important; } 189 | .ui-icon-trigger { background: url(../imgs/trigger.png) no-repeat 0px 7px !important; } 190 | .ui-icon-trigger-empty { background: url(../imgs/trigger-empty.png) no-repeat 0px 7px !important; } 191 | .ui-icon-style { background: url(../imgs/styles.png) no-repeat 0px 7px !important; } 192 | .ui-icon-delete { background: url(../imgs/delete.png) no-repeat 0px 7px !important; } 193 | 194 | /*.state .member-text { background: url(../imgs/state.png) no-repeat 0px 10px !important; }*/ 195 | .state .ui-icon-style { background-image: none !important; } 196 | 197 | #n3-ui_side_panel .member-text { 198 | display: inline-block; 199 | float: left; 200 | width: 160px; 201 | height: 30px; 202 | overflow: hidden; 203 | padding-left: 4px; 204 | } 205 | 206 | #n3-ui_side_panel .transitions { 207 | border-top: 1px dashed #666; 208 | padding: 10px 0 0 40px; 209 | } 210 | 211 | #n3-ui_side_panel .transitions .member-text { margin-top: 5px; } 212 | 213 | #n3-ui_triggerDialog { overflow: auto; } 214 | 215 | #n3-ui_triggerDialog textarea { 216 | width: 100%; 217 | height: 100px; 218 | font-family: monospace; 219 | } 220 | 221 | #n3-ui_triggerDialog li { 222 | list-style-type: none; 223 | border-top: 1px solid #444; 224 | } 225 | 226 | #n3-ui_triggerDialog li:nth-child(2) { border-top: 0; } 227 | 228 | #n3-ui_triggerDialog .add_sub_trigger { 229 | text-indent: -999em; 230 | display: inline-block; 231 | margin-left: 20px; 232 | width: 16px; 233 | height: 16px; 234 | background: url(../imgs/add.png) no-repeat; 235 | } 236 | 237 | #n3-ui_triggerDialog .action { border-bottom: 1px solid #666; } 238 | 239 | #n3-ui_newTransition { margin-top: 30px } 240 | #n3-ui_newTransition a { 241 | margin-top: 10px; 242 | padding: 5px 5px 5px 20px; 243 | border: 1px solid #666; 244 | background: url('../imgs/add.png') 0px 4px no-repeat #333; 245 | text-decoration: none; 246 | } 247 | 248 | #n3-ui_stylesDialog p { margin-bottom: 20px; } 249 | 250 | .style_desc { 251 | display: inline-block; 252 | width: 150px; 253 | margin-right: 5px; 254 | } 255 | 256 | #fill_opacity, #stroke_width { 257 | display: inline-block; 258 | width: 150px; 259 | margin-right: 20px; 260 | } 261 | 262 | #fill_color, #stroke_color { 263 | display: inline-block; 264 | width: 16px; 265 | height: 16px; 266 | border: 1px solid #fff; 267 | cursor: pointer; 268 | } 269 | 270 | p.editable:hover { outline: 1px dotted #CCC; } 271 | 272 | #n3-ui_stylesDialog table, #n3-ui_stylesDialog th, #n3-ui_stylesDialog td { 273 | border: 1px solid #444; 274 | border-collapse: collapse; 275 | } 276 | 277 | #n3-ui_stylesDialog th { 278 | padding: 5px; 279 | background: #444; 280 | border: 1px solid #666; 281 | color: #fff; 282 | } 283 | 284 | #n3-ui_stylesDialog td { padding: 5px; } 285 | 286 | #n3-ui_stylesDialog .data_tbl { 287 | max-width: 470px; 288 | max-height: 200px; 289 | overflow: auto; 290 | } 291 | 292 | #n3-ui_stylesDialog .data_tbl tr:nth-child(odd) { background-color: #222; } 293 | #n3-ui_stylesDialog .data_tbl tr:nth-child(even) { background-color: #000; } 294 | #n3-ui_stylesDialog .data_tbl tr.selected { background-color: #a77214; } 295 | 296 | #n3-ui_stylesDialog input[type=number] { width: 50px; } 297 | #n3-ui_stylesDialog input[type=range] { 298 | -webkit-appearance:none; 299 | -moz-apperance:none; 300 | height:4px; 301 | border-radius: 10px; 302 | } 303 | 304 | #n3-ui_stylesDialog a.bind { 305 | text-indent: -999em; 306 | background: url(../imgs/bind_data.png) no-repeat; 307 | display: inline-block; 308 | width: 16px; 309 | height: 16px; 310 | opacity: 0.4; 311 | } 312 | 313 | #n3-ui_stylesDialog a.bound { opacity: 1; } 314 | 315 | .prop_label { 316 | display: inline-block; 317 | width: 75px; 318 | } -------------------------------------------------------------------------------- /ui/css/n3-play.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #dcfad0; 3 | font-family: Verdana, sans-serif; 4 | font-size: 12px; 5 | } 6 | 7 | .n3-vis_stage { 8 | float: left; 9 | margin: 15px; 10 | background: #fff; 11 | border: 1px solid #ccc; 12 | } 13 | 14 | #n3-ui_stage { 15 | position: fixed; 16 | top: 0px; 17 | left: 0; 18 | height: 90%; 19 | overflow: scroll; 20 | } 21 | 22 | #n3-ui_stage svg { padding: 0px 10px; } 23 | 24 | #n3-ui_widgets { 25 | position: fixed; 26 | top: 0px; 27 | right: 0px; 28 | width: 200px; 29 | } 30 | 31 | #n3-ui_playbar { 32 | position: fixed; 33 | bottom: 0; 34 | left: 0; 35 | width: 100%; 36 | color: #fff; 37 | background: #333; 38 | } 39 | 40 | #n3-ui_playbar ul { 41 | margin: 0; 42 | list-style-type: none; 43 | } 44 | 45 | #n3-ui_playbar li a { 46 | float: left; 47 | padding: 5px 20px; 48 | border-right: 1px solid #dcfad0; 49 | color: #fff; 50 | text-decoration: none; 51 | font-size: 13px; 52 | } 53 | 54 | #n3-ui_playbar a:hover, #n3-ui_playbar a.selected { 55 | background: #dcfad0; 56 | color: #333; 57 | } -------------------------------------------------------------------------------- /ui/imgs/accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/accept.png -------------------------------------------------------------------------------- /ui/imgs/add-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/add-large.png -------------------------------------------------------------------------------- /ui/imgs/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/add.png -------------------------------------------------------------------------------- /ui/imgs/bind_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/bind_data.png -------------------------------------------------------------------------------- /ui/imgs/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/blank.gif -------------------------------------------------------------------------------- /ui/imgs/colorpicker_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_background.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_hex.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_hsb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_hsb_b.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_hsb_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_hsb_h.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_hsb_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_hsb_s.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_indic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_indic.gif -------------------------------------------------------------------------------- /ui/imgs/colorpicker_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_overlay.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_rgb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_rgb_b.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_rgb_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_rgb_g.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_rgb_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_rgb_r.png -------------------------------------------------------------------------------- /ui/imgs/colorpicker_select.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_select.gif -------------------------------------------------------------------------------- /ui/imgs/colorpicker_submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/colorpicker_submit.png -------------------------------------------------------------------------------- /ui/imgs/custom_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_background.png -------------------------------------------------------------------------------- /ui/imgs/custom_hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_hex.png -------------------------------------------------------------------------------- /ui/imgs/custom_hsb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_hsb_b.png -------------------------------------------------------------------------------- /ui/imgs/custom_hsb_h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_hsb_h.png -------------------------------------------------------------------------------- /ui/imgs/custom_hsb_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_hsb_s.png -------------------------------------------------------------------------------- /ui/imgs/custom_indic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_indic.gif -------------------------------------------------------------------------------- /ui/imgs/custom_rgb_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_rgb_b.png -------------------------------------------------------------------------------- /ui/imgs/custom_rgb_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_rgb_g.png -------------------------------------------------------------------------------- /ui/imgs/custom_rgb_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_rgb_r.png -------------------------------------------------------------------------------- /ui/imgs/custom_submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/custom_submit.png -------------------------------------------------------------------------------- /ui/imgs/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/delete.png -------------------------------------------------------------------------------- /ui/imgs/drag-handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/drag-handle.png -------------------------------------------------------------------------------- /ui/imgs/draw-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/draw-circle.png -------------------------------------------------------------------------------- /ui/imgs/draw-ellipse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/draw-ellipse.png -------------------------------------------------------------------------------- /ui/imgs/draw-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/draw-line.png -------------------------------------------------------------------------------- /ui/imgs/draw-rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/draw-rect.png -------------------------------------------------------------------------------- /ui/imgs/draw-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/draw-text.png -------------------------------------------------------------------------------- /ui/imgs/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/edit.png -------------------------------------------------------------------------------- /ui/imgs/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/export.png -------------------------------------------------------------------------------- /ui/imgs/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/play.png -------------------------------------------------------------------------------- /ui/imgs/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/select.png -------------------------------------------------------------------------------- /ui/imgs/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/select2.png -------------------------------------------------------------------------------- /ui/imgs/slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/slider.png -------------------------------------------------------------------------------- /ui/imgs/state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/state.png -------------------------------------------------------------------------------- /ui/imgs/styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/styles.png -------------------------------------------------------------------------------- /ui/imgs/trigger-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/trigger-empty.png -------------------------------------------------------------------------------- /ui/imgs/trigger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/trigger.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_diagonals-thick_18_b81900_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_diagonals-thick_18_b81900_40x40.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_diagonals-thick_20_666666_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_diagonals-thick_20_666666_40x40.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_flat_10_000000_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_flat_10_000000_40x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_flat_30_cccccc_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_flat_30_cccccc_40x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_flat_50_5c5c5c_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_flat_50_5c5c5c_40x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_100_f6f6f6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_100_f6f6f6_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_100_fdf5ce_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_100_fdf5ce_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_20_555555_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_20_555555_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_40_0078a3_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_40_0078a3_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_40_ffc73d_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_40_ffc73d_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_gloss-wave_25_333333_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_gloss-wave_25_333333_500x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_gloss-wave_35_f6a828_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_gloss-wave_35_f6a828_500x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_highlight-soft_100_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_highlight-soft_100_eeeeee_1x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_highlight-soft_75_ffe45c_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_highlight-soft_75_ffe45c_1x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_highlight-soft_80_eeeeee_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_highlight-soft_80_eeeeee_1x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_inset-soft_25_000000_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_inset-soft_25_000000_1x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-bg_inset-soft_30_f58400_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-bg_inset-soft_30_f58400_1x100.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_228ef1_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_228ef1_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_4b8e0b_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_4b8e0b_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_a83300_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_a83300_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_cccccc_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_cccccc_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_ef8c08_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_ef8c08_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_ffd27a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_ffd27a_256x240.png -------------------------------------------------------------------------------- /ui/imgs/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /ui/imgs/widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uwdata/ellipsis/288599be9b582924e374f76f6569aaa02e6f592e/ui/imgs/widget.png -------------------------------------------------------------------------------- /ui/js/d3.min.js: -------------------------------------------------------------------------------- 1 | ../../node_modules/d3/d3.v2.min.js -------------------------------------------------------------------------------- /ui/js/jquery.chromatable.js: -------------------------------------------------------------------------------- 1 | /* 2 | * File: chromatable.js 3 | * Version: 1.3.0 4 | * CVS: $Id$ 5 | * Description: Make a "sticky" header at the top of the table, so it stays put while the table scrolls 6 | * Author: Zachary Siswick 7 | * Created: Thursday 19 November 2009 8:53pm 8 | * Language: Javascript 9 | * 10 | */ 11 | (function($){ 12 | 13 | $.chromatable = { 14 | // Default options 15 | defaults: { 16 | //specify a pixel dimension, auto, or 100% 17 | width: "900px", 18 | height: "300px", 19 | scrolling: "yes" 20 | } 21 | 22 | }; 23 | 24 | $.fn.chromatable = function(options){ 25 | 26 | // Extend default options 27 | var options = $.extend({}, $.chromatable.defaults, options); 28 | 29 | return this.each(function(){ 30 | 31 | // Add jQuery methods to the element 32 | var $this = $(this); 33 | var $uniqueID = $(this).attr("ID") + ("wrapper"); 34 | 35 | 36 | //Add dimentsions from user or default parameters to the DOM elements 37 | $(this).css('width', options.width).addClass("_scrolling"); 38 | 39 | $(this).wrap('
'); 40 | 41 | $(".scrolling_outer").css({'position':'relative'}); 42 | $("#"+$uniqueID).css( 43 | 44 | {'border':'1px solid #CCCCCC', 45 | 'overflow-x':'hidden', 46 | 'overflow-y':'auto', 47 | 'padding-right':'17px' 48 | }); 49 | 50 | $("#"+$uniqueID).css('height', options.height); 51 | $("#"+$uniqueID).css('width', options.width); 52 | 53 | // clone an exact copy of the scrolling table and add to DOM before the original table 54 | // replace old class with new to differentiate between the two 55 | $(this).before($(this).clone().attr("id", "").addClass("_thead").css( 56 | 57 | {'width' : 'auto', 58 | 'display' : 'block', 59 | 'position':'absolute', 60 | 'border':'none', 61 | 'border-bottom':'1px solid #CCC', 62 | 'top':'1px' 63 | })); 64 | 65 | 66 | // remove all children within the cloned table after the thead element 67 | $('._thead').children('tbody').remove(); 68 | 69 | 70 | $(this).each(function( $this ){ 71 | 72 | // if the width is auto, we need to remove padding-right on scrolling container 73 | 74 | if (options.width == "100%" || options.width == "auto") { 75 | 76 | $("#"+$uniqueID).css({'padding-right':'0px'}); 77 | } 78 | 79 | 80 | if (options.scrolling == "no") { 81 | 82 | $("#"+$uniqueID).before('Expand table'); 83 | 84 | $("#"+$uniqueID).css({'padding-right':'0px'}); 85 | 86 | $(".expander").each( 87 | 88 | 89 | function(int){ 90 | 91 | $(this).attr("ID", int); 92 | 93 | $( this ).bind ("click",function(){ 94 | 95 | $("#"+$uniqueID).css({'height':'auto'}); 96 | 97 | $("#"+$uniqueID+" ._thead").remove(); 98 | 99 | $(this).remove(); 100 | 101 | }); 102 | }); 103 | 104 | 105 | //this is dependant on the jQuery resizable UI plugin 106 | $("#"+$uniqueID).resizable({ handles: 's' }).css("overflow-y", "hidden"); 107 | 108 | } 109 | 110 | }); 111 | 112 | 113 | // Get a relative reference to the "sticky header" 114 | $curr = $this.prev(); 115 | 116 | // Copy the cell widths across from the original table 117 | $("thead:eq(0)>tr th",this).each( function (i) { 118 | 119 | $("thead:eq(0)>tr th:eq("+i+")", $curr).width( $(this).width()); 120 | 121 | }); 122 | 123 | 124 | //check to see if the width is set to auto, if not, we don't need to call the resizer function 125 | if (options.width == "100%" || "auto"){ 126 | 127 | 128 | // call the resizer function whenever the table width has been adjusted 129 | $(window).resize(function(){ 130 | 131 | resizer($this); 132 | }); 133 | } 134 | }); 135 | 136 | }; 137 | 138 | // private function to temporarily hide the header when the browser is resized 139 | 140 | function resizer($this) { 141 | 142 | // Need a relative reference to the "sticky header" 143 | $curr = $this.prev(); 144 | 145 | $("thead:eq(0)>tr th", $this).each( function (i) { 146 | 147 | $("thead:eq(0)>tr th:eq("+i+")", $curr).width( $(this).width()); 148 | 149 | }); 150 | 151 | }; 152 | 153 | })(jQuery); -------------------------------------------------------------------------------- /ui/js/jquery.getPath.js: -------------------------------------------------------------------------------- 1 | jQuery.fn.getPath = function () { 2 | if (this.length != 1) throw 'Requires one element.'; 3 | 4 | var path, node = this; 5 | 6 | if(node.attr('id')) 7 | return '#' + node.attr('id'); 8 | 9 | while (node.length) { 10 | var realNode = node[0], name = realNode.localName; 11 | if (!name) break; 12 | name = name.toLowerCase(); 13 | // Only go up to the visualization's stage element though 14 | // (which definitely have an ID) 15 | // TODO: how to make this work w/non-svg stages? 16 | if(name == 'svg') 17 | return '#' + node.attr('id') + (path ? '>' + path : ''); 18 | 19 | var parent = node.parent(); 20 | 21 | var siblings = parent.children(name); 22 | if (siblings.length > 1) { 23 | name += ':eq(' + siblings.index(realNode) + ')'; 24 | } 25 | 26 | path = name + (path ? '>' + path : ''); 27 | node = parent; 28 | } 29 | 30 | return path; 31 | }; -------------------------------------------------------------------------------- /ui/js/jquery.json-2.3.min.js: -------------------------------------------------------------------------------- 1 | 2 | (function($){var escapeable=/["\\\x00-\x1f\x7f-\x9f]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};$.toJSON=typeof JSON==='object'&&JSON.stringify?JSON.stringify:function(o){if(o===null){return'null';} 3 | var type=typeof o;if(type==='undefined'){return undefined;} 4 | if(type==='number'||type==='boolean'){return''+o;} 5 | if(type==='string'){return $.quoteString(o);} 6 | if(type==='object'){if(typeof o.toJSON==='function'){return $.toJSON(o.toJSON());} 7 | if(o.constructor===Date){var month=o.getUTCMonth()+1,day=o.getUTCDate(),year=o.getUTCFullYear(),hours=o.getUTCHours(),minutes=o.getUTCMinutes(),seconds=o.getUTCSeconds(),milli=o.getUTCMilliseconds();if(month<10){month='0'+month;} 8 | if(day<10){day='0'+day;} 9 | if(hours<10){hours='0'+hours;} 10 | if(minutes<10){minutes='0'+minutes;} 11 | if(seconds<10){seconds='0'+seconds;} 12 | if(milli<100){milli='0'+milli;} 13 | if(milli<10){milli='0'+milli;} 14 | return'"'+year+'-'+month+'-'+day+'T'+ 15 | hours+':'+minutes+':'+seconds+'.'+milli+'Z"';} 16 | if(o.constructor===Array){var ret=[];for(var i=0;i' + 25 | ''); 27 | } 28 | } 29 | 30 | function populatePlayBar() { 31 | $('#n3-ui_playbar ul').html(''); 32 | 33 | for(var i in sceneOrder) { 34 | var s = sceneOrder[i]; 35 | $('#n3-ui_playbar ul').append('
  • ' + s + '
  • '); 36 | } 37 | } 38 | 39 | function switchScene(id) { 40 | $('.selected').removeClass('selected'); 41 | $('#n3-ui_' + id).addClass('selected'); 42 | 43 | n3.timeline.switchScene(id); 44 | } -------------------------------------------------------------------------------- /ui/js/n3.js: -------------------------------------------------------------------------------- 1 | ../../n3.js -------------------------------------------------------------------------------- /ui/play.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Author a Story with N3 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 54 | 55 | 56 |
    57 | 58 |
    59 | 60 |
    61 | 62 |
    63 | 64 |
    65 | 69 |
    70 | 71 | --------------------------------------------------------------------------------