├── html ├── scripts │ ├── rickshaw │ │ ├── .gitignore │ │ ├── examples │ │ │ ├── images │ │ │ │ ├── om_bar.png │ │ │ │ ├── om_step.png │ │ │ │ ├── om_curves.png │ │ │ │ ├── om_lines.png │ │ │ │ ├── om_stack.png │ │ │ │ ├── om_stream.png │ │ │ │ ├── interp_step.png │ │ │ │ ├── offset_pct.png │ │ │ │ ├── offset_stack.png │ │ │ │ ├── offset_value.png │ │ │ │ ├── om_percent.png │ │ │ │ ├── om_scatter.png │ │ │ │ ├── interp_linear.png │ │ │ │ ├── offset_stream.png │ │ │ │ └── interp_cardinal.png │ │ │ ├── screenshots │ │ │ │ ├── ajax.png │ │ │ │ ├── colors.png │ │ │ │ ├── lines.png │ │ │ │ ├── simple.png │ │ │ │ ├── start.png │ │ │ │ ├── status.png │ │ │ │ ├── stops.png │ │ │ │ ├── y_axis.png │ │ │ │ ├── extensions.png │ │ │ │ └── scatterplot.png │ │ │ ├── css │ │ │ │ ├── lines.css │ │ │ │ └── extensions.css │ │ │ ├── data │ │ │ │ ├── data.jsonp │ │ │ │ ├── data.json │ │ │ │ ├── data2.json │ │ │ │ └── status.json │ │ │ ├── start.html │ │ │ ├── simple.html │ │ │ ├── negative.html │ │ │ ├── scatterplot.html │ │ │ ├── ajax.html │ │ │ ├── refresh.html │ │ │ ├── bars.html │ │ │ ├── inconsistent.html │ │ │ ├── jsonp.html │ │ │ ├── gaps.html │ │ │ ├── y_axis.html │ │ │ ├── status.html │ │ │ ├── colors.html │ │ │ ├── stops.html │ │ │ ├── lines.html │ │ │ ├── formatter.html │ │ │ ├── fixed.html │ │ │ ├── hover.html │ │ │ ├── js │ │ │ │ └── extensions.js │ │ │ ├── index.html │ │ │ └── series.html │ │ ├── package.json │ │ ├── src │ │ │ ├── js │ │ │ │ ├── Rickshaw.Graph.JSONP.js │ │ │ │ ├── Rickshaw.Graph.Unstacker.js │ │ │ │ ├── Rickshaw.Graph.Renderer.Line.js │ │ │ │ ├── Rickshaw.Graph.Renderer.Stack.js │ │ │ │ ├── Rickshaw.js │ │ │ │ ├── Rickshaw.Fixtures.RandomData.js │ │ │ │ ├── Rickshaw.Graph.Behavior.Series.Order.js │ │ │ │ ├── Rickshaw.Fixtures.Number.js │ │ │ │ ├── Rickshaw.Graph.Renderer.ScatterPlot.js │ │ │ │ ├── Rickshaw.Graph.RangeSlider.js │ │ │ │ ├── Rickshaw.Graph.Smoother.js │ │ │ │ ├── Rickshaw.Graph.Legend.js │ │ │ │ ├── Rickshaw.Color.Palette.js │ │ │ │ ├── Rickshaw.Graph.Ajax.js │ │ │ │ ├── Rickshaw.Graph.Behavior.Series.Highlight.js │ │ │ │ ├── Rickshaw.Graph.Axis.Time.js │ │ │ │ ├── Rickshaw.Series.FixedDuration.js │ │ │ │ ├── Rickshaw.Graph.Renderer.Area.js │ │ │ │ ├── Rickshaw.Graph.Axis.Y.js │ │ │ │ ├── Rickshaw.Fixtures.Color.js │ │ │ │ ├── Rickshaw.Fixtures.Time.js │ │ │ │ ├── Rickshaw.Graph.Renderer.Bar.js │ │ │ │ ├── Rickshaw.Graph.Annotate.js │ │ │ │ ├── Rickshaw.Series.js │ │ │ │ ├── Rickshaw.Graph.Renderer.js │ │ │ │ ├── Rickshaw.Compat.ClassList.js │ │ │ │ ├── Rickshaw.Graph.Behavior.Series.Toggle.js │ │ │ │ ├── Rickshaw.Graph.HoverDetail.js │ │ │ │ └── Rickshaw.Graph.js │ │ │ └── css │ │ │ │ ├── legend.css │ │ │ │ ├── detail.css │ │ │ │ └── graph.css │ │ ├── tests │ │ │ ├── data │ │ │ │ └── simple.svg │ │ │ ├── Rickshaw.Class.js │ │ │ ├── Rickshaw.Series.FixedDuration.js │ │ │ ├── Rickshaw.Graph.Renderer.js │ │ │ ├── Rickshaw.Graph.js │ │ │ └── Rickshaw.Series.js │ │ ├── tutorial │ │ │ ├── transform.pl │ │ │ ├── example_01.html │ │ │ ├── transform_epoch.pl │ │ │ ├── example_02.html │ │ │ ├── vendor │ │ │ │ └── prettify │ │ │ │ │ └── prettify.css │ │ │ ├── transform_region.pl │ │ │ ├── example_03.html │ │ │ ├── example_04.html │ │ │ ├── style.css │ │ │ ├── example_05.html │ │ │ ├── example_06.html │ │ │ └── example_07.html │ │ ├── LICENSE │ │ ├── Makefile │ │ └── rickshaw.min.css │ ├── d3 │ │ └── LICENSE │ ├── jquery-tod.js │ ├── mapplayer │ │ ├── frame.js │ │ └── mapplayer.js │ └── main.js ├── images │ ├── tod │ │ ├── 0.png │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 9.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 16.png │ │ ├── 17.png │ │ ├── 18.png │ │ ├── 19.png │ │ ├── 20.png │ │ ├── 21.png │ │ ├── 22.png │ │ └── 23.png │ └── loading.gif └── img │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── README.md ├── clean_incidents.php ├── json_calls.php └── clean_calls.php /html/scripts/rickshaw/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *swp 3 | node_modules 4 | -------------------------------------------------------------------------------- /html/images/tod/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/0.png -------------------------------------------------------------------------------- /html/images/tod/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/1.png -------------------------------------------------------------------------------- /html/images/tod/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/2.png -------------------------------------------------------------------------------- /html/images/tod/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/3.png -------------------------------------------------------------------------------- /html/images/tod/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/4.png -------------------------------------------------------------------------------- /html/images/tod/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/5.png -------------------------------------------------------------------------------- /html/images/tod/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/6.png -------------------------------------------------------------------------------- /html/images/tod/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/7.png -------------------------------------------------------------------------------- /html/images/tod/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/8.png -------------------------------------------------------------------------------- /html/images/tod/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/9.png -------------------------------------------------------------------------------- /html/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/loading.gif -------------------------------------------------------------------------------- /html/images/tod/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/10.png -------------------------------------------------------------------------------- /html/images/tod/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/11.png -------------------------------------------------------------------------------- /html/images/tod/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/12.png -------------------------------------------------------------------------------- /html/images/tod/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/13.png -------------------------------------------------------------------------------- /html/images/tod/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/14.png -------------------------------------------------------------------------------- /html/images/tod/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/15.png -------------------------------------------------------------------------------- /html/images/tod/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/16.png -------------------------------------------------------------------------------- /html/images/tod/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/17.png -------------------------------------------------------------------------------- /html/images/tod/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/18.png -------------------------------------------------------------------------------- /html/images/tod/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/19.png -------------------------------------------------------------------------------- /html/images/tod/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/20.png -------------------------------------------------------------------------------- /html/images/tod/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/21.png -------------------------------------------------------------------------------- /html/images/tod/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/22.png -------------------------------------------------------------------------------- /html/images/tod/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/images/tod/23.png -------------------------------------------------------------------------------- /html/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /html/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_bar.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_step.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_curves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_curves.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_lines.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_stack.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_stream.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/ajax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/ajax.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/interp_step.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/interp_step.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/offset_pct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/offset_pct.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/offset_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/offset_stack.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/offset_value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/offset_value.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_percent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_percent.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/om_scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/om_scatter.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/colors.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/lines.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/simple.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/start.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/status.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/stops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/stops.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/y_axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/y_axis.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/interp_linear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/interp_linear.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/offset_stream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/offset_stream.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/images/interp_cardinal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/images/interp_cardinal.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/extensions.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/screenshots/scatterplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwinters0/crimeline/HEAD/html/scripts/rickshaw/examples/screenshots/scatterplot.png -------------------------------------------------------------------------------- /html/scripts/rickshaw/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rickshaw", 3 | "version": "1.1.2", 4 | "dependencies": { 5 | "d3": ">= 2.10.1" 6 | }, 7 | "keywords": ["d3", "charts", "rickshaw", "svg", "graph"], 8 | "main": "./rickshaw", 9 | "engines": { 10 | "node": ">= 0.8.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.JSONP.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.JSONP'); 2 | 3 | Rickshaw.Graph.JSONP = Rickshaw.Class.create( Rickshaw.Graph.Ajax, { 4 | 5 | request: function() { 6 | 7 | $.ajax( { 8 | url: this.dataURL, 9 | dataType: 'jsonp', 10 | success: this.success.bind(this), 11 | error: this.error.bind(this) 12 | } ); 13 | } 14 | } ); 15 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/css/lines.css: -------------------------------------------------------------------------------- 1 | div, span, p, td { 2 | font-family: Arial, sans-serif; 3 | } 4 | #chart { 5 | display: inline-block; 6 | } 7 | #legend { 8 | display: inline-block; 9 | position: relative; 10 | left: 8px; 11 | } 12 | #legend_container { 13 | position: absolute; 14 | right: 0; 15 | bottom: 26px; 16 | width: 0; 17 | } 18 | #chart_container { 19 | float: left; 20 | position: relative; 21 | } 22 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tests/data/simple.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/data/data.jsonp: -------------------------------------------------------------------------------- 1 | callback([ 2 | { 3 | 'color': 'blue', 4 | 'name': 'London', 5 | 'data': [ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 }, { x: 3, y: 30 }, { x: 4, y: 32 } ], 6 | }, { 7 | 'name': 'New York', 8 | 'data': [ { x: 0, y: 19 }, { x: 1, y: 22 }, { x: 2, y: 29 }, { x: 3, y: 20 }, { x: 4, y: 14 } ], 9 | }, { 10 | 'name': 'Tokyo', 11 | 'data': [ { x: 0, y: 8 }, { x: 1, y: 12 }, { x: 2, y: 15 }, { x: 3, y: 11 }, { x: 4, y: 10 } ], 12 | } 13 | ]); 14 | 15 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/data/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "color": "blue", 4 | "name": "New York", 5 | "data": [ { "x": 0, "y": 40 }, { "x": 1, "y": 49 }, { "x": 2, "y": 38 }, { "x": 3, "y": 30 }, { "x": 4, "y": 32 } ] 6 | }, { 7 | "name": "London", 8 | "data": [ { "x": 0, "y": 19 }, { "x": 1, "y": 22 }, { "x": 2, "y": 29 }, { "x": 3, "y": 20 }, { "x": 4, "y": 14 } ] 9 | }, { 10 | "name": "Tokyo", 11 | "data": [ { "x": 0, "y": 8 }, { "x": 1, "y": 12 }, { "x": 2, "y": 15 }, { "x": 3, "y": 11 }, { "x": 4, "y": 10 } ] 12 | } 13 | ] 14 | 15 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/data/data2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "color": "blue", 4 | "name": "New York", 5 | "data": [ { "x": 0, "y": 50 }, { "x": 1, "y": 32 }, { "x": 2, "y": 38 }, { "x": 3, "y": 30 }, { "x": 4, "y": 32 } ] 6 | }, { 7 | "name": "London", 8 | "data": [ { "x": 0, "y": 19 }, { "x": 1, "y": 76 }, { "x": 2, "y": 28 }, { "x": 3, "y": 20 }, { "x": 4, "y": 14 } ] 9 | }, { 10 | "name": "Tokyo", 11 | "data": [ { "x": 0, "y": 8 }, { "x": 1, "y": 12 }, { "x": 2, "y": 14 }, { "x": 3, "y": 11 }, { "x": 4, "y": 28 } ] 12 | } 13 | ] 14 | 15 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/start.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 24 | 25 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/transform.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | my @header; 7 | my $data; 8 | 9 | while (<>) { 10 | 11 | # the third line is the header 12 | if ($. == 3) { 13 | # and we want the years 14 | @header = map { m/^(\d{4})/; $1; } 15 | (split ',')[1..11]; 16 | } 17 | 18 | if (m/^United States/) { 19 | my $i = -1; 20 | $data = "[ " . join(", ", map { "{ x: $header[++$i], y: $_ }" } (split ',')[1..11]) . " ]"; 21 | last; 22 | } 23 | } 24 | 25 | print $data; 26 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Unstacker.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Unstacker'); 2 | 3 | Rickshaw.Graph.Unstacker = function(args) { 4 | 5 | this.graph = args.graph; 6 | var self = this; 7 | 8 | this.graph.stackData.hooks.after.push( { 9 | name: 'unstacker', 10 | f: function(data) { 11 | 12 | if (!self.graph.renderer.unstack) return data; 13 | 14 | data.forEach( function(seriesData) { 15 | seriesData.forEach( function(d) { 16 | d.y0 = 0; 17 | } ); 18 | } ); 19 | 20 | return data; 21 | } 22 | } ); 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/example_01.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 25 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/transform_epoch.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Time::Local 'timegm'; 7 | 8 | my @header; 9 | my $data; 10 | 11 | while (<>) { 12 | 13 | # the third line is the header 14 | if ($. == 3) { 15 | # and we want the years in epoch seconds 16 | @header = map { m/^(\d{4})/; year_to_seconds($1); } 17 | (split ',')[1..11]; 18 | } 19 | 20 | if (m/^United States/) { 21 | my $i = -1; 22 | $data = "[ " . join(", ", map { "{ x: $header[++$i], y: $_ }" } (split ',')[1..11]) . " ]"; 23 | last; 24 | } 25 | } 26 | 27 | sub year_to_seconds { 28 | my $year = shift; 29 | return timegm(0, 0, 0, 1, 0, $year); 30 | } 31 | 32 | print $data; 33 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Renderer.Line.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Renderer.Line'); 2 | 3 | Rickshaw.Graph.Renderer.Line = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { 4 | 5 | name: 'line', 6 | 7 | defaults: function($super) { 8 | 9 | return Rickshaw.extend( $super(), { 10 | unstack: true, 11 | fill: false, 12 | stroke: true 13 | } ); 14 | }, 15 | 16 | seriesPathFactory: function() { 17 | 18 | var graph = this.graph; 19 | 20 | var factory = d3.svg.line() 21 | .x( function(d) { return graph.x(d.x) } ) 22 | .y( function(d) { return graph.y(d.y) } ) 23 | .interpolate(this.graph.interpolation).tension(this.tension) 24 | 25 | factory.defined && factory.defined( function(d) { return d.y !== null } ); 26 | return factory; 27 | } 28 | } ); 29 | 30 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 30 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/example_02.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 25 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/negative.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 33 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/vendor/prettify/prettify.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | 3 | .str { color: #080; } 4 | .kwd { color: #008; } 5 | .com { color: #800; } 6 | .typ { color: #606; } 7 | .lit { color: #066; } 8 | .pun { color: #660; } 9 | .pln { color: #000; } 10 | .tag { color: #008; } 11 | .atn { color: #606; } 12 | .atv { color: #080; } 13 | .dec { color: #606; } 14 | pre.prettyprint { padding: 2px; border: 1px solid #888; } 15 | 16 | @media print { 17 | .str { color: #060; } 18 | .kwd { color: #006; font-weight: bold; } 19 | .com { color: #600; font-style: italic; } 20 | .typ { color: #404; font-weight: bold; } 21 | .lit { color: #044; } 22 | .pun { color: #440; } 23 | .pln { color: #000; } 24 | .tag { color: #006; font-weight: bold; } 25 | .atn { color: #404; } 26 | .atv { color: #060; } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/transform_region.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Time::Local 'timegm'; 7 | 8 | my @header; 9 | my $data; 10 | 11 | while (<>) { 12 | 13 | # the third line is the header 14 | if ($. == 3) { 15 | # and we want the years in epoch seconds 16 | @header = map { m/^(\d{4})/; year_to_seconds($1); } 17 | (split ',')[1..11]; 18 | } 19 | 20 | if (m/^(Northeast|Midwest|South|West)/) { 21 | my $i = -1; 22 | $data .= "\n{\n\tname: \"$1\",\n"; 23 | $data .= "\tdata: [ " . join(", ", map { "{ x: $header[++$i], y: $_ }" } (split ',')[1..11]) . " ],\n\n},"; 24 | } 25 | 26 | chop $data && last if m/^Alabama/; # we have what we need now. 27 | } 28 | 29 | sub year_to_seconds { 30 | my $year = shift; 31 | return timegm(0, 0, 0, 1, 0, $year); 32 | } 33 | 34 | print "$data\n"; 35 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Renderer.Stack.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Renderer.Stack'); 2 | 3 | Rickshaw.Graph.Renderer.Stack = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { 4 | 5 | name: 'stack', 6 | 7 | defaults: function($super) { 8 | 9 | return Rickshaw.extend( $super(), { 10 | fill: true, 11 | stroke: false, 12 | unstack: false 13 | } ); 14 | }, 15 | 16 | seriesPathFactory: function() { 17 | 18 | var graph = this.graph; 19 | 20 | var factory = d3.svg.area() 21 | .x( function(d) { return graph.x(d.x) } ) 22 | .y0( function(d) { return graph.y(d.y0) } ) 23 | .y1( function(d) { return graph.y(d.y + d.y0) } ) 24 | .interpolate(this.graph.interpolation).tension(this.tension); 25 | 26 | factory.defined && factory.defined( function(d) { return d.y !== null } ); 27 | return factory; 28 | } 29 | } ); 30 | 31 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.js: -------------------------------------------------------------------------------- 1 | var Rickshaw = { 2 | 3 | namespace: function(namespace, obj) { 4 | 5 | var parts = namespace.split('.'); 6 | 7 | var parent = Rickshaw; 8 | 9 | for(var i = 1, length = parts.length; i < length; i++) { 10 | var currentPart = parts[i]; 11 | parent[currentPart] = parent[currentPart] || {}; 12 | parent = parent[currentPart]; 13 | } 14 | return parent; 15 | }, 16 | 17 | keys: function(obj) { 18 | var keys = []; 19 | for (var key in obj) keys.push(key); 20 | return keys; 21 | }, 22 | 23 | extend: function(destination, source) { 24 | 25 | for (var property in source) { 26 | destination[property] = source[property]; 27 | } 28 | return destination; 29 | } 30 | }; 31 | 32 | if (typeof module !== 'undefined' && module.exports) { 33 | var d3 = require('d3'); 34 | module.exports = Rickshaw; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Fixtures.RandomData.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Fixtures.RandomData'); 2 | 3 | Rickshaw.Fixtures.RandomData = function(timeInterval) { 4 | 5 | var addData; 6 | timeInterval = timeInterval || 1; 7 | 8 | var lastRandomValue = 200; 9 | 10 | var timeBase = Math.floor(new Date().getTime() / 1000); 11 | 12 | this.addData = function(data) { 13 | 14 | var randomValue = Math.random() * 100 + 15 + lastRandomValue; 15 | var index = data[0].length; 16 | 17 | var counter = 1; 18 | 19 | data.forEach( function(series) { 20 | var randomVariance = Math.random() * 20; 21 | var v = randomValue / 25 + counter++ 22 | + (Math.cos((index * counter * 11) / 960) + 2) * 15 23 | + (Math.cos(index / 7) + 2) * 7 24 | + (Math.cos(index / 17) + 2) * 1; 25 | 26 | series.push( { x: (index * timeInterval) + timeBase, y: v + randomVariance } ); 27 | } ); 28 | 29 | lastRandomValue = randomValue * .85; 30 | } 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/scatterplot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 44 | 45 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/example_03.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 28 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/ajax.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Shutterstock Images, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Behavior.Series.Order.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Order'); 2 | 3 | Rickshaw.Graph.Behavior.Series.Order = function(args) { 4 | 5 | this.graph = args.graph; 6 | this.legend = args.legend; 7 | 8 | var self = this; 9 | 10 | $(function() { 11 | $(self.legend.list).sortable( { 12 | containment: 'parent', 13 | tolerance: 'pointer', 14 | update: function( event, ui ) { 15 | var series = []; 16 | $(self.legend.list).find('li').each( function(index, item) { 17 | if (!item.series) return; 18 | series.push(item.series); 19 | } ); 20 | 21 | for (var i = self.graph.series.length - 1; i >= 0; i--) { 22 | self.graph.series[i] = series.shift(); 23 | } 24 | 25 | self.graph.update(); 26 | } 27 | } ); 28 | $(self.legend.list).disableSelection(); 29 | }); 30 | 31 | //hack to make jquery-ui sortable behave 32 | this.graph.onUpdate( function() { 33 | var h = window.getComputedStyle(self.legend.element).height; 34 | self.legend.element.style.height = h; 35 | } ); 36 | }; 37 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/refresh.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 |
17 | 18 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/bars.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Fixtures.Number.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Fixtures.Number'); 2 | 3 | Rickshaw.Fixtures.Number.formatKMBT = function(y) { 4 | abs_y = Math.abs(y); 5 | if (abs_y >= 1000000000000) { return y / 1000000000000 + "T" } 6 | else if (abs_y >= 1000000000) { return y / 1000000000 + "B" } 7 | else if (abs_y >= 1000000) { return y / 1000000 + "M" } 8 | else if (abs_y >= 1000) { return y / 1000 + "K" } 9 | else if (abs_y < 1 && y > 0) { return y.toFixed(2) } 10 | else if (abs_y == 0) { return '' } 11 | else { return y } 12 | }; 13 | 14 | Rickshaw.Fixtures.Number.formatBase1024KMGTP = function(y) { 15 | abs_y = Math.abs(y); 16 | if (abs_y >= 1125899906842624) { return y / 1125899906842624 + "P" } 17 | else if (abs_y >= 1099511627776){ return y / 1099511627776 + "T" } 18 | else if (abs_y >= 1073741824) { return y / 1073741824 + "G" } 19 | else if (abs_y >= 1048576) { return y / 1048576 + "M" } 20 | else if (abs_y >= 1024) { return y / 1024 + "K" } 21 | else if (abs_y < 1 && y > 0) { return y.toFixed(2) } 22 | else if (abs_y == 0) { return '' } 23 | else { return y } 24 | }; 25 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Renderer.ScatterPlot.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Renderer.ScatterPlot'); 2 | 3 | Rickshaw.Graph.Renderer.ScatterPlot = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { 4 | 5 | name: 'scatterplot', 6 | 7 | defaults: function($super) { 8 | 9 | return Rickshaw.extend( $super(), { 10 | unstack: true, 11 | fill: true, 12 | stroke: false, 13 | padding:{ top: 0.01, right: 0.01, bottom: 0.01, left: 0.01 }, 14 | dotSize: 4 15 | } ); 16 | }, 17 | 18 | initialize: function($super, args) { 19 | $super(args); 20 | }, 21 | 22 | render: function() { 23 | 24 | var graph = this.graph; 25 | 26 | graph.vis.selectAll('*').remove(); 27 | 28 | graph.series.forEach( function(series) { 29 | 30 | if (series.disabled) return; 31 | 32 | var nodes = graph.vis.selectAll("path") 33 | .data(series.stack.filter( function(d) { return d.y !== null } )) 34 | .enter().append("svg:circle") 35 | .attr("cx", function(d) { return graph.x(d.x) }) 36 | .attr("cy", function(d) { return graph.y(d.y) }) 37 | .attr("r", function(d) { return ("r" in d) ? d.r : graph.renderer.dotSize}); 38 | 39 | Array.prototype.forEach.call(nodes[0], function(n) { 40 | n.setAttribute('fill', series.color); 41 | } ); 42 | 43 | }, this ); 44 | } 45 | } ); 46 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tests/Rickshaw.Class.js: -------------------------------------------------------------------------------- 1 | var Rickshaw = require('../rickshaw'); 2 | 3 | exports.load = function(test) { 4 | 5 | test.equal(typeof Rickshaw.Class, 'object', 'Rickshaw.Class is a function'); 6 | test.done(); 7 | }; 8 | 9 | exports.instantiation = function(test) { 10 | 11 | Rickshaw.namespace('Rickshaw.Sample.Class'); 12 | 13 | Rickshaw.Sample.Class = Rickshaw.Class.create({ 14 | name: 'sample', 15 | concat: function(suffix) { 16 | return [this.name, suffix].join(' '); 17 | } 18 | }); 19 | 20 | var sample = new Rickshaw.Sample.Class(); 21 | test.equal(sample.concat('polka'), 'sample polka'); 22 | 23 | Rickshaw.namespace('Rickshaw.Sample.Class.Prefix'); 24 | 25 | Rickshaw.Sample.Subclass = Rickshaw.Class.create( Rickshaw.Sample.Class, { 26 | name: 'sampler', 27 | }); 28 | 29 | var sampler = new Rickshaw.Sample.Subclass(); 30 | test.equal(sampler.concat('polka'), 'sampler polka'); 31 | 32 | test.done(); 33 | }; 34 | 35 | exports.array = function(test) { 36 | 37 | Rickshaw.namespace('Rickshaw.Sample.Array'); 38 | 39 | Rickshaw.Sample.Array = Rickshaw.Class.create(Array, { 40 | second: function() { 41 | return this[1]; 42 | } 43 | }); 44 | 45 | var array = new Rickshaw.Sample.Array(); 46 | array.push('red'); 47 | array.push('blue'); 48 | 49 | test.equal(array.second(), 'blue'); 50 | 51 | test.done(); 52 | }; 53 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/inconsistent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 47 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/jsonp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 | 18 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/gaps.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 47 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/y_axis.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 22 | 23 |
24 |
25 |
26 |
27 | 28 | 61 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.RangeSlider.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.RangeSlider'); 2 | 3 | Rickshaw.Graph.RangeSlider = function(args) { 4 | 5 | var element = this.element = args.element; 6 | var graph = this.graph = args.graph; 7 | 8 | $( function() { 9 | $(element).slider( { 10 | 11 | range: true, 12 | min: graph.dataDomain()[0], 13 | max: graph.dataDomain()[1], 14 | values: [ 15 | graph.dataDomain()[0], 16 | graph.dataDomain()[1] 17 | ], 18 | slide: function( event, ui ) { 19 | 20 | graph.window.xMin = ui.values[0]; 21 | graph.window.xMax = ui.values[1]; 22 | graph.update(); 23 | 24 | // if we're at an extreme, stick there 25 | if (graph.dataDomain()[0] == ui.values[0]) { 26 | graph.window.xMin = undefined; 27 | } 28 | if (graph.dataDomain()[1] == ui.values[1]) { 29 | graph.window.xMax = undefined; 30 | } 31 | } 32 | } ); 33 | } ); 34 | 35 | element[0].style.width = graph.width + 'px'; 36 | 37 | graph.onUpdate( function() { 38 | 39 | var values = $(element).slider('option', 'values'); 40 | 41 | $(element).slider('option', 'min', graph.dataDomain()[0]); 42 | $(element).slider('option', 'max', graph.dataDomain()[1]); 43 | 44 | if (graph.window.xMin == undefined) { 45 | values[0] = graph.dataDomain()[0]; 46 | } 47 | if (graph.window.xMax == undefined) { 48 | values[1] = graph.dataDomain()[1]; 49 | } 50 | 51 | $(element).slider('option', 'values', values); 52 | 53 | } ); 54 | }; 55 | 56 | -------------------------------------------------------------------------------- /html/scripts/d3/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Michael Bostock 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * The name Michael Bostock may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##Crimeline 2 | 3 | This project is under heavy development! That means some of this is quite ugly … 4 | 5 | ###Overview 6 | 7 | I plan to create a php/SQL backend to allow live querying against the dataset. To that end, I wrote some php CLI scripts that download the CSV data from the city's website and clean it up, since CSV is easy to directly import into most databases. 8 | 9 | In the meantime, all data is stored as static json. After cleaning the CSV data, I run a second round of scripts to aggregate & output as json. 10 | 11 | ###Getting started 12 | 13 | The code is committed with the necessary .json files already created. If you need to recreate them for any reason: 14 | 15 | 1) Run `clean_calls.php`. This will download and sanitize the call data, outputting to clean_calls.csv It will also output clean_call_types.csv, which is intended for import to SQL as a related table. 16 | 17 | 2) Run `json_calls.php`. This will aggregate the data into the "frames" needed by the mapplayer, outputted as calls.json. Note that the `$framesep` variable at the top should match the `secPerFrame` variable in html/scripts/main.js. 18 | 19 | 3) Copy calls.json into html/scripts 20 | 21 | ###Other 22 | The incident numbers in the calls data are in a slightly different format from the incident numbers in the incident reports. I originally intended to link the calls to incident reports but I haven't finished the code to munge the incident numbers so they match across data sets. 23 | 24 | You'll find the beginning of this code in clean_incidents.php 25 | 26 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/example_04.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 23 | 24 |
25 |
26 |
27 |
28 | 29 | 52 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Smoother.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Smoother'); 2 | 3 | Rickshaw.Graph.Smoother = function(args) { 4 | 5 | this.graph = args.graph; 6 | this.element = args.element; 7 | 8 | var self = this; 9 | 10 | this.aggregationScale = 1; 11 | 12 | if (this.element) { 13 | 14 | $( function() { 15 | $(self.element).slider( { 16 | min: 1, 17 | max: 100, 18 | slide: function( event, ui ) { 19 | self.setScale(ui.value); 20 | self.graph.update(); 21 | } 22 | } ); 23 | } ); 24 | } 25 | 26 | self.graph.stackData.hooks.data.push( { 27 | name: 'smoother', 28 | orderPosition: 50, 29 | f: function(data) { 30 | 31 | if (self.aggregationScale == 1) return data; 32 | 33 | var aggregatedData = []; 34 | 35 | data.forEach( function(seriesData) { 36 | 37 | var aggregatedSeriesData = []; 38 | 39 | while (seriesData.length) { 40 | 41 | var avgX = 0, avgY = 0; 42 | var slice = seriesData.splice(0, self.aggregationScale); 43 | 44 | slice.forEach( function(d) { 45 | avgX += d.x / slice.length; 46 | avgY += d.y / slice.length; 47 | } ); 48 | 49 | aggregatedSeriesData.push( { x: avgX, y: avgY } ); 50 | } 51 | 52 | aggregatedData.push(aggregatedSeriesData); 53 | } ); 54 | 55 | return aggregatedData; 56 | } 57 | } ); 58 | 59 | this.setScale = function(scale) { 60 | 61 | if (scale < 1) { 62 | throw "scale out of range: " + scale; 63 | } 64 | 65 | this.aggregationScale = scale; 66 | this.graph.update(); 67 | } 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Legend.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Legend'); 2 | 3 | Rickshaw.Graph.Legend = function(args) { 4 | 5 | var element = this.element = args.element; 6 | var graph = this.graph = args.graph; 7 | 8 | var self = this; 9 | 10 | element.classList.add('rickshaw_legend'); 11 | 12 | var list = this.list = document.createElement('ul'); 13 | element.appendChild(list); 14 | 15 | var series = graph.series 16 | .map( function(s) { return s } ) 17 | 18 | if (!args.naturalOrder) { 19 | series = series.reverse(); 20 | } 21 | 22 | this.lines = []; 23 | 24 | this.addLine = function (series) { 25 | var line = document.createElement('li'); 26 | line.className = 'line'; 27 | 28 | var swatch = document.createElement('div'); 29 | swatch.className = 'swatch'; 30 | swatch.style.backgroundColor = series.color; 31 | 32 | line.appendChild(swatch); 33 | 34 | var label = document.createElement('span'); 35 | label.className = 'label'; 36 | label.innerHTML = series.name; 37 | 38 | line.appendChild(label); 39 | list.appendChild(line); 40 | 41 | line.series = series; 42 | 43 | if (series.noLegend) { 44 | line.style.display = 'none'; 45 | } 46 | 47 | var _line = { element: line, series: series }; 48 | if (self.shelving) { 49 | self.shelving.addAnchor(_line); 50 | self.shelving.updateBehaviour(); 51 | } 52 | if (self.highlighter) { 53 | self.highlighter.addHighlightEvents(_line); 54 | } 55 | self.lines.push(_line); 56 | }; 57 | 58 | series.forEach( function(s) { 59 | self.addLine(s); 60 | } ); 61 | 62 | graph.onUpdate( function() {} ); 63 | }; 64 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | width: 780px; 3 | margin: auto; 4 | font-family: Arial, Helvetica, sans-serif; 5 | font-size: 13px; 6 | color: #282828; 7 | line-height: 135%; 8 | } 9 | a { 10 | text-decoration: none; 11 | color: steelblue; 12 | } 13 | a:hover { 14 | color: lightblue; 15 | } 16 | h1 { 17 | margin: 1em 0 1em 0; 18 | font-weight: normal; 19 | } 20 | h2 { 21 | font-weight: normal; 22 | margin: 1.4em 0 1em 0; 23 | color: black; 24 | } 25 | .tutorial-chart { 26 | margin-left: 1%; 27 | vertical-align: top; 28 | margin-top: 1em; 29 | } 30 | code { 31 | background-color: #f0f0f0; 32 | padding: 2px; 33 | } 34 | section.example { 35 | border: 1px solid #e4e4e4; 36 | } 37 | 38 | pre.prettyprint { 39 | border: none; 40 | border-top: 1px solid #e8e8e8; 41 | background: #f0f0f0; 42 | padding: 14px 14px; 43 | margin: 0; 44 | white-space: pre-wrap; 45 | } 46 | section iframe { 47 | width: 580px; 48 | height: 270px; 49 | border: none; 50 | margin: 0 auto 12px auto; 51 | display: block; 52 | } 53 | section header { 54 | border-bottom: 1px solid #e8e8e8; 55 | color: #b0b0b0; 56 | margin: 0 0 16px 0; 57 | } 58 | section header a:hover { 59 | color: steelblue; 60 | } 61 | section header a { 62 | color: #d0d0d0; 63 | text-decoration: none; 64 | margin: 6px 10px 0 0; 65 | } 66 | section header h3 { 67 | font-weight: normal; 68 | margin: 0 0 10px 0; 69 | position: relative; 70 | padding: 6px 0 0 8px; 71 | pointer-events: none; 72 | } 73 | section header a { 74 | float: right; 75 | } 76 | .linenums { 77 | color: #d0d0d0; 78 | } 79 | #example_06 iframe, 80 | #example_07 iframe { 81 | width: 750px; 82 | } 83 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/css/legend.css: -------------------------------------------------------------------------------- 1 | .rickshaw_legend { 2 | font-family: Arial; 3 | font-size: 12px; 4 | color: white; 5 | background: #404040; 6 | display: inline-block; 7 | padding: 12px 5px; 8 | border-radius: 2px; 9 | position: relative; 10 | } 11 | .rickshaw_legend:hover { 12 | z-index: 10; 13 | } 14 | .rickshaw_legend .swatch { 15 | width: 10px; 16 | height: 10px; 17 | border: 1px solid rgba(0, 0, 0, 0.2); 18 | } 19 | .rickshaw_legend .line { 20 | clear: both; 21 | line-height: 140%; 22 | padding-right: 15px; 23 | } 24 | .rickshaw_legend .line .swatch { 25 | display: inline-block; 26 | margin-right: 3px; 27 | border-radius: 2px; 28 | } 29 | .rickshaw_legend .label { 30 | margin: 0; 31 | white-space: nowrap; 32 | display: inline; 33 | font-size: inherit; 34 | background-color: transparent; 35 | color: inherit; 36 | font-weight: normal; 37 | line-height: normal; 38 | padding: 0px; 39 | text-shadow: none; 40 | } 41 | .rickshaw_legend .action:hover { 42 | opacity: 0.6; 43 | } 44 | .rickshaw_legend .action { 45 | margin-right: 0.2em; 46 | font-size: 10px; 47 | opacity: 0.2; 48 | cursor: pointer; 49 | font-size: 14px; 50 | } 51 | .rickshaw_legend .line.disabled { 52 | opacity: 0.4; 53 | } 54 | .rickshaw_legend ul { 55 | list-style-type: none; 56 | margin: 0; 57 | padding: 0; 58 | margin: 2px; 59 | cursor: pointer; 60 | } 61 | .rickshaw_legend li { 62 | padding: 0 0 0 2px; 63 | min-width: 80px; 64 | white-space: nowrap; 65 | } 66 | .rickshaw_legend li:hover { 67 | background: rgba(255, 255, 255, 0.08); 68 | border-radius: 3px; 69 | } 70 | .rickshaw_legend li:active { 71 | background: rgba(255, 255, 255, 0.2); 72 | border-radius: 3px; 73 | } 74 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Color.Palette.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace("Rickshaw.Color.Palette"); 2 | 3 | Rickshaw.Color.Palette = function(args) { 4 | 5 | var color = new Rickshaw.Fixtures.Color(); 6 | 7 | args = args || {}; 8 | this.schemes = {}; 9 | 10 | this.scheme = color.schemes[args.scheme] || args.scheme || color.schemes.colorwheel; 11 | this.runningIndex = 0; 12 | this.generatorIndex = 0; 13 | 14 | if (args.interpolatedStopCount) { 15 | var schemeCount = this.scheme.length - 1; 16 | var i, j, scheme = []; 17 | for (i = 0; i < schemeCount; i++) { 18 | scheme.push(this.scheme[i]); 19 | var generator = d3.interpolateHsl(this.scheme[i], this.scheme[i + 1]); 20 | for (j = 1; j < args.interpolatedStopCount; j++) { 21 | scheme.push(generator((1 / args.interpolatedStopCount) * j)); 22 | } 23 | } 24 | scheme.push(this.scheme[this.scheme.length - 1]); 25 | this.scheme = scheme; 26 | } 27 | this.rotateCount = this.scheme.length; 28 | 29 | this.color = function(key) { 30 | return this.scheme[key] || this.scheme[this.runningIndex++] || this.interpolateColor() || '#808080'; 31 | }; 32 | 33 | this.interpolateColor = function() { 34 | if (!Array.isArray(this.scheme)) return; 35 | var color; 36 | if (this.generatorIndex == this.rotateCount * 2 - 1) { 37 | color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[0])(0.5); 38 | this.generatorIndex = 0; 39 | this.rotateCount *= 2; 40 | } else { 41 | color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[this.generatorIndex + 1])(0.5); 42 | this.generatorIndex++; 43 | } 44 | this.scheme.push(color); 45 | return color; 46 | }; 47 | 48 | }; 49 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | 58 | 59 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Ajax.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Ajax'); 2 | 3 | Rickshaw.Graph.Ajax = Rickshaw.Class.create( { 4 | 5 | initialize: function(args) { 6 | 7 | this.dataURL = args.dataURL; 8 | 9 | this.onData = args.onData || function(d) { return d }; 10 | this.onComplete = args.onComplete || function() {}; 11 | this.onError = args.onError || function() {}; 12 | 13 | this.args = args; // pass through to Rickshaw.Graph 14 | 15 | this.request(); 16 | }, 17 | 18 | request: function() { 19 | 20 | $.ajax( { 21 | url: this.dataURL, 22 | dataType: 'json', 23 | success: this.success.bind(this), 24 | error: this.error.bind(this) 25 | } ); 26 | }, 27 | 28 | error: function() { 29 | 30 | console.log("error loading dataURL: " + this.dataURL); 31 | this.onError(this); 32 | }, 33 | 34 | success: function(data, status) { 35 | 36 | data = this.onData(data); 37 | this.args.series = this._splice({ data: data, series: this.args.series }); 38 | 39 | this.graph = this.graph || new Rickshaw.Graph(this.args); 40 | this.graph.render(); 41 | 42 | this.onComplete(this); 43 | }, 44 | 45 | _splice: function(args) { 46 | 47 | var data = args.data; 48 | var series = args.series; 49 | 50 | if (!args.series) return data; 51 | 52 | series.forEach( function(s) { 53 | 54 | var seriesKey = s.key || s.name; 55 | if (!seriesKey) throw "series needs a key or a name"; 56 | 57 | data.forEach( function(d) { 58 | 59 | var dataKey = d.key || d.name; 60 | if (!dataKey) throw "data needs a key or a name"; 61 | 62 | if (seriesKey == dataKey) { 63 | var properties = ['color', 'name', 'data']; 64 | properties.forEach( function(p) { 65 | if (d[p]) s[p] = d[p]; 66 | } ); 67 | } 68 | } ); 69 | } ); 70 | 71 | return series; 72 | } 73 | } ); 74 | 75 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/colors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 27 | 28 | 29 | 30 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/css/detail.css: -------------------------------------------------------------------------------- 1 | .rickshaw_graph .detail { 2 | pointer-events: none; 3 | position: absolute; 4 | top: 0; 5 | z-index: 2; 6 | background: rgba(0, 0, 0, 0.1); 7 | bottom: 0; 8 | width: 1px; 9 | transition: opacity 0.25s linear; 10 | -moz-transition: opacity 0.25s linear; 11 | -o-transition: opacity 0.25s linear; 12 | -webkit-transition: opacity 0.25s linear; 13 | } 14 | .rickshaw_graph .detail.inactive { 15 | opacity: 0; 16 | } 17 | .rickshaw_graph .detail .item.active { 18 | opacity: 1; 19 | } 20 | .rickshaw_graph .detail .x_label { 21 | font-family: Arial, sans-serif; 22 | border-radius: 3px; 23 | padding: 6px; 24 | opacity: 0.5; 25 | border: 1px solid #e0e0e0; 26 | font-size: 12px; 27 | position: absolute; 28 | background: white; 29 | white-space: nowrap; 30 | } 31 | .rickshaw_graph .detail .item { 32 | position: absolute; 33 | z-index: 2; 34 | border-radius: 3px; 35 | padding: 0.25em; 36 | font-size: 12px; 37 | font-family: Arial, sans-serif; 38 | opacity: 0; 39 | background: rgba(0, 0, 0, 0.4); 40 | color: white; 41 | border: 1px solid rgba(0, 0, 0, 0.4); 42 | margin-left: 1em; 43 | margin-top: -1em; 44 | white-space: nowrap; 45 | } 46 | .rickshaw_graph .detail .item.active { 47 | opacity: 1; 48 | background: rgba(0, 0, 0, 0.8); 49 | } 50 | .rickshaw_graph .detail .item:before { 51 | content: "\25c2"; 52 | position: absolute; 53 | left: -0.5em; 54 | color: rgba(0, 0, 0, 0.7); 55 | width: 0; 56 | } 57 | .rickshaw_graph .detail .dot { 58 | width: 4px; 59 | height: 4px; 60 | margin-left: -4px; 61 | margin-top: -3px; 62 | border-radius: 5px; 63 | position: absolute; 64 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.6); 65 | background: white; 66 | border-width: 2px; 67 | border-style: solid; 68 | display: none; 69 | background-clip: padding-box; 70 | } 71 | .rickshaw_graph .detail .dot.active { 72 | display: block; 73 | } 74 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tests/Rickshaw.Series.FixedDuration.js: -------------------------------------------------------------------------------- 1 | var Rickshaw = require("../rickshaw"); 2 | 3 | function seriesData() { 4 | return { 5 | name: 'series1', 6 | data: [ {x: 0, y: 20}, {x: 1, y: 21}, { x: 2, y: 15} ], 7 | color: 'red' 8 | }; 9 | } 10 | 11 | exports.basic = function(test) { 12 | 13 | test.equal(typeof Rickshaw.Series.FixedDuration, 'function', 'Rickshaw.Series.FixedDuration is a function'); 14 | test.done(); 15 | }; 16 | 17 | exports.initialize = function(test) { 18 | 19 | function instantiateMalformed() { 20 | 21 | var series = new Rickshaw.Series.FixedDuration( 22 | [seriesData()], 23 | 'spectrum2001', 24 | { timeBase: 0, maxDataPoints: 2000 } 25 | ); 26 | } 27 | 28 | test.throws(instantiateMalformed, 'FixedDuration series requires timeInterval', 'we die without a timeInterval'); 29 | 30 | function instantiateMalformed() { 31 | 32 | var series = new Rickshaw.Series.FixedDuration( 33 | [seriesData()], 34 | 'spectrum2001', 35 | { timeBase: 0, timeInterval: 30 } 36 | ); 37 | } 38 | 39 | test.throws(instantiateMalformed, 'FixedDuration series requires maxDataPoints', 'we die without maxDataPoints'); 40 | 41 | var series = new Rickshaw.Series.FixedDuration( 42 | [seriesData()], 43 | 'spectrum2001', 44 | { 45 | timeBase: 0, 46 | timeInterval: 30, 47 | maxDataPoints: 2000 48 | } 49 | ); 50 | 51 | test.ok(series instanceof Rickshaw.Series.FixedDuration); 52 | test.ok(series instanceof Array); 53 | test.done(); 54 | } 55 | 56 | exports.addData = function(test) { 57 | 58 | var series = new Rickshaw.Series.FixedDuration( 59 | [seriesData()], 60 | 'spectrum2001', 61 | { 62 | timeBase: 0, 63 | timeInterval: 1, 64 | maxDataPoints: 20 65 | } 66 | ); 67 | 68 | for (var i = 0; i < 300; i++) { 69 | series.addData({series1: 42}); 70 | } 71 | 72 | test.equal(series[0].data.length, 20 + 2, 'series length stuck around maxDataPoints'); 73 | test.equal(series.currentSize, 20, 'series.currentSize is stuck at maxDataPoints'); 74 | test.done(); 75 | } 76 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/stops.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 40 | 41 | 42 | 43 |
44 |

45 |
46 |
47 | 48 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tests/Rickshaw.Graph.Renderer.js: -------------------------------------------------------------------------------- 1 | var Rickshaw = require("../rickshaw"); 2 | 3 | exports.domain = function(test) { 4 | 5 | // document comes from jsdom 6 | var el = document.createElement("div"); 7 | 8 | var graph = new Rickshaw.Graph({ 9 | element: el, 10 | width: 960, 11 | height: 500, 12 | padding: { top: 0, right: 0, bottom: 0, left: 0 }, 13 | renderer: 'scatterplot', 14 | series: [ 15 | { 16 | color: 'steelblue', 17 | data: [ 18 | { x: 0, y: 40 }, 19 | { x: 1, y: 49 }, 20 | { x: 2, y: 38 }, 21 | { x: 3, y: 30 }, 22 | { x: 4, y: 32 } 23 | ] 24 | } 25 | ] 26 | }); 27 | 28 | var domain = graph.renderer.domain(); 29 | test.deepEqual(domain, { x: [ 0, 4 ], y: [ 0, 49 ] }, 'domain matches'); 30 | 31 | // with padding 32 | 33 | graph.configure({ padding: { top: 0.1, right: 0.1, bottom: 0.1, left: 0.1 }}); 34 | 35 | domain = graph.renderer.domain(); 36 | test.deepEqual(domain, { x: [ -0.4, 4.44 ], y: [ 0, 49 + 4.9 ] }, 'domain matches with padding'); 37 | 38 | // negative y-values minus auto 39 | 40 | graph.series[0].data[2].y = -72; 41 | graph.configure({ padding: { top: 0, right: 0, bottom: 0, left: 0 }}); 42 | 43 | domain = graph.renderer.domain(); 44 | test.deepEqual(domain, { x: [ 0, 4 ], y: [ 0, 49 ] }, 'domain matches with negative numbers and no auto'); 45 | 46 | // negative y-values w/ auto 47 | 48 | graph.series[0].data[2].y = -72; 49 | graph.configure({ padding: { top: 0, right: 0, bottom: 0, left: 0 }, min: 'auto'}); 50 | 51 | domain = graph.renderer.domain(); 52 | test.deepEqual(domain, { x: [ 0, 4 ], y: [ -72, 49 ] }, 'domain matches with negative numbers and min auto'); 53 | 54 | // different series lengths 55 | 56 | graph.series.push({ 57 | color: 'lightblue', 58 | data: [ { x: 1, y: 20 }, { x: 2, y: 38 }, { x: 3, y: 30 }, { x: 4, y: 32 }, { x: 5, y: 32 } ] 59 | }); 60 | 61 | graph.stackData(); 62 | domain = graph.renderer.domain(); 63 | test.deepEqual(domain, { x: [ 0, 5 ], y: [ -72, 49 ] }, 'multiple length series extents match'); 64 | 65 | test.done(); 66 | }; 67 | 68 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Behavior.Series.Highlight.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Highlight'); 2 | 3 | Rickshaw.Graph.Behavior.Series.Highlight = function(args) { 4 | 5 | this.graph = args.graph; 6 | this.legend = args.legend; 7 | 8 | var self = this; 9 | 10 | var colorSafe = {}; 11 | var activeLine = null; 12 | 13 | this.addHighlightEvents = function (l) { 14 | 15 | l.element.addEventListener( 'mouseover', function(e) { 16 | 17 | if (activeLine) return; 18 | else activeLine = l; 19 | 20 | self.legend.lines.forEach( function(line, index) { 21 | 22 | if (l === line) { 23 | 24 | // if we're not in a stacked renderer bring active line to the top 25 | if (index > 0 && self.graph.renderer.unstack) { 26 | 27 | var seriesIndex = self.graph.series.length - index - 1; 28 | line.originalIndex = seriesIndex; 29 | 30 | var series = self.graph.series.splice(seriesIndex, 1)[0]; 31 | self.graph.series.push(series); 32 | } 33 | return; 34 | } 35 | 36 | colorSafe[line.series.name] = colorSafe[line.series.name] || line.series.color; 37 | line.series.color = d3.interpolateRgb(line.series.color, d3.rgb('#d8d8d8'))(0.8).toString(); 38 | } ); 39 | 40 | self.graph.update(); 41 | 42 | }, false ); 43 | 44 | l.element.addEventListener( 'mouseout', function(e) { 45 | 46 | if (!activeLine) return; 47 | else activeLine = null; 48 | 49 | self.legend.lines.forEach( function(line) { 50 | 51 | // return reordered series to its original place 52 | if (l === line && line.hasOwnProperty('originalIndex')) { 53 | 54 | var series = self.graph.series.pop(); 55 | self.graph.series.splice(line.originalIndex, 0, series); 56 | delete line['originalIndex']; 57 | } 58 | 59 | if (colorSafe[line.series.name]) { 60 | line.series.color = colorSafe[line.series.name]; 61 | } 62 | } ); 63 | 64 | self.graph.update(); 65 | 66 | }, false ); 67 | }; 68 | 69 | if (this.legend) { 70 | this.legend.lines.forEach( function(l) { 71 | self.addHighlightEvents(l); 72 | } ); 73 | } 74 | 75 | }; 76 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/Makefile: -------------------------------------------------------------------------------- 1 | NODE_PREFIX=$(shell npm prefix) 2 | NODE_MODULES=$(NODE_PREFIX)/node_modules 3 | 4 | CSS_MIN=$(NODE_MODULES)/.bin/cleancss 5 | JS_MIN=$(NODE_MODULES)/.bin/uglifyjs 6 | 7 | CSS_FILES=\ 8 | src/css/detail.css\ 9 | src/css/graph.css\ 10 | src/css/legend.css\ 11 | 12 | JS_FILES=\ 13 | src/js/Rickshaw.js\ 14 | src/js/Rickshaw.Class.js\ 15 | src/js/Rickshaw.Compat.ClassList.js\ 16 | src/js/Rickshaw.Graph.js\ 17 | src/js/Rickshaw.Fixtures.Color.js\ 18 | src/js/Rickshaw.Fixtures.RandomData.js\ 19 | src/js/Rickshaw.Fixtures.Time.js\ 20 | src/js/Rickshaw.Fixtures.Number.js\ 21 | src/js/Rickshaw.Color.Palette.js\ 22 | src/js/Rickshaw.Graph.Ajax.js\ 23 | src/js/Rickshaw.Graph.Annotate.js\ 24 | src/js/Rickshaw.Graph.Axis.Time.js\ 25 | src/js/Rickshaw.Graph.Axis.Y.js\ 26 | src/js/Rickshaw.Graph.Behavior.Series.Highlight.js\ 27 | src/js/Rickshaw.Graph.Behavior.Series.Order.js\ 28 | src/js/Rickshaw.Graph.Behavior.Series.Toggle.js\ 29 | src/js/Rickshaw.Graph.HoverDetail.js\ 30 | src/js/Rickshaw.Graph.JSONP.js\ 31 | src/js/Rickshaw.Graph.Legend.js\ 32 | src/js/Rickshaw.Graph.RangeSlider.js\ 33 | src/js/Rickshaw.Graph.Renderer.js\ 34 | src/js/Rickshaw.Graph.Renderer.Line.js\ 35 | src/js/Rickshaw.Graph.Renderer.Stack.js\ 36 | src/js/Rickshaw.Graph.Renderer.Bar.js\ 37 | src/js/Rickshaw.Graph.Renderer.Area.js\ 38 | src/js/Rickshaw.Graph.Renderer.ScatterPlot.js\ 39 | src/js/Rickshaw.Graph.Smoother.js\ 40 | src/js/Rickshaw.Graph.Unstacker.js\ 41 | src/js/Rickshaw.Series.js\ 42 | src/js/Rickshaw.Series.FixedDuration.js\ 43 | 44 | .PHONY: clean build 45 | 46 | build: rickshaw.min.css rickshaw.min.js 47 | 48 | clean: 49 | rm -rf rickshaw.css rickshaw.js rickshaw.min.* 50 | 51 | $(CSS_MIN): 52 | npm install clean-css 53 | 54 | $(JS_MIN): 55 | npm install uglify-js 56 | 57 | rickshaw.css: 58 | cat $(CSS_FILES) > rickshaw.css 59 | 60 | rickshaw.js: 61 | cat $(JS_FILES) > rickshaw.js 62 | 63 | rickshaw.min.css: $(CSS_MIN) rickshaw.css 64 | $(CSS_MIN) rickshaw.css > rickshaw.min.css 65 | 66 | rickshaw.min.js: $(JS_MIN) rickshaw.js 67 | $(JS_MIN) --reserved-names "\$$super" rickshaw.js > rickshaw.min.js 68 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/lines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Axis.Time.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Axis.Time'); 2 | 3 | Rickshaw.Graph.Axis.Time = function(args) { 4 | 5 | var self = this; 6 | 7 | this.graph = args.graph; 8 | this.elements = []; 9 | this.ticksTreatment = args.ticksTreatment || 'plain'; 10 | this.fixedTimeUnit = args.timeUnit; 11 | 12 | var time = new Rickshaw.Fixtures.Time(); 13 | 14 | this.appropriateTimeUnit = function() { 15 | 16 | var unit; 17 | var units = time.units; 18 | 19 | var domain = this.graph.x.domain(); 20 | var rangeSeconds = domain[1] - domain[0]; 21 | 22 | units.forEach( function(u) { 23 | if (Math.floor(rangeSeconds / u.seconds) >= 2) { 24 | unit = unit || u; 25 | } 26 | } ); 27 | 28 | return (unit || time.units[time.units.length - 1]); 29 | }; 30 | 31 | this.tickOffsets = function() { 32 | 33 | var domain = this.graph.x.domain(); 34 | 35 | var unit = this.fixedTimeUnit || this.appropriateTimeUnit(); 36 | var count = Math.ceil((domain[1] - domain[0]) / unit.seconds); 37 | 38 | var runningTick = domain[0]; 39 | 40 | var offsets = []; 41 | 42 | for (var i = 0; i < count; i++) { 43 | 44 | var tickValue = time.ceil(runningTick, unit); 45 | runningTick = tickValue + unit.seconds / 2; 46 | 47 | offsets.push( { value: tickValue, unit: unit } ); 48 | } 49 | 50 | return offsets; 51 | }; 52 | 53 | this.render = function() { 54 | 55 | this.elements.forEach( function(e) { 56 | e.parentNode.removeChild(e); 57 | } ); 58 | 59 | this.elements = []; 60 | 61 | var offsets = this.tickOffsets(); 62 | 63 | offsets.forEach( function(o) { 64 | 65 | if (self.graph.x(o.value) > self.graph.x.range()[1]) return; 66 | 67 | var element = document.createElement('div'); 68 | element.style.left = self.graph.x(o.value) + 'px'; 69 | element.classList.add('x_tick'); 70 | element.classList.add(self.ticksTreatment); 71 | 72 | var title = document.createElement('div'); 73 | title.classList.add('title'); 74 | title.innerHTML = o.unit.formatter(new Date(o.value * 1000)); 75 | element.appendChild(title); 76 | 77 | self.graph.element.appendChild(element); 78 | self.elements.push(element); 79 | 80 | } ); 81 | }; 82 | 83 | this.graph.onUpdate( function() { self.render() } ); 84 | }; 85 | 86 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Series.FixedDuration.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Series.FixedDuration'); 2 | 3 | Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, { 4 | 5 | initialize: function (data, palette, options) { 6 | 7 | var options = options || {} 8 | 9 | if (typeof(options.timeInterval) === 'undefined') { 10 | throw new Error('FixedDuration series requires timeInterval'); 11 | } 12 | 13 | if (typeof(options.maxDataPoints) === 'undefined') { 14 | throw new Error('FixedDuration series requires maxDataPoints'); 15 | } 16 | 17 | this.palette = new Rickshaw.Color.Palette(palette); 18 | this.timeBase = typeof(options.timeBase) === 'undefined' ? Math.floor(new Date().getTime() / 1000) : options.timeBase; 19 | this.setTimeInterval(options.timeInterval); 20 | 21 | if (this[0] && this[0].data && this[0].data.length) { 22 | this.currentSize = this[0].data.length; 23 | this.currentIndex = this[0].data.length; 24 | } else { 25 | this.currentSize = 0; 26 | this.currentIndex = 0; 27 | } 28 | 29 | this.maxDataPoints = options.maxDataPoints; 30 | 31 | 32 | if (data && (typeof(data) == "object") && (data instanceof Array)) { 33 | data.forEach( function (item) { this.addItem(item) }, this ); 34 | this.currentSize += 1; 35 | this.currentIndex += 1; 36 | } 37 | 38 | // reset timeBase for zero-filled values if needed 39 | this.timeBase -= (this.maxDataPoints - this.currentSize) * this.timeInterval; 40 | 41 | // zero-fill up to maxDataPoints size if we don't have that much data yet 42 | if ((typeof(this.maxDataPoints) !== 'undefined') && (this.currentSize < this.maxDataPoints)) { 43 | for (var i = this.maxDataPoints - this.currentSize - 1; i > 0; i--) { 44 | this.currentSize += 1; 45 | this.currentIndex += 1; 46 | this.forEach( function (item) { 47 | item.data.unshift({ x: ((i-1) * this.timeInterval || 1) + this.timeBase, y: 0, i: i }); 48 | }, this ); 49 | } 50 | } 51 | }, 52 | 53 | addData: function($super, data) { 54 | 55 | $super(data) 56 | 57 | this.currentSize += 1; 58 | this.currentIndex += 1; 59 | 60 | if (this.maxDataPoints !== undefined) { 61 | while (this.currentSize > this.maxDataPoints) { 62 | this.dropData(); 63 | } 64 | } 65 | }, 66 | 67 | dropData: function() { 68 | 69 | this.forEach(function(item) { 70 | item.data.splice(0, 1); 71 | } ); 72 | 73 | this.currentSize -= 1; 74 | }, 75 | 76 | getIndex: function () { 77 | return this.currentIndex; 78 | } 79 | } ); 80 | 81 | -------------------------------------------------------------------------------- /html/scripts/jquery-tod.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | /* 3 | note: 'this' in the jquery plugin context refers to the dom node we were invoked on. 4 | For that reason, you should return 'this' to maintain chainability. 5 | 6 | XXX TODO BUG ETC: This is ultra Q&D! I intend to flesh this out into a real plugin 7 | after this project. Much thanks to http://wesnoth.org/ for hours of fun and for 8 | lending me these images. Everything here is GPLv3! 9 | */ 10 | var methods = { 11 | init: function(options) { 12 | return this.each(function() { 13 | var $this = $(this), 14 | data = $(this).data('tod'); 15 | 16 | if (options !== undefined && options['date']) { 17 | //console.log("tod init with " + options['date']); 18 | var oDate = new Date(options['date']); 19 | } else { 20 | //console.log("tod init with now()"); 21 | var oDate = new Date(); 22 | } 23 | 24 | var img = oDate.getHours(); 25 | //console.log("tod hours is " + img); 26 | img = "images/tod/" + img + ".png"; 27 | 28 | if (! data) { 29 | //init tod on a new dom element 30 | //console.log("not data"); 31 | $(this).data('tod', { 32 | tod_set: true, 33 | options: options, 34 | img: img 35 | }); 36 | $(this).html(""); 37 | } else if (data.img != img) { 38 | //XXX why am I doing this with data instead of attr? 39 | //console.log("!= img"); 40 | data.img = img; 41 | $(this).data('tod', data); 42 | $("#jquery-tod-img").attr("src", img) 43 | } 44 | 45 | }); 46 | }, 47 | setTime: function(newTime) { //XXX do not use! blah data.tod.blah 48 | console.log("setTime here: " + newTime); 49 | return this.each(function() { 50 | var data = $(this).data('tod'); 51 | if (! data || data.tod === undefined) { 52 | //XXX should do more here 53 | //$.error("tod.setTime() on a non-tod object!"); 54 | } 55 | 56 | var oDate = new Date(newTime); 57 | var img = oDate.getHours(); 58 | img = "images/tod/" + img + ".png"; 59 | $(this).html(""); 60 | }); 61 | } 62 | }; 63 | 64 | $.fn.tod = function(method) { 65 | if (methods[method]) { 66 | return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 67 | } else if (typeof method === 'object' || ! method) { 68 | return methods.init.apply(this, arguments); 69 | } else { 70 | $.error('Method ' + method + ' does not exist on tod.'); 71 | } 72 | }; 73 | })(jQuery); 74 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/formatter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Renderer.Area.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Renderer.Area'); 2 | 3 | Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { 4 | 5 | name: 'area', 6 | 7 | defaults: function($super) { 8 | 9 | return Rickshaw.extend( $super(), { 10 | unstack: false, 11 | fill: false, 12 | stroke: false 13 | } ); 14 | }, 15 | 16 | seriesPathFactory: function() { 17 | 18 | var graph = this.graph; 19 | 20 | var factory = d3.svg.area() 21 | .x( function(d) { return graph.x(d.x) } ) 22 | .y0( function(d) { return graph.y(d.y0) } ) 23 | .y1( function(d) { return graph.y(d.y + d.y0) } ) 24 | .interpolate(graph.interpolation).tension(this.tension) 25 | 26 | factory.defined && factory.defined( function(d) { return d.y !== null } ); 27 | return factory; 28 | }, 29 | 30 | seriesStrokeFactory: function() { 31 | 32 | var graph = this.graph; 33 | 34 | var factory = d3.svg.line() 35 | .x( function(d) { return graph.x(d.x) } ) 36 | .y( function(d) { return graph.y(d.y + d.y0) } ) 37 | .interpolate(graph.interpolation).tension(this.tension) 38 | 39 | factory.defined && factory.defined( function(d) { return d.y !== null } ); 40 | return factory; 41 | }, 42 | 43 | render: function() { 44 | 45 | var graph = this.graph; 46 | 47 | graph.vis.selectAll('*').remove(); 48 | 49 | // insert or stacked areas so strokes lay on top of areas 50 | var method = this.unstack ? 'append' : 'insert'; 51 | 52 | var nodes = graph.vis.selectAll("path") 53 | .data(this.graph.stackedData) 54 | .enter()[method]("svg:g", 'g'); 55 | 56 | nodes.append("svg:path") 57 | .attr("d", this.seriesPathFactory()) 58 | .attr("class", 'area'); 59 | 60 | if (this.stroke) { 61 | nodes.append("svg:path") 62 | .attr("d", this.seriesStrokeFactory()) 63 | .attr("class", 'line'); 64 | } 65 | 66 | var i = 0; 67 | graph.series.forEach( function(series) { 68 | if (series.disabled) return; 69 | series.path = nodes[0][i++]; 70 | this._styleSeries(series); 71 | }, this ); 72 | }, 73 | 74 | _styleSeries: function(series) { 75 | 76 | if (!series.path) return; 77 | 78 | d3.select(series.path).select('.area') 79 | .attr('fill', series.color); 80 | 81 | if (this.stroke) { 82 | d3.select(series.path).select('.line') 83 | .attr('fill', 'none') 84 | .attr('stroke', series.stroke || d3.interpolateRgb(series.color, 'black')(0.125)) 85 | .attr('stroke-width', this.strokeWidth); 86 | } 87 | 88 | if (series.className) { 89 | series.path.setAttribute('class', series.className); 90 | } 91 | } 92 | } ); 93 | 94 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Axis.Y.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Axis.Y'); 2 | 3 | Rickshaw.Graph.Axis.Y = function(args) { 4 | 5 | var self = this; 6 | var berthRate = 0.10; 7 | 8 | this.initialize = function(args) { 9 | 10 | this.graph = args.graph; 11 | this.orientation = args.orientation || 'right'; 12 | 13 | var pixelsPerTick = args.pixelsPerTick || 75; 14 | this.ticks = args.ticks || Math.floor(this.graph.height / pixelsPerTick); 15 | this.tickSize = args.tickSize || 4; 16 | this.ticksTreatment = args.ticksTreatment || 'plain'; 17 | 18 | if (args.element) { 19 | 20 | this.element = args.element; 21 | this.vis = d3.select(args.element) 22 | .append("svg:svg") 23 | .attr('class', 'rickshaw_graph y_axis'); 24 | 25 | this.element = this.vis[0][0]; 26 | this.element.style.position = 'relative'; 27 | 28 | this.setSize({ width: args.width, height: args.height }); 29 | 30 | } else { 31 | this.vis = this.graph.vis; 32 | } 33 | 34 | this.graph.onUpdate( function() { self.render() } ); 35 | }; 36 | 37 | this.setSize = function(args) { 38 | 39 | args = args || {}; 40 | 41 | if (!this.element) return; 42 | 43 | if (typeof window !== 'undefined') { 44 | 45 | var style = window.getComputedStyle(this.element.parentNode, null); 46 | var elementWidth = parseInt(style.getPropertyValue('width')); 47 | 48 | if (!args.auto) { 49 | var elementHeight = parseInt(style.getPropertyValue('height')); 50 | } 51 | } 52 | 53 | this.width = args.width || elementWidth || this.graph.width * berthRate; 54 | this.height = args.height || elementHeight || this.graph.height; 55 | 56 | this.vis 57 | .attr('width', this.width) 58 | .attr('height', this.height * (1 + berthRate)); 59 | 60 | var berth = this.height * berthRate; 61 | this.element.style.top = -1 * berth + 'px'; 62 | }; 63 | 64 | this.render = function() { 65 | 66 | if (this.graph.height !== this._renderHeight) this.setSize({ auto: true }); 67 | 68 | var axis = d3.svg.axis().scale(this.graph.y).orient(this.orientation); 69 | axis.tickFormat( args.tickFormat || function(y) { return y } ); 70 | 71 | if (this.orientation == 'left') { 72 | var berth = this.height * berthRate; 73 | var transform = 'translate(' + this.width + ', ' + berth + ')'; 74 | } 75 | 76 | if (this.element) { 77 | this.vis.selectAll('*').remove(); 78 | } 79 | 80 | this.vis 81 | .append("svg:g") 82 | .attr("class", ["y_ticks", this.ticksTreatment].join(" ")) 83 | .attr("transform", transform) 84 | .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize)) 85 | 86 | var gridSize = (this.orientation == 'right' ? 1 : -1) * this.graph.width; 87 | 88 | this.graph.vis 89 | .append("svg:g") 90 | .attr("class", "y_grid") 91 | .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize)); 92 | 93 | this._renderHeight = this.graph.height; 94 | }; 95 | 96 | this.initialize(args); 97 | }; 98 | 99 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/example_05.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 26 | 27 |
28 |
29 |
30 |
31 | 32 | 75 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/fixed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 |
39 |
40 | 41 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /clean_incidents.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | $end_ts) { 82 | continue; 83 | } 84 | 85 | $line = array_map('cleanVal', $line); 86 | /* 87 | Array 88 | ( 89 | [0] => Incident ID 90 | [1] => Incident Type 91 | [2] => Case Number 92 | [3] => Incident Date 93 | [4] => Suspect 94 | [5] => Arrested 95 | [6] => Address 96 | [7] => Victim 97 | [8] => Details 98 | [9] => Released By 99 | [10] => Date Modified 100 | ) 101 | */ 102 | 103 | $line[3] = date("Y-m-d H:i:s", $ts); 104 | 105 | $line[2] = "\"{$line[2]}\""; 106 | $line[4] = "\"{$line[4]}\""; 107 | $line[5] = "\"{$line[5]}\""; 108 | $line[6] = "\"{$line[6]}\""; 109 | $line[7] = "\"{$line[7]}\""; 110 | $line[8] = "\"{$line[8]}\""; 111 | 112 | //print_r($line); 113 | fwrite($fp_out, implode(',', $line) . "\n"); 114 | $j++; 115 | 116 | echo "\r$i lines, $j in date range ..."; 117 | } 118 | 119 | echo " Done!\n"; 120 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tests/Rickshaw.Graph.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | 3 | exports.svg = function(test) { 4 | 5 | var jsdom = require("jsdom").jsdom; 6 | global.document = jsdom(""); 7 | global.window = global.document.createWindow(); 8 | 9 | var Rickshaw = require('../rickshaw'); 10 | new Rickshaw.Compat.ClassList(); 11 | 12 | var el = document.createElement("div"); 13 | 14 | var graph = new Rickshaw.Graph({ 15 | element: el, 16 | width: 960, 17 | height: 500, 18 | renderer: 'scatterplot', 19 | series: [{ 20 | color: 'steelblue', 21 | data: [ 22 | { x: 0, y: 40 }, 23 | { x: 1, y: 49 }, 24 | { x: 2, y: 38 }, 25 | { x: 3, y: 30 }, 26 | { x: 4, y: 32 } ] 27 | }] 28 | } ); 29 | 30 | graph.renderer.dotSize = 6; 31 | graph.render(); 32 | 33 | var generatedSVG = el.innerHTML; 34 | 35 | var exampleSVGFilename = __dirname + '/data/simple.svg'; 36 | var exampleSVG = fs.readFileSync(exampleSVGFilename, 'utf8').trim(); 37 | 38 | test.equal(generatedSVG, exampleSVG, "simple graph svg content matches"); 39 | 40 | test.done(); 41 | } 42 | 43 | exports.inconsistent = function(test) { 44 | 45 | var jsdom = require("jsdom").jsdom; 46 | global.document = jsdom(""); 47 | global.window = global.document.createWindow(); 48 | 49 | var Rickshaw = require('../rickshaw'); 50 | new Rickshaw.Compat.ClassList(); 51 | 52 | var el = document.createElement("div"); 53 | 54 | var series = [ 55 | { 56 | color: 'steelblue', 57 | data: [ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 } ], 58 | }, { 59 | color: 'red', 60 | data: [ { x: 0, y: 40 }, { x: 1, y: 49 }, { x: 2, y: 38 } ], 61 | } 62 | ]; 63 | 64 | test.doesNotThrow( function() { 65 | 66 | var graph = new Rickshaw.Graph({ 67 | element: el, 68 | width: 960, 69 | height: 500, 70 | renderer: 'stack', 71 | series: series 72 | }); 73 | 74 | }, "create basic graph okay" ); 75 | 76 | series[0].data.push( { x: 3, y: 88 } ); 77 | 78 | test.doesNotThrow( function() { 79 | 80 | var graph = new Rickshaw.Graph({ 81 | element: el, 82 | width: 960, 83 | height: 500, 84 | renderer: 'line', 85 | series: series 86 | }); 87 | 88 | }, "we don't throw for inconsistent length series for lines" ); 89 | 90 | test.throws( function() { 91 | 92 | var graph = new Rickshaw.Graph({ 93 | element: el, 94 | width: 960, 95 | height: 500, 96 | renderer: 'stack', 97 | series: series 98 | }); 99 | 100 | }, null, "throw for inconsistent stacked series for stack renderer" ); 101 | 102 | test.throws( function() { 103 | 104 | var graph = new Rickshaw.Graph({ 105 | element: null, 106 | width: 960, 107 | height: 500, 108 | renderer: 'stack', 109 | series: series 110 | }); 111 | 112 | }, null, "throw an error for undefined element reference" ); 113 | 114 | test.done(); 115 | } 116 | 117 | -------------------------------------------------------------------------------- /json_calls.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | = $framesep) { 77 | //increment frame 78 | $curTime += $framesep; 79 | $framenum++; 80 | while ($curTime + $framesep < $testTime) { 81 | //insert blank frames if necessary 82 | $frames[$framenum]['data'] = array(); 83 | $frames[$framenum]['t'] = date("Y/m/d H:i:00", $curTime); //frame title 84 | $curTime += $framesep; 85 | $framenum++; 86 | } 87 | } 88 | 89 | $frames[$framenum]['data'][] = $item; 90 | $frames[$framenum]['t'] = date("Y/m/d H:i:00", $curTime); //frame title 91 | // see http://dygraphs.com/date-formats.html for why Y/m/d 92 | 93 | /* 94 | if ($framenum > 5) { 95 | break; 96 | } 97 | */ 98 | echo "\r$i lines, $framenum frames..."; 99 | } 100 | $data = array_values($frames); 101 | $json = json_encode($data); 102 | fwrite($fp_out, $json); 103 | echo "\n"; 104 | 105 | echo "Done!\n"; 106 | 107 | -------------------------------------------------------------------------------- /html/scripts/mapplayer/frame.js: -------------------------------------------------------------------------------- 1 | function MapFrame(params) { 2 | //Init 3 | var map; 4 | var markers = []; 5 | var iws = []; //info windows 6 | var listeners = []; 7 | var opacity = 0; 8 | var strokeOpacityBase = 0.5; 9 | 10 | for (var i= 0; i < params.data.length; i++) { 11 | map = params.map; 12 | //console.dir(params.data[i]); 13 | var ll = new google.maps.LatLng(params.data[i].lat, params.data[i].lng); 14 | //TODO: abstract the icon and allow it to be set per marker 15 | var mObj = { 16 | position: ll, 17 | icon: { 18 | path: google.maps.SymbolPath.CIRCLE, 19 | scale: 4, 20 | fillColor: "#ff0000", 21 | fillOpacity: opacity, 22 | strokeWeight: 1, 23 | strokeOpacity: strokeOpacityBase * opacity 24 | }, 25 | }; 26 | if (params.data[i].title) { 27 | mObj.title = params.data[i].title; 28 | } 29 | if (params.data[i].color) { 30 | mObj.icon.fillColor = params.data[i].color; 31 | } 32 | var m = new google.maps.Marker(mObj); 33 | markers[i] = m; 34 | } 35 | setMarkerOpacities(); 36 | 37 | if (params.exData != undefined) { 38 | this.exData = params.exData; 39 | } 40 | 41 | 42 | //Private functions 43 | 44 | function showIW(y) { 45 | //console.log("showIW " + y); 46 | //console.dir(markers); 47 | if (iws[y] === undefined) { 48 | var iwObj = { 49 | content: markers[y].title 50 | }; 51 | if (params.data[y].iwContent) { 52 | iwObj.content = params.data[y].iwContent; 53 | } 54 | var iw = new google.maps.InfoWindow(iwObj); 55 | iws[y] = iw; 56 | } 57 | iws[y].open(map,markers[y]); 58 | } 59 | 60 | function setMarkerOpacities() { 61 | for (var i= 0; i < markers.length; i++) { 62 | var color = markers[i].icon.fillColor; 63 | var curIcon = markers[i].icon; 64 | curIcon.fillOpacity = opacity; 65 | curIcon.strokeOpacity = strokeOpacityBase * opacity; 66 | markers[i].setIcon(curIcon); 67 | } 68 | } 69 | 70 | 71 | //Public Functions 72 | 73 | 74 | this.draw = function() { 75 | var callback; 76 | if (opacity <= 0) { 77 | return; 78 | } 79 | for (var i= 0; i < markers.length; i++) { 80 | callback = function(i) { 81 | return function () { 82 | showIW(i); 83 | }; 84 | }(i); 85 | markers[i].setMap(map); 86 | //google.maps.event.addListener(markers[i], 'click', function() { showIW(i); }); //Y U no work? 87 | listeners[i] = google.maps.event.addListener(markers[i], 'click', callback); 88 | } 89 | } 90 | 91 | this.setOpacity = function(o) { 92 | opacity = o; 93 | if (opacity <= 0) { 94 | opacity = 0; 95 | this.hide(); 96 | return; 97 | } 98 | setMarkerOpacities(); 99 | } 100 | 101 | this.setRelativeOpacity = function(o) { 102 | this.setOpacity(opacity += o); 103 | } 104 | 105 | this.hide = function() { 106 | for (var i= 0; i < markers.length; i++) { 107 | markers[i].setMap(null) 108 | google.maps.event.removeListener(listeners[i]); 109 | listeners[i] = null; 110 | } 111 | listeners = []; 112 | } 113 | } 114 | 115 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Fixtures.Color.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Fixtures.Color'); 2 | 3 | Rickshaw.Fixtures.Color = function() { 4 | 5 | this.schemes = {}; 6 | 7 | this.schemes.spectrum14 = [ 8 | '#ecb796', 9 | '#dc8f70', 10 | '#b2a470', 11 | '#92875a', 12 | '#716c49', 13 | '#d2ed82', 14 | '#bbe468', 15 | '#a1d05d', 16 | '#e7cbe6', 17 | '#d8aad6', 18 | '#a888c2', 19 | '#9dc2d3', 20 | '#649eb9', 21 | '#387aa3' 22 | ].reverse(); 23 | 24 | this.schemes.spectrum2000 = [ 25 | '#57306f', 26 | '#514c76', 27 | '#646583', 28 | '#738394', 29 | '#6b9c7d', 30 | '#84b665', 31 | '#a7ca50', 32 | '#bfe746', 33 | '#e2f528', 34 | '#fff726', 35 | '#ecdd00', 36 | '#d4b11d', 37 | '#de8800', 38 | '#de4800', 39 | '#c91515', 40 | '#9a0000', 41 | '#7b0429', 42 | '#580839', 43 | '#31082b' 44 | ]; 45 | 46 | this.schemes.spectrum2001 = [ 47 | '#2f243f', 48 | '#3c2c55', 49 | '#4a3768', 50 | '#565270', 51 | '#6b6b7c', 52 | '#72957f', 53 | '#86ad6e', 54 | '#a1bc5e', 55 | '#b8d954', 56 | '#d3e04e', 57 | '#ccad2a', 58 | '#cc8412', 59 | '#c1521d', 60 | '#ad3821', 61 | '#8a1010', 62 | '#681717', 63 | '#531e1e', 64 | '#3d1818', 65 | '#320a1b' 66 | ]; 67 | 68 | this.schemes.classic9 = [ 69 | '#423d4f', 70 | '#4a6860', 71 | '#848f39', 72 | '#a2b73c', 73 | '#ddcb53', 74 | '#c5a32f', 75 | '#7d5836', 76 | '#963b20', 77 | '#7c2626', 78 | '#491d37', 79 | '#2f254a' 80 | ].reverse(); 81 | 82 | this.schemes.httpStatus = { 83 | 503: '#ea5029', 84 | 502: '#d23f14', 85 | 500: '#bf3613', 86 | 410: '#efacea', 87 | 409: '#e291dc', 88 | 403: '#f457e8', 89 | 408: '#e121d2', 90 | 401: '#b92dae', 91 | 405: '#f47ceb', 92 | 404: '#a82a9f', 93 | 400: '#b263c6', 94 | 301: '#6fa024', 95 | 302: '#87c32b', 96 | 307: '#a0d84c', 97 | 304: '#28b55c', 98 | 200: '#1a4f74', 99 | 206: '#27839f', 100 | 201: '#52adc9', 101 | 202: '#7c979f', 102 | 203: '#a5b8bd', 103 | 204: '#c1cdd1' 104 | }; 105 | 106 | this.schemes.colorwheel = [ 107 | '#b5b6a9', 108 | '#858772', 109 | '#785f43', 110 | '#96557e', 111 | '#4682b4', 112 | '#65b9ac', 113 | '#73c03a', 114 | '#cb513a' 115 | ].reverse(); 116 | 117 | this.schemes.cool = [ 118 | '#5e9d2f', 119 | '#73c03a', 120 | '#4682b4', 121 | '#7bc3b8', 122 | '#a9884e', 123 | '#c1b266', 124 | '#a47493', 125 | '#c09fb5' 126 | ]; 127 | 128 | this.schemes.munin = [ 129 | '#00cc00', 130 | '#0066b3', 131 | '#ff8000', 132 | '#ffcc00', 133 | '#330099', 134 | '#990099', 135 | '#ccff00', 136 | '#ff0000', 137 | '#808080', 138 | '#008f00', 139 | '#00487d', 140 | '#b35a00', 141 | '#b38f00', 142 | '#6b006b', 143 | '#8fb300', 144 | '#b30000', 145 | '#bebebe', 146 | '#80ff80', 147 | '#80c9ff', 148 | '#ffc080', 149 | '#ffe680', 150 | '#aa80ff', 151 | '#ee00cc', 152 | '#ff8080', 153 | '#666600', 154 | '#ffbfff', 155 | '#00ffcc', 156 | '#cc6699', 157 | '#999900' 158 | ]; 159 | }; 160 | -------------------------------------------------------------------------------- /html/scripts/mapplayer/mapplayer.js: -------------------------------------------------------------------------------- 1 | function MapPlayer(params) { 2 | //Init 3 | var frames = []; 4 | var curFrame = 0; 5 | var fps, 6 | decay, //how many frames a point will exist before fading out completely 7 | map, 8 | intervalId; 9 | var frameDrawCallback; 10 | 11 | setOptions(params); 12 | 13 | function setOptions(params) { 14 | if (params === undefined) { 15 | return; 16 | } 17 | 18 | if (params.fps === undefined && fps === undefined) { 19 | fps = 1; 20 | } else { 21 | fps = params.fps; 22 | if (intervalId) { 23 | //we are currently playing. 24 | this.pause(); 25 | this.play(); 26 | } 27 | } 28 | 29 | if (params.decay === undefined && decay === undefined) { 30 | decay = 5; 31 | } else { 32 | decay = params.decay; 33 | } 34 | 35 | if (params.onFrame !== undefined) { 36 | frameDrawCallback = params.onFrame; 37 | } 38 | } 39 | 40 | //Public Functions 41 | 42 | this.pushFrame = function(f) { 43 | frames.push(f); 44 | } 45 | 46 | this.drawNext = function() { 47 | if (frames.length == 0) { 48 | throw "No frames to draw"; 49 | } 50 | 51 | if (curFrame + 1 > frames.length - 1) { 52 | //if next frame is off the end, wipe and go to beginning 53 | this.clear(); 54 | curFrame = 0; 55 | this.draw(); 56 | return; 57 | } 58 | 59 | var dimPer = -1/decay; 60 | if (frames.length < decay) { 61 | dimPer = -1/frames.length; 62 | } 63 | 64 | var dimStart = curFrame - decay; 65 | if (dimStart < 0) { 66 | dimStart = 0; 67 | } 68 | 69 | for (var i = dimStart; i <= curFrame; i++) { 70 | frames[i].setRelativeOpacity(dimPer); 71 | } 72 | 73 | curFrame++; 74 | this.draw(); 75 | } 76 | 77 | this.draw = function() { 78 | if (frames.length == 0) { 79 | throw "No frames to draw"; 80 | } 81 | frames[curFrame].setOpacity(1); 82 | frames[curFrame].draw(); 83 | if (frameDrawCallback != undefined) { 84 | frameDrawCallback({ 85 | exData: frames[curFrame].exData, 86 | index: curFrame - 0, //math to avoid ref 87 | count: this.numFrames(), 88 | }); 89 | } 90 | } 91 | 92 | this.clear = function() { 93 | for (var i = 0; i < frames.length; i++) { 94 | frames[i].hide(); 95 | } 96 | } 97 | 98 | this.numFrames = function() { 99 | return frames.length - 1; 100 | } 101 | 102 | this.jump = function(i) { 103 | if (i > frames.length - 1) { 104 | throw "jump() exceeded frame count"; 105 | } 106 | this.clear(); 107 | curFrame = i; 108 | } 109 | 110 | this.play = function() { 111 | var that = this; 112 | intervalId = setInterval(function() {that.drawNext()}, 1000 / fps); 113 | } 114 | 115 | this.pause = function() { 116 | if (this.isPlaying()) { 117 | clearInterval(intervalId); 118 | intervalId = undefined; 119 | } 120 | } 121 | 122 | this.isPlaying = function() { 123 | if (intervalId != undefined) { 124 | return true; 125 | } 126 | } 127 | 128 | this.togglePlay = function() { 129 | if (this.isPlaying()) { 130 | this.pause(); 131 | } else { 132 | this.play(); 133 | } 134 | } 135 | 136 | } 137 | 138 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/example_06.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 29 | 30 |
31 |
32 |
33 |
34 |
35 | 36 | 84 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Fixtures.Time.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Fixtures.Time'); 2 | 3 | Rickshaw.Fixtures.Time = function() { 4 | 5 | var tzOffset = new Date().getTimezoneOffset() * 60; 6 | 7 | var self = this; 8 | 9 | this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; 10 | 11 | this.units = [ 12 | { 13 | name: 'decade', 14 | seconds: 86400 * 365.25 * 10, 15 | formatter: function(d) { return (parseInt(d.getUTCFullYear() / 10) * 10) } 16 | }, { 17 | name: 'year', 18 | seconds: 86400 * 365.25, 19 | formatter: function(d) { return d.getUTCFullYear() } 20 | }, { 21 | name: 'month', 22 | seconds: 86400 * 30.5, 23 | formatter: function(d) { return self.months[d.getUTCMonth()] } 24 | }, { 25 | name: 'week', 26 | seconds: 86400 * 7, 27 | formatter: function(d) { return self.formatDate(d) } 28 | }, { 29 | name: 'day', 30 | seconds: 86400, 31 | formatter: function(d) { return d.getUTCDate() } 32 | }, { 33 | name: '6 hour', 34 | seconds: 3600 * 6, 35 | formatter: function(d) { return self.formatTime(d) } 36 | }, { 37 | name: 'hour', 38 | seconds: 3600, 39 | formatter: function(d) { return self.formatTime(d) } 40 | }, { 41 | name: '15 minute', 42 | seconds: 60 * 15, 43 | formatter: function(d) { return self.formatTime(d) } 44 | }, { 45 | name: 'minute', 46 | seconds: 60, 47 | formatter: function(d) { return d.getUTCMinutes() } 48 | }, { 49 | name: '15 second', 50 | seconds: 15, 51 | formatter: function(d) { return d.getUTCSeconds() + 's' } 52 | }, { 53 | name: 'second', 54 | seconds: 1, 55 | formatter: function(d) { return d.getUTCSeconds() + 's' } 56 | } 57 | ]; 58 | 59 | this.unit = function(unitName) { 60 | return this.units.filter( function(unit) { return unitName == unit.name } ).shift(); 61 | }; 62 | 63 | this.formatDate = function(d) { 64 | return d.toUTCString().match(/, (\w+ \w+ \w+)/)[1]; 65 | }; 66 | 67 | this.formatTime = function(d) { 68 | return d.toUTCString().match(/(\d+:\d+):/)[1]; 69 | }; 70 | 71 | this.ceil = function(time, unit) { 72 | 73 | if (unit.name == 'month') { 74 | 75 | var nearFuture = new Date((time + unit.seconds - 1) * 1000); 76 | 77 | var rounded = new Date(0); 78 | rounded.setUTCFullYear(nearFuture.getUTCFullYear()); 79 | rounded.setUTCMonth(nearFuture.getUTCMonth()); 80 | rounded.setUTCDate(1); 81 | rounded.setUTCHours(0); 82 | rounded.setUTCMinutes(0); 83 | rounded.setUTCSeconds(0); 84 | rounded.setUTCMilliseconds(0); 85 | 86 | return rounded.getTime() / 1000; 87 | } 88 | 89 | if (unit.name == 'year') { 90 | 91 | var nearFuture = new Date((time + unit.seconds - 1) * 1000); 92 | 93 | var rounded = new Date(0); 94 | rounded.setUTCFullYear(nearFuture.getUTCFullYear()); 95 | rounded.setUTCMonth(0); 96 | rounded.setUTCDate(1); 97 | rounded.setUTCHours(0); 98 | rounded.setUTCMinutes(0); 99 | rounded.setUTCSeconds(0); 100 | rounded.setUTCMilliseconds(0); 101 | 102 | return rounded.getTime() / 1000; 103 | } 104 | 105 | return Math.ceil(time / unit.seconds) * unit.seconds; 106 | }; 107 | }; 108 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/hover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 40 | 41 | 42 | 43 |
44 |

45 |
46 |
47 | 48 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Renderer.Bar.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Renderer.Bar'); 2 | 3 | Rickshaw.Graph.Renderer.Bar = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { 4 | 5 | name: 'bar', 6 | 7 | defaults: function($super) { 8 | 9 | var defaults = Rickshaw.extend( $super(), { 10 | gapSize: 0.05, 11 | unstack: false 12 | } ); 13 | 14 | delete defaults.tension; 15 | return defaults; 16 | }, 17 | 18 | initialize: function($super, args) { 19 | args = args || {}; 20 | this.gapSize = args.gapSize || this.gapSize; 21 | $super(args); 22 | }, 23 | 24 | domain: function($super) { 25 | 26 | var domain = $super(); 27 | 28 | var frequentInterval = this._frequentInterval(); 29 | domain.x[1] += parseInt(frequentInterval.magnitude); 30 | 31 | return domain; 32 | }, 33 | 34 | barWidth: function() { 35 | 36 | var stackedData = this.graph.stackedData || this.graph.stackData(); 37 | var data = stackedData.slice(-1).shift(); 38 | 39 | var frequentInterval = this._frequentInterval(); 40 | var barWidth = this.graph.x(data[0].x + frequentInterval.magnitude * (1 - this.gapSize)); 41 | 42 | return barWidth; 43 | }, 44 | 45 | render: function() { 46 | 47 | var graph = this.graph; 48 | 49 | graph.vis.selectAll('*').remove(); 50 | 51 | var barWidth = this.barWidth(); 52 | var barXOffset = 0; 53 | 54 | var activeSeriesCount = graph.series.filter( function(s) { return !s.disabled; } ).length; 55 | var seriesBarWidth = this.unstack ? barWidth / activeSeriesCount : barWidth; 56 | 57 | var transform = function(d) { 58 | // add a matrix transform for negative values 59 | var matrix = [ 1, 0, 0, (d.y < 0 ? -1 : 1), 0, (d.y < 0 ? graph.y.magnitude(Math.abs(d.y)) * 2 : 0) ]; 60 | return "matrix(" + matrix.join(',') + ")"; 61 | }; 62 | 63 | graph.series.forEach( function(series) { 64 | 65 | if (series.disabled) return; 66 | 67 | var nodes = graph.vis.selectAll("path") 68 | .data(series.stack.filter( function(d) { return d.y !== null } )) 69 | .enter().append("svg:rect") 70 | .attr("x", function(d) { return graph.x(d.x) + barXOffset }) 71 | .attr("y", function(d) { return (graph.y(d.y0 + Math.abs(d.y))) * (d.y < 0 ? -1 : 1 ) }) 72 | .attr("width", seriesBarWidth) 73 | .attr("height", function(d) { return graph.y.magnitude(Math.abs(d.y)) }) 74 | .attr("transform", transform); 75 | 76 | Array.prototype.forEach.call(nodes[0], function(n) { 77 | n.setAttribute('fill', series.color); 78 | } ); 79 | 80 | if (this.unstack) barXOffset += seriesBarWidth; 81 | 82 | }, this ); 83 | }, 84 | 85 | _frequentInterval: function() { 86 | 87 | var stackedData = this.graph.stackedData || this.graph.stackData(); 88 | var data = stackedData.slice(-1).shift(); 89 | 90 | var intervalCounts = {}; 91 | 92 | for (var i = 0; i < data.length - 1; i++) { 93 | var interval = data[i + 1].x - data[i].x; 94 | intervalCounts[interval] = intervalCounts[interval] || 0; 95 | intervalCounts[interval]++; 96 | } 97 | 98 | var frequentInterval = { count: 0 }; 99 | 100 | Rickshaw.keys(intervalCounts).forEach( function(i) { 101 | if (frequentInterval.count < intervalCounts[i]) { 102 | 103 | frequentInterval = { 104 | count: intervalCounts[i], 105 | magnitude: i 106 | }; 107 | } 108 | } ); 109 | 110 | this._frequentInterval = function() { return frequentInterval }; 111 | 112 | return frequentInterval; 113 | } 114 | } ); 115 | 116 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Annotate.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Annotate'); 2 | 3 | Rickshaw.Graph.Annotate = function(args) { 4 | 5 | var graph = this.graph = args.graph; 6 | this.elements = { timeline: args.element }; 7 | 8 | var self = this; 9 | 10 | this.data = {}; 11 | 12 | this.elements.timeline.classList.add('rickshaw_annotation_timeline'); 13 | 14 | this.add = function(time, content, end_time) { 15 | self.data[time] = self.data[time] || {'boxes': []}; 16 | self.data[time].boxes.push({content: content, end: end_time}); 17 | }; 18 | 19 | this.update = function() { 20 | 21 | Rickshaw.keys(self.data).forEach( function(time) { 22 | 23 | var annotation = self.data[time]; 24 | var left = self.graph.x(time); 25 | 26 | if (left < 0 || left > self.graph.x.range()[1]) { 27 | if (annotation.element) { 28 | annotation.line.classList.add('offscreen'); 29 | annotation.element.style.display = 'none'; 30 | } 31 | 32 | annotation.boxes.forEach( function(box) { 33 | if ( box.rangeElement ) box.rangeElement.classList.add('offscreen'); 34 | }); 35 | 36 | return; 37 | } 38 | 39 | if (!annotation.element) { 40 | var element = annotation.element = document.createElement('div'); 41 | element.classList.add('annotation'); 42 | this.elements.timeline.appendChild(element); 43 | element.addEventListener('click', function(e) { 44 | element.classList.toggle('active'); 45 | annotation.line.classList.toggle('active'); 46 | annotation.boxes.forEach( function(box) { 47 | if ( box.rangeElement ) box.rangeElement.classList.toggle('active'); 48 | }); 49 | }, false); 50 | 51 | } 52 | 53 | annotation.element.style.left = left + 'px'; 54 | annotation.element.style.display = 'block'; 55 | 56 | annotation.boxes.forEach( function(box) { 57 | 58 | 59 | var element = box.element; 60 | 61 | if (!element) { 62 | element = box.element = document.createElement('div'); 63 | element.classList.add('content'); 64 | element.innerHTML = box.content; 65 | annotation.element.appendChild(element); 66 | 67 | annotation.line = document.createElement('div'); 68 | annotation.line.classList.add('annotation_line'); 69 | self.graph.element.appendChild(annotation.line); 70 | 71 | if ( box.end ) { 72 | box.rangeElement = document.createElement('div'); 73 | box.rangeElement.classList.add('annotation_range'); 74 | self.graph.element.appendChild(box.rangeElement); 75 | } 76 | 77 | } 78 | 79 | if ( box.end ) { 80 | 81 | var annotationRangeStart = left; 82 | var annotationRangeEnd = Math.min( self.graph.x(box.end), self.graph.x.range()[1] ); 83 | 84 | // annotation makes more sense at end 85 | if ( annotationRangeStart > annotationRangeEnd ) { 86 | annotationRangeEnd = left; 87 | annotationRangeStart = Math.max( self.graph.x(box.end), self.graph.x.range()[0] ); 88 | } 89 | 90 | var annotationRangeWidth = annotationRangeEnd - annotationRangeStart; 91 | 92 | box.rangeElement.style.left = annotationRangeStart + 'px'; 93 | box.rangeElement.style.width = annotationRangeWidth + 'px' 94 | 95 | box.rangeElement.classList.remove('offscreen'); 96 | } 97 | 98 | annotation.line.classList.remove('offscreen'); 99 | annotation.line.style.left = left + 'px'; 100 | } ); 101 | }, this ); 102 | }; 103 | 104 | this.graph.onUpdate( function() { self.update() } ); 105 | }; 106 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/js/extensions.js: -------------------------------------------------------------------------------- 1 | var RenderControls = function(args) { 2 | 3 | this.initialize = function() { 4 | 5 | this.element = args.element; 6 | this.graph = args.graph; 7 | this.settings = this.serialize(); 8 | 9 | this.inputs = { 10 | renderer: this.element.elements.renderer, 11 | interpolation: this.element.elements.interpolation, 12 | offset: this.element.elements.offset 13 | }; 14 | 15 | this.element.addEventListener('change', function(e) { 16 | 17 | this.settings = this.serialize(); 18 | 19 | if (e.target.name == 'renderer') { 20 | this.setDefaultOffset(e.target.value); 21 | } 22 | 23 | this.syncOptions(); 24 | this.settings = this.serialize(); 25 | 26 | var config = { 27 | renderer: this.settings.renderer, 28 | interpolation: this.settings.interpolation 29 | }; 30 | 31 | if (this.settings.offset == 'value') { 32 | config.unstack = true; 33 | config.offset = 'zero'; 34 | } else if (this.settings.offset == 'expand') { 35 | config.unstack = true; 36 | config.offset = this.settings.offset; 37 | } else { 38 | config.unstack = false; 39 | config.offset = this.settings.offset; 40 | } 41 | 42 | this.graph.configure(config); 43 | this.graph.render(); 44 | 45 | }.bind(this), false); 46 | } 47 | 48 | this.serialize = function() { 49 | 50 | var values = {}; 51 | var pairs = $(this.element).serializeArray(); 52 | 53 | pairs.forEach( function(pair) { 54 | values[pair.name] = pair.value; 55 | } ); 56 | 57 | return values; 58 | }; 59 | 60 | this.syncOptions = function() { 61 | 62 | var options = this.rendererOptions[this.settings.renderer]; 63 | 64 | Array.prototype.forEach.call(this.inputs.interpolation, function(input) { 65 | 66 | if (options.interpolation) { 67 | input.disabled = false; 68 | input.parentNode.classList.remove('disabled'); 69 | } else { 70 | input.disabled = true; 71 | input.parentNode.classList.add('disabled'); 72 | } 73 | }); 74 | 75 | Array.prototype.forEach.call(this.inputs.offset, function(input) { 76 | 77 | if (options.offset.filter( function(o) { return o == input.value } ).length) { 78 | input.disabled = false; 79 | input.parentNode.classList.remove('disabled'); 80 | 81 | } else { 82 | input.disabled = true; 83 | input.parentNode.classList.add('disabled'); 84 | } 85 | 86 | }.bind(this)); 87 | 88 | }; 89 | 90 | this.setDefaultOffset = function(renderer) { 91 | 92 | var options = this.rendererOptions[renderer]; 93 | 94 | if (options.defaults && options.defaults.offset) { 95 | 96 | Array.prototype.forEach.call(this.inputs.offset, function(input) { 97 | if (input.value == options.defaults.offset) { 98 | input.checked = true; 99 | } else { 100 | input.checked = false; 101 | } 102 | 103 | }.bind(this)); 104 | } 105 | }; 106 | 107 | this.rendererOptions = { 108 | 109 | area: { 110 | interpolation: true, 111 | offset: ['zero', 'wiggle', 'expand', 'value'], 112 | defaults: { offset: 'zero' } 113 | }, 114 | line: { 115 | interpolation: true, 116 | offset: ['expand', 'value'], 117 | defaults: { offset: 'value' } 118 | }, 119 | bar: { 120 | interpolation: false, 121 | offset: ['zero', 'wiggle', 'expand', 'value'], 122 | defaults: { offset: 'zero' } 123 | }, 124 | scatterplot: { 125 | interpolation: false, 126 | offset: ['value'], 127 | defaults: { offset: 'value' } 128 | } 129 | }; 130 | 131 | this.initialize(); 132 | }; 133 | 134 | -------------------------------------------------------------------------------- /clean_calls.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | $end_ts) { 67 | //continue; 68 | } 69 | 70 | $line = array_map('cleanVal', $line); 71 | /* 72 | Array 73 | ( 74 | [0] => Incident Number 75 | [1] => Type (code) 76 | [2] => Description (code description) 77 | [3] => Date/Time 78 | [4] => Address 79 | ) 80 | */ 81 | //associative tables in csv 'cuz I can 82 | $type_id = array_search($line[1], $types); 83 | if ($type_id === false) { 84 | $types[] = $line[1]; 85 | $type_id = count($types); 86 | fwrite($fp_type_out, "$type_id,{$line[1]},\"{$line[2]}\"\n"); 87 | $t++; 88 | } else { 89 | $type_id++; //array_search and count() use different bases for 'first item' 90 | } 91 | 92 | /* XXX This is what's needed to make the sense of incident types! 93 | unset($line[1]); 94 | $line[2] = $type_id; 95 | */ 96 | unset($line[2]); 97 | //$line[2] = $line[2]; 98 | 99 | $line[3] = date("Y-m-d H:i:s", $ts); 100 | 101 | //$line[4] is overloaded 102 | $loc = explode("\n", $line[4]); 103 | $line[4] = "\"{$loc[0]}\""; //address 104 | //$loc[1] is always 'Madison, WI' so not included 105 | $loc[2] = str_replace('(', '', $loc[2]); 106 | $loc[2] = str_replace(')', '', $loc[2]); 107 | $coords = explode(",", $loc[2]); 108 | $coords = array_map('trim', $coords); 109 | $line[5] = $coords[0]; //lat 110 | $line[6] = $coords[1]; //lng 111 | 112 | //make date the first column 113 | $temp = $line[0]; 114 | $line[0] = $line[3]; 115 | $line[3] = $temp; 116 | 117 | fwrite($fp_out, implode(',', $line) . "\n"); 118 | $j++; 119 | 120 | /* 121 | if ($j > 5) { 122 | die(); 123 | } 124 | */ 125 | 126 | echo "\r$i lines, $j in date range ..."; 127 | } 128 | echo "\n"; 129 | 130 | echo "Sorting by date ...\n"; 131 | fclose($fp_out); 132 | 133 | system("sort $output_file -o $output_file"); 134 | echo "Done!\n"; 135 | echo "Found $t incident types.\n"; 136 | 137 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tutorial/example_07.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 29 | 30 |
31 |
32 |
33 |
34 |
35 | 36 |
37 | 38 | 39 |
40 |
41 | 42 | 107 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 60 |

Rickshaw Examples

61 | 62 |
63 | 64 |
65 |

Getting Started

66 | 67 | Bare minimum to get a graph on the page with a couple of points. 68 | 69 |
70 |
71 | 72 |
73 | 74 |
75 |

Lines & Toggling

76 | 77 | Basic lines with a legend and x-axis ticks and labels. Toggle lines on and off by clicking checkmarks. 78 | 79 |
80 |
81 | 82 |
83 | 84 |
85 |

Interactive Real-Time Data

86 | 87 | Dig into continously updating data. Change line interpolations, zoom in on the x-axis, apply smoothing, stack and un-stack, drag and drop to re-order the stack, toggle data on and off. 88 | 89 |
90 |
91 | 92 |
93 | 94 |
95 |

Scatter Plot with Multiple Series

96 | 97 | Basic scatter plot with two overlapping series. 98 | 99 |
100 |
101 | 102 |
103 | 104 |
105 |

Stacked Bars with Deterministic Colors

106 | 107 | Requests per second by HTTP status as stacked bars. Colors come from a deterministic palette that can carry across graphs. 108 | 109 |
110 |
111 | 112 |
113 | 114 |
115 |

Color Schemes

116 | 117 | A number of color schemes are built in. You can specify your own too. 118 | 119 |
120 |
121 | 122 |
123 | 124 |
125 |

Interpolated Colors

126 | 127 | Interpolate color schemes for graphs with many series. 128 | 129 |
130 |
131 | 132 |
133 | 134 |
135 |

Data via AJAX / JSONP

136 | 137 | Ask Rickshaw to fetch data via AJAX, rather than specifying in the constructor. There's a JSONP impelementation too. 138 | 139 |
140 |
141 | 142 |
143 | 144 |
145 |

Y-Axis Tick Marks and Grid Lines

146 | 147 | Draw Y-Axis tick marks and grid lines with abbreviated numbers. 148 | 149 |
150 |
151 | 152 |
153 |
154 | 155 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Series.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Series'); 2 | 3 | Rickshaw.Series = Rickshaw.Class.create( Array, { 4 | 5 | initialize: function (data, palette, options) { 6 | 7 | options = options || {} 8 | 9 | this.palette = new Rickshaw.Color.Palette(palette); 10 | 11 | this.timeBase = typeof(options.timeBase) === 'undefined' ? 12 | Math.floor(new Date().getTime() / 1000) : 13 | options.timeBase; 14 | 15 | var timeInterval = typeof(options.timeInterval) == 'undefined' ? 16 | 1000 : 17 | options.timeInterval; 18 | 19 | this.setTimeInterval(timeInterval); 20 | 21 | if (data && (typeof(data) == "object") && (data instanceof Array)) { 22 | data.forEach( function(item) { this.addItem(item) }, this ); 23 | } 24 | }, 25 | 26 | addItem: function(item) { 27 | 28 | if (typeof(item.name) === 'undefined') { 29 | throw('addItem() needs a name'); 30 | } 31 | 32 | item.color = (item.color || this.palette.color(item.name)); 33 | item.data = (item.data || []); 34 | 35 | // backfill, if necessary 36 | if ((item.data.length == 0) && this.length && (this.getIndex() > 0)) { 37 | this[0].data.forEach( function(plot) { 38 | item.data.push({ x: plot.x, y: 0 }); 39 | } ); 40 | } else if (item.data.length == 0) { 41 | item.data.push({ x: this.timeBase - (this.timeInterval || 0), y: 0 }); 42 | } 43 | 44 | this.push(item); 45 | 46 | if (this.legend) { 47 | this.legend.addLine(this.itemByName(item.name)); 48 | } 49 | }, 50 | 51 | addData: function(data) { 52 | 53 | var index = this.getIndex(); 54 | 55 | Rickshaw.keys(data).forEach( function(name) { 56 | if (! this.itemByName(name)) { 57 | this.addItem({ name: name }); 58 | } 59 | }, this ); 60 | 61 | this.forEach( function(item) { 62 | item.data.push({ 63 | x: (index * this.timeInterval || 1) + this.timeBase, 64 | y: (data[item.name] || 0) 65 | }); 66 | }, this ); 67 | }, 68 | 69 | getIndex: function () { 70 | return (this[0] && this[0].data && this[0].data.length) ? this[0].data.length : 0; 71 | }, 72 | 73 | itemByName: function(name) { 74 | 75 | for (var i = 0; i < this.length; i++) { 76 | if (this[i].name == name) 77 | return this[i]; 78 | } 79 | }, 80 | 81 | setTimeInterval: function(iv) { 82 | this.timeInterval = iv / 1000; 83 | }, 84 | 85 | setTimeBase: function (t) { 86 | this.timeBase = t; 87 | }, 88 | 89 | dump: function() { 90 | 91 | var data = { 92 | timeBase: this.timeBase, 93 | timeInterval: this.timeInterval, 94 | items: [] 95 | }; 96 | 97 | this.forEach( function(item) { 98 | 99 | var newItem = { 100 | color: item.color, 101 | name: item.name, 102 | data: [] 103 | }; 104 | 105 | item.data.forEach( function(plot) { 106 | newItem.data.push({ x: plot.x, y: plot.y }); 107 | } ); 108 | 109 | data.items.push(newItem); 110 | } ); 111 | 112 | return data; 113 | }, 114 | 115 | load: function(data) { 116 | 117 | if (data.timeInterval) { 118 | this.timeInterval = data.timeInterval; 119 | } 120 | 121 | if (data.timeBase) { 122 | this.timeBase = data.timeBase; 123 | } 124 | 125 | if (data.items) { 126 | data.items.forEach( function(item) { 127 | this.push(item); 128 | if (this.legend) { 129 | this.legend.addLine(this.itemByName(item.name)); 130 | } 131 | 132 | }, this ); 133 | } 134 | } 135 | } ); 136 | 137 | Rickshaw.Series.zeroFill = function(series) { 138 | Rickshaw.Series.fill(series, 0); 139 | }; 140 | 141 | Rickshaw.Series.fill = function(series, fill) { 142 | 143 | var x; 144 | var i = 0; 145 | 146 | var data = series.map( function(s) { return s.data } ); 147 | 148 | while ( i < Math.max.apply(null, data.map( function(d) { return d.length } )) ) { 149 | 150 | x = Math.min.apply( null, 151 | data 152 | .filter(function(d) { return d[i] }) 153 | .map(function(d) { return d[i].x }) 154 | ); 155 | 156 | data.forEach( function(d) { 157 | if (!d[i] || d[i].x != x) { 158 | d.splice(i, 0, { x: x, y: fill }); 159 | } 160 | } ); 161 | 162 | i++; 163 | } 164 | }; 165 | 166 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Renderer.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace("Rickshaw.Graph.Renderer"); 2 | 3 | Rickshaw.Graph.Renderer = Rickshaw.Class.create( { 4 | 5 | initialize: function(args) { 6 | this.graph = args.graph; 7 | this.tension = args.tension || this.tension; 8 | this.graph.unstacker = this.graph.unstacker || new Rickshaw.Graph.Unstacker( { graph: this.graph } ); 9 | this.configure(args); 10 | }, 11 | 12 | seriesPathFactory: function() { 13 | //implement in subclass 14 | }, 15 | 16 | seriesStrokeFactory: function() { 17 | // implement in subclass 18 | }, 19 | 20 | defaults: function() { 21 | return { 22 | tension: 0.8, 23 | strokeWidth: 2, 24 | unstack: true, 25 | padding: { top: 0.01, right: 0, bottom: 0.01, left: 0 }, 26 | stroke: false, 27 | fill: false 28 | }; 29 | }, 30 | 31 | domain: function() { 32 | 33 | var values = { xMin: [], xMax: [], y: [] }; 34 | 35 | var stackedData = this.graph.stackedData || this.graph.stackData(); 36 | var firstPoint = stackedData[0][0]; 37 | 38 | var xMin = firstPoint.x; 39 | var xMax = firstPoint.x 40 | 41 | var yMin = firstPoint.y + firstPoint.y0; 42 | var yMax = firstPoint.y + firstPoint.y0; 43 | 44 | stackedData.forEach( function(series) { 45 | 46 | series.forEach( function(d) { 47 | 48 | var y = d.y + d.y0; 49 | 50 | if (y < yMin) yMin = y; 51 | if (y > yMax) yMax = y; 52 | } ); 53 | 54 | if (series[0].x < xMin) xMin = series[0].x; 55 | if (series[series.length - 1].x > xMax) xMax = series[series.length - 1].x; 56 | } ); 57 | 58 | xMin -= (xMax - xMin) * this.padding.left; 59 | xMax += (xMax - xMin) * this.padding.right; 60 | 61 | yMin = this.graph.min === 'auto' ? yMin : this.graph.min || 0; 62 | yMax = this.graph.max || yMax; 63 | 64 | if (this.graph.min === 'auto' || yMin < 0) { 65 | yMin -= (yMax - yMin) * this.padding.bottom; 66 | } 67 | 68 | if (this.graph.max === undefined) { 69 | yMax += (yMax - yMin) * this.padding.top; 70 | } 71 | 72 | return { x: [xMin, xMax], y: [yMin, yMax] }; 73 | }, 74 | 75 | render: function() { 76 | 77 | var graph = this.graph; 78 | 79 | graph.vis.selectAll('*').remove(); 80 | 81 | var nodes = graph.vis.selectAll("path") 82 | .data(this.graph.stackedData) 83 | .enter().append("svg:path") 84 | .attr("d", this.seriesPathFactory()); 85 | 86 | var i = 0; 87 | graph.series.forEach( function(series) { 88 | if (series.disabled) return; 89 | series.path = nodes[0][i++]; 90 | this._styleSeries(series); 91 | }, this ); 92 | }, 93 | 94 | _styleSeries: function(series) { 95 | 96 | var fill = this.fill ? series.color : 'none'; 97 | var stroke = this.stroke ? series.color : 'none'; 98 | 99 | series.path.setAttribute('fill', fill); 100 | series.path.setAttribute('stroke', stroke); 101 | series.path.setAttribute('stroke-width', this.strokeWidth); 102 | series.path.setAttribute('class', series.className); 103 | }, 104 | 105 | configure: function(args) { 106 | 107 | args = args || {}; 108 | 109 | Rickshaw.keys(this.defaults()).forEach( function(key) { 110 | 111 | if (!args.hasOwnProperty(key)) { 112 | this[key] = this[key] || this.graph[key] || this.defaults()[key]; 113 | return; 114 | } 115 | 116 | if (typeof this.defaults()[key] == 'object') { 117 | 118 | Rickshaw.keys(this.defaults()[key]).forEach( function(k) { 119 | 120 | this[key][k] = 121 | args[key][k] !== undefined ? args[key][k] : 122 | this[key][k] !== undefined ? this[key][k] : 123 | this.defaults()[key][k]; 124 | }, this ); 125 | 126 | } else { 127 | this[key] = 128 | args[key] !== undefined ? args[key] : 129 | this[key] !== undefined ? this[key] : 130 | this.graph[key] !== undefined ? this.graph[key] : 131 | this.defaults()[key]; 132 | } 133 | 134 | }, this ); 135 | }, 136 | 137 | setStrokeWidth: function(strokeWidth) { 138 | if (strokeWidth !== undefined) { 139 | this.strokeWidth = strokeWidth; 140 | } 141 | }, 142 | 143 | setTension: function(tension) { 144 | if (tension !== undefined) { 145 | this.tension = tension; 146 | } 147 | } 148 | } ); 149 | 150 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Compat.ClassList.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Compat.ClassList'); 2 | 3 | Rickshaw.Compat.ClassList = function() { 4 | 5 | /* adapted from http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ 6 | 7 | if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { 8 | 9 | (function (view) { 10 | 11 | "use strict"; 12 | 13 | var 14 | classListProp = "classList" 15 | , protoProp = "prototype" 16 | , elemCtrProto = (view.HTMLElement || view.Element)[protoProp] 17 | , objCtr = Object 18 | , strTrim = String[protoProp].trim || function () { 19 | return this.replace(/^\s+|\s+$/g, ""); 20 | } 21 | , arrIndexOf = Array[protoProp].indexOf || function (item) { 22 | var 23 | i = 0 24 | , len = this.length 25 | ; 26 | for (; i < len; i++) { 27 | if (i in this && this[i] === item) { 28 | return i; 29 | } 30 | } 31 | return -1; 32 | } 33 | // Vendors: please allow content code to instantiate DOMExceptions 34 | , DOMEx = function (type, message) { 35 | this.name = type; 36 | this.code = DOMException[type]; 37 | this.message = message; 38 | } 39 | , checkTokenAndGetIndex = function (classList, token) { 40 | if (token === "") { 41 | throw new DOMEx( 42 | "SYNTAX_ERR" 43 | , "An invalid or illegal string was specified" 44 | ); 45 | } 46 | if (/\s/.test(token)) { 47 | throw new DOMEx( 48 | "INVALID_CHARACTER_ERR" 49 | , "String contains an invalid character" 50 | ); 51 | } 52 | return arrIndexOf.call(classList, token); 53 | } 54 | , ClassList = function (elem) { 55 | var 56 | trimmedClasses = strTrim.call(elem.className) 57 | , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] 58 | , i = 0 59 | , len = classes.length 60 | ; 61 | for (; i < len; i++) { 62 | this.push(classes[i]); 63 | } 64 | this._updateClassName = function () { 65 | elem.className = this.toString(); 66 | }; 67 | } 68 | , classListProto = ClassList[protoProp] = [] 69 | , classListGetter = function () { 70 | return new ClassList(this); 71 | } 72 | ; 73 | // Most DOMException implementations don't allow calling DOMException's toString() 74 | // on non-DOMExceptions. Error's toString() is sufficient here. 75 | DOMEx[protoProp] = Error[protoProp]; 76 | classListProto.item = function (i) { 77 | return this[i] || null; 78 | }; 79 | classListProto.contains = function (token) { 80 | token += ""; 81 | return checkTokenAndGetIndex(this, token) !== -1; 82 | }; 83 | classListProto.add = function (token) { 84 | token += ""; 85 | if (checkTokenAndGetIndex(this, token) === -1) { 86 | this.push(token); 87 | this._updateClassName(); 88 | } 89 | }; 90 | classListProto.remove = function (token) { 91 | token += ""; 92 | var index = checkTokenAndGetIndex(this, token); 93 | if (index !== -1) { 94 | this.splice(index, 1); 95 | this._updateClassName(); 96 | } 97 | }; 98 | classListProto.toggle = function (token) { 99 | token += ""; 100 | if (checkTokenAndGetIndex(this, token) === -1) { 101 | this.add(token); 102 | } else { 103 | this.remove(token); 104 | } 105 | }; 106 | classListProto.toString = function () { 107 | return this.join(" "); 108 | }; 109 | 110 | if (objCtr.defineProperty) { 111 | var classListPropDesc = { 112 | get: classListGetter 113 | , enumerable: true 114 | , configurable: true 115 | }; 116 | try { 117 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 118 | } catch (ex) { // IE 8 doesn't support enumerable:true 119 | if (ex.number === -0x7FF5EC54) { 120 | classListPropDesc.enumerable = false; 121 | objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 122 | } 123 | } 124 | } else if (objCtr[protoProp].__defineGetter__) { 125 | elemCtrProto.__defineGetter__(classListProp, classListGetter); 126 | } 127 | 128 | }(window)); 129 | 130 | } 131 | }; 132 | 133 | if ( (typeof RICKSHAW_NO_COMPAT !== "undefined" && !RICKSHAW_NO_COMPAT) || typeof RICKSHAW_NO_COMPAT === "undefined") { 134 | new Rickshaw.Compat.ClassList(); 135 | } 136 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/css/extensions.css: -------------------------------------------------------------------------------- 1 | div, span, p, td { 2 | font-family: Arial, sans-serif; 3 | } 4 | #content { 5 | width: 1200px; 6 | } 7 | #chart { 8 | display: inline-block; 9 | } 10 | #chart path { 11 | -webkit-transition: opacity 0.2s linear; 12 | } 13 | #slider { 14 | margin-top: 10px; 15 | } 16 | #legend { 17 | display: inline-block; 18 | position: relative; 19 | left: 8px; 20 | } 21 | #legend_container { 22 | position: absolute; 23 | right: 0; 24 | bottom: 26px; 25 | width: 0; 26 | } 27 | #chart_container { 28 | position: relative; 29 | display: inline-block; 30 | } 31 | #smoother { 32 | margin: 0 0 10px 16px; 33 | width: 100px; 34 | } 35 | .rickshaw_graph .detail { 36 | left: -1000; 37 | } 38 | #chart { 39 | border: 1px solid #f0f0f0; 40 | } 41 | #side_panel { 42 | padding: 0 20px 20px 0; 43 | width: 240px; 44 | display: inline-block; 45 | vertical-align: top; 46 | } 47 | #side_panel section { 48 | color: #505050; 49 | font-size: 12px; 50 | } 51 | #side_panel section h6 { 52 | margin: 0 0 1em 0; 53 | font-size: 12px; 54 | font-weight: normal; 55 | } 56 | #side_panel .ui-slider-horizontal { 57 | height: 1px !important; 58 | border-color: #e0e0e0; 59 | margin-bottom: 10px; 60 | } 61 | #side_panel .ui-slider-handle { 62 | border-color: #a0a0a0; 63 | height: 9px !important; 64 | width: 9px !important; 65 | top: -5px !important; 66 | border-radius: 6px; 67 | outline: none; 68 | cursor: pointer; 69 | } 70 | #legend { 71 | background-color: white; 72 | margin-left: 0; 73 | padding: 0; 74 | left: 0; 75 | } 76 | #legend .label { 77 | color: #404040; 78 | } 79 | #legend .action { 80 | color: black; 81 | opacity: 0.5; 82 | } 83 | #legend ul { 84 | padding: 0; 85 | } 86 | h1 { 87 | font-family: Franklin Gothic Medium, UnDotum, Helvetica, Arial; 88 | font-weight: normal; 89 | font-size: 20px; 90 | } 91 | section { 92 | border: none; 93 | border-top: 1px solid #eaeaea; 94 | padding: 15px 0; 95 | } 96 | #smoother { 97 | margin: 5px 0 0 10px; 98 | width: 90%; 99 | } 100 | label.disabled { 101 | opacity: 0.4; 102 | } 103 | #renderer_form.toggler { 104 | display: block; 105 | margin: 0; 106 | } 107 | #renderer_form.toggler input[type=radio]:checked { 108 | outline: 2px solid steelblue; 109 | } 110 | #renderer_form.toggler input[type=radio] { 111 | -moz-appearance: button; 112 | background: white; 113 | margin: 0 7px; 114 | width: 39px; 115 | height: 26px; 116 | position: absolute; 117 | } 118 | #renderer_form.toggler label { 119 | display: inline-block; 120 | padding: 0; 121 | width: 39px; 122 | padding-top: 27px; 123 | text-align: center; 124 | font-size: 10px; 125 | color: #808080; 126 | background-repeat: no-repeat; 127 | position: relative; 128 | margin: 0 7px; 129 | cursor: pointer; 130 | } 131 | #interpolation_form, 132 | #offset_form { 133 | vertical-align: top; 134 | display: inline-block; 135 | width: 45%; 136 | } 137 | #interpolation_form label, 138 | #offset_form label { 139 | display: block; 140 | } 141 | label[for=area] { 142 | background: url(../images/om_stack.png); 143 | } 144 | label[for=line] { 145 | background: url(../images/om_lines.png); 146 | } 147 | label[for=bar] { 148 | background: url(../images/om_bar.png); 149 | } 150 | label[for=scatter] { 151 | background: url(../images/om_scatter.png); 152 | } 153 | #offset_form label, 154 | #interpolation_form label { 155 | background-repeat: no-repeat; 156 | background-position: 2em center; 157 | cursor: pointer; 158 | cursor: hand; 159 | } 160 | #offset_form label span, 161 | #interpolation_form label span { 162 | padding-left: 36px; 163 | } 164 | label[for=stack] { 165 | background-image: url(../images/offset_stack.png); 166 | } 167 | label[for=pct] { 168 | background-image: url(../images/offset_pct.png); 169 | } 170 | label[for=stream] { 171 | background-image: url(../images/offset_stream.png); 172 | } 173 | label[for=value] { 174 | background-image: url(../images/offset_value.png); 175 | } 176 | label[for=cardinal] { 177 | background-image: url(../images/interp_cardinal.png); 178 | } 179 | label[for=linear] { 180 | background-image: url(../images/interp_linear.png); 181 | } 182 | label[for=step] { 183 | background-image: url(../images/interp_step.png); 184 | } 185 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/tests/Rickshaw.Series.js: -------------------------------------------------------------------------------- 1 | var Rickshaw = require("../rickshaw"); 2 | 3 | function seriesData() { 4 | return { 5 | name: 'series1', 6 | data: [ {x: 0, y: 20}, {x: 1, y: 21}, { x: 2, y: 15} ], 7 | color: 'red' 8 | }; 9 | } 10 | 11 | exports.basic = function(test) { 12 | 13 | test.equal(typeof Rickshaw.Series, 'function', 'Rickshaw.Series is a function'); 14 | test.done(); 15 | }; 16 | 17 | exports.initialize = function(test) { 18 | 19 | var series = new Rickshaw.Series([seriesData()], 'spectrum2001', {timeBase: 0}); 20 | 21 | test.ok(series instanceof Rickshaw.Series); 22 | test.ok(series instanceof Array); 23 | 24 | test.deepEqual( 25 | series[0].data, 26 | [ { x: 0, y: 20 }, 27 | { x: 1, y: 21 }, 28 | { x: 2, y: 15 } ], 29 | 'data made it in as we expect' 30 | ); 31 | 32 | test.done(); 33 | }; 34 | 35 | exports.addItem = function(test) { 36 | 37 | var series = new Rickshaw.Series([seriesData()], 'spectrum2001', {timeBase: 0}); 38 | 39 | series.addItem( { 40 | name: 'series2', 41 | data: [ {x: 0, y: 10}, {x: 1, y: 13}, {x: 2, y: 12} ] 42 | } ); 43 | 44 | test.equal(series.length, 2, 'series has two items'); 45 | test.done(); 46 | } 47 | 48 | exports.addData = function(test) { 49 | 50 | var series = new Rickshaw.Series([seriesData()], 'spectrum2001', {timeBase: 0}); 51 | 52 | series.addData( { series1: 22, } ); 53 | 54 | test.equal(series[0].data.length, 4, 'first series has four data points'); 55 | test.equal(series[0].data[3].y, 22, 'first series last data point made it in'); 56 | 57 | series.addData( { series1: 29, series2: 57 } ); 58 | 59 | test.equal(series[0].data[4].y, 29, 'first series has a new data point'); 60 | 61 | test.equal(series[1].data.length, 5, 'second series has five data points'); 62 | test.equal(series[1].data[4].y, 57, 'second series last data point made it in'); 63 | 64 | test.done(); 65 | } 66 | 67 | exports.itemByName = function(test) { 68 | 69 | var series = new Rickshaw.Series([seriesData()], 'spectrum2001', {timeBase: 0}); 70 | 71 | test.strictEqual(series.itemByName('series1'), series[0], 'we get the right item'); 72 | test.strictEqual(series.itemByName('series1').name, 'series1', 'item by name is right'); 73 | test.done(); 74 | } 75 | 76 | exports.dump = function(test) { 77 | 78 | var series = new Rickshaw.Series([seriesData()], 'spectrum2001', {timeBase: 0}); 79 | 80 | test.deepEqual( 81 | series.dump(), 82 | { 83 | "timeBase":0, 84 | "timeInterval": 1, 85 | "items":[{ 86 | "color":"red", 87 | "name":"series1", 88 | "data":[{"x":0,"y":20},{"x":1,"y":21},{"x":2,"y":15}] 89 | }] 90 | }, 91 | 'dumped series matches' 92 | ); 93 | 94 | test.done(); 95 | } 96 | 97 | exports.zeroFill = function(test) { 98 | 99 | var series = [ 100 | { name: "series1", data: [{ x: 1, y: 22 }, { x: 3, y: 29 }] }, 101 | { name: "series2", data: [{ x: 2, y: 49 }] } 102 | ]; 103 | 104 | Rickshaw.Series.zeroFill(series); 105 | 106 | var expectedSeries = [ 107 | { name: "series1", data: [{ x: 1, y: 22 }, { x: 2, y: 0 }, { x: 3, y: 29 }] }, 108 | { name: "series2", data: [{ x: 1, y: 0}, { x: 2, y: 49 }, { x: 3, y: 0 }] } 109 | ]; 110 | 111 | test.deepEqual(series, expectedSeries, "zero fill fills in zeros"); 112 | test.done(); 113 | } 114 | 115 | exports.nullFill = function(test) { 116 | 117 | var series = [ 118 | { name: "series1", data: [{ x: 1, y: 22 }, { x: 3, y: 29 }] }, 119 | { name: "series2", data: [{ x: 2, y: 49 }] } 120 | ]; 121 | 122 | Rickshaw.Series.fill(series, null); 123 | 124 | var expectedSeries = [ 125 | { name: "series1", data: [{ x: 1, y: 22 }, { x: 2, y: null }, { x: 3, y: 29 }] }, 126 | { name: "series2", data: [{ x: 1, y: null}, { x: 2, y: 49 }, { x: 3, y: null }] } 127 | ]; 128 | 129 | test.deepEqual(series, expectedSeries, "null fill fills in nulls"); 130 | test.done(); 131 | } 132 | 133 | exports.load = function(test) { 134 | 135 | var series = new Rickshaw.Series([], 'spectrum2001', {timeBase: 0}); 136 | 137 | series.load({ 138 | items: [ seriesData() ], 139 | timeInterval: 3, 140 | timeBase: 0 141 | }); 142 | 143 | delete series.palette; 144 | 145 | test.equal(series.timeBase, 0, 'time base made it in'); 146 | test.equal(series.timeInterval, 3, 'time interval made it in'); 147 | test.equal(series[0].data.length, 3, 'series data made it in'); 148 | test.done(); 149 | } 150 | /* 151 | */ 152 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/css/graph.css: -------------------------------------------------------------------------------- 1 | /* graph */ 2 | 3 | .rickshaw_graph { 4 | position: relative; 5 | } 6 | .rickshaw_graph svg { 7 | display: block; 8 | overflow: hidden; 9 | } 10 | 11 | /* ticks */ 12 | 13 | .rickshaw_graph .x_tick { 14 | position: absolute; 15 | top: 0; 16 | bottom: 0; 17 | width: 0px; 18 | border-left: 1px dotted rgba(0, 0, 0, 0.2); 19 | pointer-events: none; 20 | } 21 | .rickshaw_graph .x_tick .title { 22 | position: absolute; 23 | font-size: 12px; 24 | font-family: Arial, sans-serif; 25 | opacity: 0.5; 26 | white-space: nowrap; 27 | margin-left: 3px; 28 | bottom: 1px; 29 | } 30 | 31 | /* annotations */ 32 | 33 | .rickshaw_annotation_timeline { 34 | height: 1px; 35 | border-top: 1px solid #e0e0e0; 36 | margin-top: 10px; 37 | position: relative; 38 | } 39 | .rickshaw_annotation_timeline .annotation { 40 | position: absolute; 41 | height: 6px; 42 | width: 6px; 43 | margin-left: -2px; 44 | top: -3px; 45 | border-radius: 5px; 46 | background-color: rgba(0, 0, 0, 0.25); 47 | } 48 | .rickshaw_graph .annotation_line { 49 | position: absolute; 50 | top: 0; 51 | bottom: -6px; 52 | width: 0px; 53 | border-left: 2px solid rgba(0, 0, 0, 0.3); 54 | display: none; 55 | } 56 | .rickshaw_graph .annotation_line.active { 57 | display: block; 58 | } 59 | 60 | .rickshaw_graph .annotation_range { 61 | background: rgba(0, 0, 0, 0.1); 62 | display: none; 63 | position: absolute; 64 | top: 0; 65 | bottom: -6px; 66 | z-index: -10; 67 | } 68 | .rickshaw_graph .annotation_range.active { 69 | display: block; 70 | } 71 | .rickshaw_graph .annotation_range.active.offscreen { 72 | display: none; 73 | } 74 | 75 | .rickshaw_annotation_timeline .annotation .content { 76 | background: white; 77 | color: black; 78 | opacity: 0.9; 79 | padding: 5px 5px; 80 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.8); 81 | border-radius: 3px; 82 | position: relative; 83 | z-index: 20; 84 | font-size: 12px; 85 | padding: 6px 8px 8px; 86 | top: 18px; 87 | left: -11px; 88 | width: 160px; 89 | display: none; 90 | cursor: pointer; 91 | } 92 | .rickshaw_annotation_timeline .annotation .content:before { 93 | content: "\25b2"; 94 | position: absolute; 95 | top: -11px; 96 | color: white; 97 | text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.8); 98 | } 99 | .rickshaw_annotation_timeline .annotation.active, 100 | .rickshaw_annotation_timeline .annotation:hover { 101 | background-color: rgba(0, 0, 0, 0.8); 102 | cursor: none; 103 | } 104 | .rickshaw_annotation_timeline .annotation .content:hover { 105 | z-index: 50; 106 | } 107 | .rickshaw_annotation_timeline .annotation.active .content { 108 | display: block; 109 | } 110 | .rickshaw_annotation_timeline .annotation:hover .content { 111 | display: block; 112 | z-index: 50; 113 | } 114 | .rickshaw_graph .y_axis { 115 | fill: none; 116 | } 117 | .rickshaw_graph .y_ticks .tick { 118 | stroke: rgba(0, 0, 0, 0.16); 119 | stroke-width: 2px; 120 | shape-rendering: crisp-edges; 121 | pointer-events: none; 122 | } 123 | .rickshaw_graph .y_grid .tick { 124 | z-index: -1; 125 | stroke: rgba(0, 0, 0, 0.20); 126 | stroke-width: 1px; 127 | stroke-dasharray: 1 1; 128 | } 129 | .rickshaw_graph .y_grid path { 130 | fill: none; 131 | stroke: none; 132 | } 133 | .rickshaw_graph .y_ticks path { 134 | fill: none; 135 | stroke: #808080; 136 | } 137 | .rickshaw_graph .y_ticks text { 138 | opacity: 0.5; 139 | font-size: 12px; 140 | pointer-events: none; 141 | } 142 | .rickshaw_graph .x_tick.glow .title, 143 | .rickshaw_graph .y_ticks.glow text { 144 | fill: black; 145 | color: black; 146 | text-shadow: 147 | -1px 1px 0 rgba(255, 255, 255, 0.1), 148 | 1px -1px 0 rgba(255, 255, 255, 0.1), 149 | 1px 1px 0 rgba(255, 255, 255, 0.1), 150 | 0px 1px 0 rgba(255, 255, 255, 0.1), 151 | 0px -1px 0 rgba(255, 255, 255, 0.1), 152 | 1px 0px 0 rgba(255, 255, 255, 0.1), 153 | -1px 0px 0 rgba(255, 255, 255, 0.1), 154 | -1px -1px 0 rgba(255, 255, 255, 0.1); 155 | } 156 | .rickshaw_graph .x_tick.inverse .title, 157 | .rickshaw_graph .y_ticks.inverse text { 158 | fill: white; 159 | color: white; 160 | text-shadow: 161 | -1px 1px 0 rgba(0, 0, 0, 0.8), 162 | 1px -1px 0 rgba(0, 0, 0, 0.8), 163 | 1px 1px 0 rgba(0, 0, 0, 0.8), 164 | 0px 1px 0 rgba(0, 0, 0, 0.8), 165 | 0px -1px 0 rgba(0, 0, 0, 0.8), 166 | 1px 0px 0 rgba(0, 0, 0, 0.8), 167 | -1px 0px 0 rgba(0, 0, 0, 0.8), 168 | -1px -1px 0 rgba(0, 0, 0, 0.8); 169 | } 170 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.Behavior.Series.Toggle.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Toggle'); 2 | 3 | Rickshaw.Graph.Behavior.Series.Toggle = function(args) { 4 | 5 | this.graph = args.graph; 6 | this.legend = args.legend; 7 | 8 | var self = this; 9 | 10 | this.addAnchor = function(line) { 11 | var anchor = document.createElement('a'); 12 | anchor.innerHTML = '✔'; 13 | anchor.classList.add('action'); 14 | line.element.insertBefore(anchor, line.element.firstChild); 15 | 16 | anchor.onclick = function(e) { 17 | if (line.series.disabled) { 18 | line.series.enable(); 19 | line.element.classList.remove('disabled'); 20 | } else { 21 | line.series.disable(); 22 | line.element.classList.add('disabled'); 23 | } 24 | } 25 | 26 | var label = line.element.getElementsByTagName('span')[0]; 27 | label.onclick = function(e){ 28 | 29 | var disableAllOtherLines = line.series.disabled; 30 | if ( ! disableAllOtherLines ) { 31 | for ( var i = 0; i < self.legend.lines.length; i++ ) { 32 | var l = self.legend.lines[i]; 33 | if ( line.series === l.series ) { 34 | // noop 35 | } else if ( l.series.disabled ) { 36 | // noop 37 | } else { 38 | disableAllOtherLines = true; 39 | break; 40 | } 41 | } 42 | } 43 | 44 | // show all or none 45 | if ( disableAllOtherLines ) { 46 | 47 | // these must happen first or else we try ( and probably fail ) to make a no line graph 48 | line.series.enable(); 49 | line.element.classList.remove('disabled'); 50 | 51 | self.legend.lines.forEach(function(l){ 52 | if ( line.series === l.series ) { 53 | // noop 54 | } else { 55 | l.series.disable(); 56 | l.element.classList.add('disabled'); 57 | } 58 | }); 59 | 60 | } else { 61 | 62 | self.legend.lines.forEach(function(l){ 63 | l.series.enable(); 64 | l.element.classList.remove('disabled'); 65 | }); 66 | 67 | } 68 | 69 | }; 70 | 71 | }; 72 | 73 | if (this.legend) { 74 | 75 | $(this.legend.list).sortable( { 76 | start: function(event, ui) { 77 | ui.item.bind('no.onclick', 78 | function(event) { 79 | event.preventDefault(); 80 | } 81 | ); 82 | }, 83 | stop: function(event, ui) { 84 | setTimeout(function(){ 85 | ui.item.unbind('no.onclick'); 86 | }, 250); 87 | } 88 | }) 89 | 90 | this.legend.lines.forEach( function(l) { 91 | self.addAnchor(l); 92 | } ); 93 | } 94 | 95 | this._addBehavior = function() { 96 | 97 | this.graph.series.forEach( function(s) { 98 | 99 | s.disable = function() { 100 | 101 | if (self.graph.series.length <= 1) { 102 | throw('only one series left'); 103 | } 104 | 105 | s.disabled = true; 106 | self.graph.update(); 107 | }; 108 | 109 | s.enable = function() { 110 | s.disabled = false; 111 | self.graph.update(); 112 | }; 113 | } ); 114 | }; 115 | this._addBehavior(); 116 | 117 | this.updateBehaviour = function () { this._addBehavior() }; 118 | 119 | }; 120 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/rickshaw.min.css: -------------------------------------------------------------------------------- 1 | .rickshaw_graph .detail{pointer-events:none;position:absolute;top:0;z-index:2;background:rgba(0,0,0,.1);bottom:0;width:1px;transition:opacity .25s linear;-moz-transition:opacity .25s linear;-o-transition:opacity .25s linear;-webkit-transition:opacity .25s linear}.rickshaw_graph .detail.inactive{opacity:0}.rickshaw_graph .detail .item.active{opacity:1}.rickshaw_graph .detail .x_label{font-family:Arial,sans-serif;border-radius:3px;padding:6px;opacity:.5;border:1px solid #e0e0e0;font-size:12px;position:absolute;background:#fff;white-space:nowrap}.rickshaw_graph .detail .item{position:absolute;z-index:2;border-radius:3px;padding:.25em;font-size:12px;font-family:Arial,sans-serif;opacity:0;background:rgba(0,0,0,.4);color:#fff;border:1px solid rgba(0,0,0,.4);margin-left:1em;margin-top:-1em;white-space:nowrap}.rickshaw_graph .detail .item.active{opacity:1;background:rgba(0,0,0,.8)}.rickshaw_graph .detail .item:before{content:"\25c2";position:absolute;left:-.5em;color:rgba(0,0,0,.7);width:0}.rickshaw_graph .detail .dot{width:4px;height:4px;margin-left:-4px;margin-top:-3px;border-radius:5px;position:absolute;box-shadow:0 0 2px rgba(0,0,0,.6);background:#fff;border-width:2px;border-style:solid;display:none;background-clip:padding-box}.rickshaw_graph .detail .dot.active{display:block}.rickshaw_graph{position:relative}.rickshaw_graph svg{display:block;overflow:hidden}.rickshaw_graph .x_tick{position:absolute;top:0;bottom:0;width:0;border-left:1px dotted rgba(0,0,0,.2);pointer-events:none}.rickshaw_graph .x_tick .title{position:absolute;font-size:12px;font-family:Arial,sans-serif;opacity:.5;white-space:nowrap;margin-left:3px;bottom:1px}.rickshaw_annotation_timeline{height:1px;border-top:1px solid #e0e0e0;margin-top:10px;position:relative}.rickshaw_annotation_timeline .annotation{position:absolute;height:6px;width:6px;margin-left:-2px;top:-3px;border-radius:5px;background-color:rgba(0,0,0,.25)}.rickshaw_graph .annotation_line{position:absolute;top:0;bottom:-6px;width:0;border-left:2px solid rgba(0,0,0,.3);display:none}.rickshaw_graph .annotation_line.active{display:block}.rickshaw_graph .annotation_range{background:rgba(0,0,0,.1);display:none;position:absolute;top:0;bottom:-6px;z-index:-10}.rickshaw_graph .annotation_range.active{display:block}.rickshaw_graph .annotation_range.active.offscreen{display:none}.rickshaw_annotation_timeline .annotation .content{background:#fff;color:#000;opacity:.9;padding:5px 5px;box-shadow:0 0 2px rgba(0,0,0,.8);border-radius:3px;position:relative;z-index:20;font-size:12px;padding:6px 8px 8px;top:18px;left:-11px;width:160px;display:none;cursor:pointer}.rickshaw_annotation_timeline .annotation .content:before{content:"\25b2";position:absolute;top:-11px;color:#fff;text-shadow:0 -1px 1px rgba(0,0,0,.8)}.rickshaw_annotation_timeline .annotation.active,.rickshaw_annotation_timeline .annotation:hover{background-color:rgba(0,0,0,.8);cursor:none}.rickshaw_annotation_timeline .annotation .content:hover{z-index:50}.rickshaw_annotation_timeline .annotation.active .content{display:block}.rickshaw_annotation_timeline .annotation:hover .content{display:block;z-index:50}.rickshaw_graph .y_axis{fill:none}.rickshaw_graph .y_ticks .tick{stroke:rgba(0,0,0,.16);stroke-width:2px;shape-rendering:crisp-edges;pointer-events:none}.rickshaw_graph .y_grid .tick{z-index:-1;stroke:rgba(0,0,0,.20);stroke-width:1px;stroke-dasharray:1 1}.rickshaw_graph .y_grid path{fill:none;stroke:none}.rickshaw_graph .y_ticks path{fill:none;stroke:#808080}.rickshaw_graph .y_ticks text{opacity:.5;font-size:12px;pointer-events:none}.rickshaw_graph .x_tick.glow .title,.rickshaw_graph .y_ticks.glow text{fill:black;color:#000;text-shadow:-1px 1px 0 rgba(255,255,255,.1),1px -1px 0 rgba(255,255,255,.1),1px 1px 0 rgba(255,255,255,.1),0px 1px 0 rgba(255,255,255,.1),0px -1px 0 rgba(255,255,255,.1),1px 0 0 rgba(255,255,255,.1),-1px 0 0 rgba(255,255,255,.1),-1px -1px 0 rgba(255,255,255,.1)}.rickshaw_graph .x_tick.inverse .title,.rickshaw_graph .y_ticks.inverse text{fill:white;color:#fff;text-shadow:-1px 1px 0 rgba(0,0,0,.8),1px -1px 0 rgba(0,0,0,.8),1px 1px 0 rgba(0,0,0,.8),0px 1px 0 rgba(0,0,0,.8),0px -1px 0 rgba(0,0,0,.8),1px 0 0 rgba(0,0,0,.8),-1px 0 0 rgba(0,0,0,.8),-1px -1px 0 rgba(0,0,0,.8)}.rickshaw_legend{font-family:Arial;font-size:12px;color:#fff;background:#404040;display:inline-block;padding:12px 5px;border-radius:2px;position:relative}.rickshaw_legend:hover{z-index:10}.rickshaw_legend .swatch{width:10px;height:10px;border:1px solid rgba(0,0,0,.2)}.rickshaw_legend .line{clear:both;line-height:140%;padding-right:15px}.rickshaw_legend .line .swatch{display:inline-block;margin-right:3px;border-radius:2px}.rickshaw_legend .label{white-space:nowrap;display:inline}.rickshaw_legend .action:hover{opacity:.6}.rickshaw_legend .action{margin-right:.2em;font-size:10px;opacity:.2;cursor:pointer;font-size:14px}.rickshaw_legend .line.disabled{opacity:.4}.rickshaw_legend ul{list-style-type:none;margin:0;padding:0;margin:2px;cursor:pointer}.rickshaw_legend li{padding:0 0 0 2px;min-width:80px;white-space:nowrap}.rickshaw_legend li:hover{background:rgba(255,255,255,.08);border-radius:3px}.rickshaw_legend li:active{background:rgba(255,255,255,.2);border-radius:3px} -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/series.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | 40 |
41 |

Random Data in the Future

42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 |
54 |
55 |
56 |
Smoothing
57 |
58 |
59 |
60 |
61 | 62 |
63 |
64 |
65 |
66 |
67 | 68 |
69 | 70 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.HoverDetail.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph.HoverDetail'); 2 | 3 | Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({ 4 | 5 | initialize: function(args) { 6 | 7 | var graph = this.graph = args.graph; 8 | 9 | this.xFormatter = args.xFormatter || function(x) { 10 | return new Date( x * 1000 ).toUTCString(); 11 | }; 12 | 13 | this.yFormatter = args.yFormatter || function(y) { 14 | return y === null ? y : y.toFixed(2); 15 | }; 16 | 17 | var element = this.element = document.createElement('div'); 18 | element.className = 'detail'; 19 | 20 | this.visible = true; 21 | graph.element.appendChild(element); 22 | 23 | this.lastEvent = null; 24 | this._addListeners(); 25 | 26 | this.onShow = args.onShow; 27 | this.onHide = args.onHide; 28 | this.onRender = args.onRender; 29 | 30 | this.formatter = args.formatter || this.formatter; 31 | 32 | }, 33 | 34 | formatter: function(series, x, y, formattedX, formattedY, d) { 35 | return series.name + ': ' + formattedY; 36 | }, 37 | 38 | update: function(e) { 39 | 40 | e = e || this.lastEvent; 41 | if (!e) return; 42 | this.lastEvent = e; 43 | 44 | if (!e.target.nodeName.match(/^(path|svg|rect)$/)) return; 45 | 46 | var graph = this.graph; 47 | 48 | var eventX = e.offsetX || e.layerX; 49 | var eventY = e.offsetY || e.layerY; 50 | 51 | var j = 0; 52 | var points = []; 53 | var nearestPoint; 54 | 55 | this.graph.series.active().forEach( function(series) { 56 | 57 | var data = this.graph.stackedData[j++]; 58 | 59 | var domainX = graph.x.invert(eventX); 60 | 61 | var domainIndexScale = d3.scale.linear() 62 | .domain([data[0].x, data.slice(-1)[0].x]) 63 | .range([0, data.length]); 64 | 65 | var approximateIndex = Math.floor(domainIndexScale(domainX)); 66 | var dataIndex = Math.min(approximateIndex || 0, data.length - 1); 67 | 68 | for (var i = approximateIndex; i < data.length - 1;) { 69 | 70 | if (!data[i] || !data[i + 1]) break; 71 | if (data[i].x <= domainX && data[i + 1].x > domainX) { dataIndex = i; break } 72 | 73 | if (data[i + 1].x <= domainX) { i++ } else { i-- } 74 | } 75 | 76 | var value = data[dataIndex]; 77 | 78 | var distance = Math.sqrt( 79 | Math.pow(Math.abs(graph.x(value.x) - eventX), 2) + 80 | Math.pow(Math.abs(graph.y(value.y + value.y0) - eventY), 2) 81 | ); 82 | 83 | var xFormatter = series.xFormatter || this.xFormatter; 84 | var yFormatter = series.yFormatter || this.yFormatter; 85 | 86 | var point = { 87 | formattedXValue: xFormatter(value.x), 88 | formattedYValue: yFormatter(value.y), 89 | series: series, 90 | value: value, 91 | distance: distance, 92 | order: j, 93 | name: series.name 94 | }; 95 | 96 | if (!nearestPoint || distance < nearestPoint.distance) { 97 | nearestPoint = point; 98 | } 99 | 100 | points.push(point); 101 | 102 | }, this ); 103 | 104 | 105 | nearestPoint.active = true; 106 | 107 | var domainX = nearestPoint.value.x; 108 | var formattedXValue = nearestPoint.formattedXValue; 109 | 110 | this.element.innerHTML = ''; 111 | this.element.style.left = graph.x(domainX) + 'px'; 112 | 113 | this.visible && this.render( { 114 | points: points, 115 | detail: points, // for backwards compatibility 116 | mouseX: eventX, 117 | mouseY: eventY, 118 | formattedXValue: formattedXValue, 119 | domainX: domainX 120 | } ); 121 | }, 122 | 123 | hide: function() { 124 | this.visible = false; 125 | this.element.classList.add('inactive'); 126 | 127 | if (typeof this.onHide == 'function') { 128 | this.onHide(); 129 | } 130 | }, 131 | 132 | show: function() { 133 | this.visible = true; 134 | this.element.classList.remove('inactive'); 135 | 136 | if (typeof this.onShow == 'function') { 137 | this.onShow(); 138 | } 139 | }, 140 | 141 | render: function(args) { 142 | 143 | var graph = this.graph; 144 | var points = args.points; 145 | var point = points.filter( function(p) { return p.active } ).shift(); 146 | 147 | if (point.value.y === null) return; 148 | 149 | var formattedXValue = this.xFormatter(point.value.x); 150 | var formattedYValue = this.yFormatter(point.value.y); 151 | 152 | this.element.innerHTML = ''; 153 | this.element.style.left = graph.x(point.value.x) + 'px'; 154 | 155 | var xLabel = document.createElement('div'); 156 | 157 | xLabel.className = 'x_label'; 158 | xLabel.innerHTML = formattedXValue; 159 | this.element.appendChild(xLabel); 160 | 161 | var item = document.createElement('div'); 162 | 163 | item.className = 'item'; 164 | item.innerHTML = this.formatter(point.series, point.value.x, point.value.y, formattedXValue, formattedYValue, point); 165 | item.style.top = this.graph.y(point.value.y0 + point.value.y) + 'px'; 166 | 167 | this.element.appendChild(item); 168 | 169 | var dot = document.createElement('div'); 170 | 171 | dot.className = 'dot'; 172 | dot.style.top = item.style.top; 173 | dot.style.borderColor = point.series.color; 174 | 175 | this.element.appendChild(dot); 176 | 177 | if (point.active) { 178 | item.className = 'item active'; 179 | dot.className = 'dot active'; 180 | } 181 | 182 | this.show(); 183 | 184 | if (typeof this.onRender == 'function') { 185 | this.onRender(args); 186 | } 187 | }, 188 | 189 | _addListeners: function() { 190 | 191 | this.graph.element.addEventListener( 192 | 'mousemove', 193 | function(e) { 194 | this.visible = true; 195 | this.update(e) 196 | }.bind(this), 197 | false 198 | ); 199 | 200 | this.graph.onUpdate( function() { this.update() }.bind(this) ); 201 | 202 | this.graph.element.addEventListener( 203 | 'mouseout', 204 | function(e) { 205 | if (e.relatedTarget && !(e.relatedTarget.compareDocumentPosition(this.graph.element) & Node.DOCUMENT_POSITION_CONTAINS)) { 206 | this.hide(); 207 | } 208 | }.bind(this), 209 | false 210 | ); 211 | } 212 | }); 213 | 214 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/examples/data/status.json: -------------------------------------------------------------------------------- 1 | {"1320630300":{"304":41,"206":0,"502":0,"400":2,"408":49,"401":32,"200":1275,"302":13,"500":1,"301":7,"404":1},"1320629640":{"304":42,"206":0,"502":0,"400":2,"408":42,"401":29,"200":1245,"302":10,"500":0,"301":5,"404":0},"1320627540":{"304":55,"502":0,"400":2,"401":22,"408":43,"200":1178,"302":9,"301":8,"404":5},"1320630180":{"403":0,"304":39,"206":0,"502":0,"400":2,"408":39,"401":31,"200":1340,"302":14,"500":1,"404":1,"301":5},"1320628200":{"304":37,"206":0,"502":0,"400":2,"408":47,"401":30,"200":1232,"302":27,"301":8,"404":1},"1320629220":{"304":46,"206":0,"502":0,"400":2,"408":42,"401":29,"200":1212,"302":8,"301":8,"404":0},"1320627360":{"304":30,"206":0,"502":0,"400":1,"408":43,"401":27,"200":1190,"302":10,"500":0,"301":9,"404":1},"1320630120":{"304":44,"206":0,"502":0,"400":2,"408":48,"401":31,"200":1314,"302":11,"500":1,"301":7,"404":0},"1320629280":{"304":28,"206":0,"502":0,"400":2,"401":29,"408":43,"200":1236,"302":9,"500":0,"301":11},"1320628380":{"304":56,"206":0,"502":0,"400":4,"408":44,"401":23,"200":1305,"201":0,"302":16,"301":6,"404":1},"1320630480":{"304":60,"206":0,"502":0,"400":3,"408":51,"401":29,"200":1267,"302":12,"500":0,"301":8,"404":1},"1320628920":{"304":38,"206":0,"502":0,"400":2,"408":35,"401":21,"200":1211,"201":0,"302":9,"301":7,"404":1},"1320628320":{"304":46,"206":0,"502":0,"400":4,"408":43,"401":21,"200":1197,"302":13,"500":0,"301":5,"404":1},"1320629460":{"304":40,"502":1,"400":2,"401":27,"408":46,"200":1179,"302":39,"301":10,"404":1},"1320628500":{"304":40,"206":0,"502":0,"400":5,"408":42,"401":31,"200":1316,"302":10,"301":7,"404":1},"1320627300":{"304":34,"206":0,"502":0,"400":1,"408":48,"401":24,"200":1264,"302":9,"301":7,"404":1},"1320627960":{"403":0,"304":59,"206":0,"502":0,"400":2,"408":38,"401":27,"200":1180,"201":0,"302":7,"409":0,"500":0,"404":0,"301":8},"1320630600":{"304":40,"206":0,"502":0,"400":3,"408":46,"401":28,"200":1297,"302":12,"500":1,"301":7,"404":2},"1320628560":{"304":34,"206":0,"502":0,"400":3,"408":41,"401":31,"200":1275,"302":8,"301":9,"404":1},"1320628080":{"304":31,"206":0,"502":0,"400":2,"408":41,"401":27,"200":1177,"201":0,"302":5,"405":0,"301":7,"404":1},"1320627780":{"304":59,"502":0,"400":3,"401":27,"408":39,"200":1246,"302":14,"301":9,"404":1},"1320629940":{"304":42,"502":0,"400":2,"401":34,"408":44,"200":1297,"302":12,"500":0,"301":4,"404":1},"1320628020":{"304":38,"502":0,"400":3,"408":43,"401":27,"200":1200,"302":6,"405":0,"301":9,"404":0},"1320630360":{"304":44,"206":1,"502":0,"400":1,"408":47,"401":25,"200":1270,"302":11,"500":0,"301":7,"404":1},"1320629760":{"304":40,"206":0,"502":0,"400":2,"408":43,"401":29,"200":1302,"302":9,"500":0,"301":5,"404":1},"1320627720":{"304":36,"502":0,"400":3,"401":24,"408":43,"200":1166,"302":7,"301":9,"404":1},"1320627420":{"304":35,"206":0,"502":0,"400":2,"408":41,"401":28,"200":1220,"302":14,"301":10,"404":1},"1320629340":{"304":41,"206":0,"502":0,"400":3,"408":42,"401":33,"200":1255,"302":39,"301":10,"404":1},"1320628860":{"304":27,"206":0,"502":0,"400":2,"408":39,"401":20,"200":1167,"302":6,"301":7,"404":1},"1320629160":{"403":0,"304":43,"502":0,"400":2,"408":43,"401":23,"200":1214,"302":9,"410":0,"301":7,"404":1},"1320630420":{"304":39,"206":0,"502":0,"400":2,"408":46,"401":31,"200":1318,"302":10,"500":1,"301":9,"404":1},"1320627120":{"304":30,"206":0,"502":0,"400":3,"408":42,"401":29,"200":1166,"302":7,"301":6,"404":0},"1320630000":{"304":35,"502":0,"400":2,"401":29,"408":49,"200":1244,"302":8,"500":0,"301":3,"404":1},"1320629880":{"304":41,"502":0,"400":2,"401":30,"408":45,"200":1282,"302":9,"500":0,"301":5,"404":1},"1320628260":{"304":34,"204":0,"502":0,"400":5,"408":44,"401":28,"200":1205,"201":0,"302":8,"500":0,"301":10,"404":1},"1320629400":{"304":42,"206":0,"502":0,"400":2,"408":43,"401":30,"200":1242,"302":43,"301":10,"404":1},"1320627600":{"304":53,"206":0,"502":0,"400":2,"408":39,"401":26,"200":1190,"302":13,"500":0,"301":8,"404":3},"1320627660":{"304":42,"206":0,"502":1,"400":2,"408":42,"401":25,"200":1199,"201":0,"302":14,"301":7,"404":2},"1320629100":{"304":26,"502":0,"400":2,"401":23,"408":45,"200":1213,"302":9,"500":0,"301":7,"404":2},"1320627900":{"304":48,"502":0,"400":2,"401":26,"408":45,"200":1219,"302":7,"301":9,"404":1},"1320629580":{"304":22,"206":0,"502":0,"400":2,"408":37,"401":27,"200":1184,"302":13,"500":0,"301":8,"404":1},"1320628440":{"304":46,"206":0,"502":0,"400":4,"408":45,"401":27,"200":1320,"201":0,"302":11,"500":0,"301":6,"404":0},"1320628680":{"304":60,"502":0,"400":4,"408":45,"401":29,"200":1269,"201":0,"302":10,"301":9,"404":1},"1320630060":{"304":47,"502":0,"400":2,"401":28,"408":49,"200":1249,"302":8,"500":0,"301":6,"404":1},"1320627840":{"304":55,"502":0,"400":1,"401":23,"408":39,"200":1186,"302":7,"301":9,"404":1},"1320627180":{"304":24,"206":0,"502":0,"400":2,"408":38,"401":26,"200":1163,"302":7,"301":5,"404":1},"1320628740":{"304":50,"206":0,"502":0,"400":4,"408":40,"401":28,"200":1235,"302":10,"500":0,"301":8,"404":1},"1320630540":{"304":47,"206":0,"502":0,"400":4,"408":42,"401":28,"200":1371,"302":12,"500":0,"301":6,"404":1},"1320629040":{"304":29,"206":0,"502":1,"400":3,"408":42,"401":25,"200":1169,"302":7,"500":0,"410":0,"301":7,"404":1},"1320630240":{"403":0,"304":27,"206":0,"502":0,"400":1,"408":51,"401":30,"200":1297,"302":9,"500":0,"404":1,"301":7},"1320627480":{"304":34,"206":0,"502":0,"400":2,"408":40,"401":26,"200":1183,"302":13,"301":10,"404":2},"1320628620":{"304":37,"502":0,"400":5,"401":31,"408":45,"200":1215,"302":9,"301":10,"404":1},"1320628140":{"304":49,"206":0,"502":0,"400":2,"408":40,"401":33,"200":1238,"302":16,"301":8,"404":1},"1320629520":{"304":31,"502":0,"400":2,"401":29,"408":38,"200":1233,"302":32,"301":10,"404":0},"1320627240":{"304":40,"206":0,"502":0,"400":1,"401":23,"408":42,"200":1186,"302":8,"301":6},"1320629700":{"304":45,"206":0,"502":0,"400":1,"408":47,"401":26,"200":1206,"302":7,"500":0,"301":6,"404":1},"1320628980":{"304":38,"502":0,"400":2,"408":42,"401":27,"200":1234,"302":11,"405":0,"301":7,"404":0},"1320628800":{"304":32,"206":0,"502":0,"400":2,"408":50,"401":22,"200":1121,"302":9,"500":0,"301":7,"404":1},"1320629820":{"304":41,"206":0,"502":0,"400":2,"408":40,"401":23,"200":1280,"302":8,"301":5,"404":1}} -------------------------------------------------------------------------------- /html/scripts/main.js: -------------------------------------------------------------------------------- 1 | var crimeApp; 2 | $(document).ready(function() { 3 | crimeApp = new CrimeApp(); 4 | crimeApp.init(); 5 | }); 6 | 7 | 8 | function CrimeApp() { 9 | //v0.1.6 10 | 11 | var map, mapManager, callTypes, calls; 12 | var mapInitDone, sliderReady, wasPlaying, callsLoaded, typesLoaded; 13 | var firstFrameDate; 14 | var secPerFrame = 600; 15 | 16 | //Public functions 17 | 18 | this.init = function init() { 19 | var mapOptions = { 20 | center: new google.maps.LatLng(43.082, -89.400), 21 | zoom: 11, 22 | mapTypeId: google.maps.MapTypeId.ROADMAP 23 | }; 24 | 25 | map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); 26 | google.maps.event.addListener(map, 'dragend', onMapDragEnd); 27 | 28 | mapManager = new MapPlayer({ 29 | onFrame: onFrame, 30 | fps: 6, 31 | decay: 12, 32 | }); 33 | 34 | $.getJSON("scripts/call_types.json", onLoadCallTypes); 35 | $.getJSON("scripts/calls.json", onLoadCalls); 36 | } 37 | 38 | //Private functions 39 | 40 | function roundMinutesDown(time) { 41 | var tDate = new Date(time); 42 | var minutes = tDate.getMinutes(); 43 | var minutes = minutes - (minutes % (secPerFrame / 60)); 44 | tDate.setMinutes(minutes); 45 | return tDate; 46 | } 47 | 48 | function formatTime(time) { 49 | var fDate = new Date(time); 50 | var niceDateText; 51 | var hours, hourLabel, minutes; 52 | 53 | hours = fDate.getHours(); 54 | if (hours < 12) { 55 | hourLabel = "am"; 56 | } else { 57 | hourLabel = "pm"; 58 | hours -= 12; 59 | } 60 | if (hours == 0) { 61 | hours = 12; 62 | } 63 | 64 | minutes = fDate.getMinutes(); 65 | if (minutes.toString().length == 1) { 66 | minutes = "0" + minutes; 67 | } 68 | 69 | niceDateText = fDate.toDateString() + ", "; 70 | niceDateText += hours + ":" + minutes + hourLabel; 71 | 72 | return niceDateText; 73 | } 74 | 75 | function onLoadComplete() { 76 | //Don't turn on the UI until ajax is done. 77 | if (!(typesLoaded && callsLoaded)) { 78 | return; 79 | } 80 | 81 | //Create frames based on data 82 | var i, //each frame 83 | j, //each call per frame 84 | f; //new frame object 85 | for (i = 0; i < calls.length; i++) { 86 | for (j = 0; j < calls[i]['data'].length; j++) { 87 | calls[i]['data'][j].color = callTypes[calls[i]['data'][j]['it']].color; 88 | calls[i]['data'][j].iwContent = 89 | calls[i]['data'][j]['title'] + "
" + 90 | "#" + calls[i]['data'][j]['in'] + "
" + 91 | callTypes[calls[i]['data'][j]['it']].title; 92 | } 93 | f = new MapFrame({ 94 | exData: { title: calls[i]['t'] }, 95 | map: map, 96 | data: calls[i]['data'] 97 | }); 98 | mapManager.pushFrame(f); 99 | } 100 | mapManager.draw(); 101 | 102 | firstFrameDate = roundMinutesDown(calls[0]['data'][0]['d']); 103 | 104 | $("#slider").on("slidecreate", function() { 105 | sliderReady = true; 106 | }); 107 | $("#slider").on("slidestart", function() { 108 | wasPlaying = mapManager.isPlaying(); 109 | mapManager.pause(); 110 | }); 111 | $("#slider").on("slide", function(event, ui) { 112 | var seekTime = firstFrameDate.getTime(); 113 | seekTime += (secPerFrame * ui.value * 1000); 114 | $("#curTime").text(formatTime(seekTime)); 115 | }); 116 | $("#slider").on("slidestop", function(event, ui) { 117 | jump(ui.value); 118 | if (wasPlaying) { 119 | mapManager.play(); 120 | } 121 | }); 122 | $("#slider").slider({ 123 | min: 0, 124 | max: mapManager.numFrames(), 125 | }); 126 | 127 | $("#playToggle").click(btnTogglePlay); 128 | $("#playToggle").removeAttr("disabled"); 129 | $("#nextFrame").click(btnNextFrame); 130 | $("#nextFrame").removeAttr("disabled"); 131 | //$("#speedUp").on("click", btnSpeedUp); 132 | //$("#speedDown").on("click", btnSpeedDown); 133 | 134 | $("#btnLegend").click(btnLegend); 135 | $("#btnInfo").click(btnInfo); 136 | $("#btnSearch").click(btnSearch); 137 | $("#btnContact").click(btnContact); 138 | 139 | setTimeout(function() {$("#loading").fadeOut(300);}, 1000); //allow 1s for map draw 140 | } 141 | 142 | function onLoadCallTypes(data) { 143 | callTypes = data; 144 | typesLoaded = true; 145 | onLoadComplete(); 146 | } 147 | 148 | function onLoadCalls(data) { 149 | calls = data; 150 | callsLoaded = true; 151 | onLoadComplete(); 152 | } 153 | 154 | function onMapDragEnd() { 155 | //console.log(map.getCenter().toString()); 156 | } 157 | 158 | function onFrame(infos) { 159 | //$("#curTime").text(formatTime(infos.exData.title) + " (" + infos.index + "/" + infos.count + ")"); 160 | $("#curTime").text(formatTime(infos.exData.title)); 161 | $("#tod").tod({date: String(infos.exData.title)}); 162 | if (sliderReady === true) { 163 | $("#slider").slider("option", "value", infos.index); 164 | } 165 | } 166 | 167 | function jump(i) { 168 | mapManager.jump(i); 169 | mapManager.draw(); 170 | } 171 | 172 | function btnLegend() { 173 | //mapManager.pause(); 174 | $("#legendDialog").dialog({ 175 | title: "Legend", 176 | //modal: true, 177 | width: 500, 178 | resizable: false, 179 | }); 180 | } 181 | 182 | function btnInfo() { 183 | mapManager.pause(); 184 | $("#infoDialog").dialog({ 185 | title: "Info", 186 | modal: true, 187 | width: 500, 188 | resizable: false, 189 | }); 190 | } 191 | 192 | function btnSearch() { 193 | mapManager.pause(); 194 | $("#searchDialog").dialog({ 195 | title: "Search", 196 | modal: true, 197 | width: 500, 198 | resizable: false, 199 | }); 200 | } 201 | 202 | function btnContact() { 203 | mapManager.pause(); 204 | $("#contactDialog").dialog({ 205 | title: "Contact", 206 | modal: true, 207 | width: 500, 208 | resizable: false, 209 | }); 210 | } 211 | 212 | function btnNextFrame() { 213 | console.log("main.nextFrame()"); 214 | mapManager.drawNext(); 215 | } 216 | 217 | function btnTogglePlay() { 218 | mapManager.togglePlay(); 219 | if (mapManager.isPlaying()) { 220 | $("#playToggle").val("Pause"); 221 | $("#nextFrame").attr("disabled","disabled"); 222 | } else { 223 | $("#playToggle").val("Play"); 224 | $("#nextFrame").removeAttr("disabled"); 225 | } 226 | 227 | } 228 | 229 | function btnSpeedUp() { 230 | //TODO 231 | console.log("speed up"); 232 | } 233 | 234 | function btnSpeedDown() { 235 | //TODO 236 | console.log("speed down"); 237 | } 238 | } 239 | 240 | 241 | -------------------------------------------------------------------------------- /html/scripts/rickshaw/src/js/Rickshaw.Graph.js: -------------------------------------------------------------------------------- 1 | Rickshaw.namespace('Rickshaw.Graph'); 2 | 3 | Rickshaw.Graph = function(args) { 4 | 5 | if (!args.element) throw "Rickshaw.Graph needs a reference to an element"; 6 | 7 | this.element = args.element; 8 | this.series = args.series; 9 | 10 | this.defaults = { 11 | interpolation: 'cardinal', 12 | offset: 'zero', 13 | min: undefined, 14 | max: undefined 15 | }; 16 | 17 | Rickshaw.keys(this.defaults).forEach( function(k) { 18 | this[k] = args[k] || this.defaults[k]; 19 | }, this ); 20 | 21 | this.window = {}; 22 | 23 | this.updateCallbacks = []; 24 | 25 | var self = this; 26 | 27 | this.initialize = function(args) { 28 | 29 | this.validateSeries(args.series); 30 | 31 | this.series.active = function() { return self.series.filter( function(s) { return !s.disabled } ) }; 32 | 33 | this.setSize({ width: args.width, height: args.height }); 34 | 35 | this.element.classList.add('rickshaw_graph'); 36 | this.vis = d3.select(this.element) 37 | .append("svg:svg") 38 | .attr('width', this.width) 39 | .attr('height', this.height); 40 | 41 | var renderers = [ 42 | Rickshaw.Graph.Renderer.Stack, 43 | Rickshaw.Graph.Renderer.Line, 44 | Rickshaw.Graph.Renderer.Bar, 45 | Rickshaw.Graph.Renderer.Area, 46 | Rickshaw.Graph.Renderer.ScatterPlot 47 | ]; 48 | 49 | renderers.forEach( function(r) { 50 | if (!r) return; 51 | self.registerRenderer(new r( { graph: self } )); 52 | } ); 53 | 54 | this.setRenderer(args.renderer || 'stack', args); 55 | this.discoverRange(); 56 | }; 57 | 58 | this.validateSeries = function(series) { 59 | 60 | if (!(series instanceof Array) && !(series instanceof Rickshaw.Series)) { 61 | var seriesSignature = Object.prototype.toString.apply(series); 62 | throw "series is not an array: " + seriesSignature; 63 | } 64 | 65 | var pointsCount; 66 | 67 | series.forEach( function(s) { 68 | 69 | if (!(s instanceof Object)) { 70 | throw "series element is not an object: " + s; 71 | } 72 | if (!(s.data)) { 73 | throw "series has no data: " + JSON.stringify(s); 74 | } 75 | if (!(s.data instanceof Array)) { 76 | throw "series data is not an array: " + JSON.stringify(s.data); 77 | } 78 | 79 | var x = s.data[0].x; 80 | var y = s.data[0].y; 81 | 82 | if (typeof x != 'number' || ( typeof y != 'number' && y !== null ) ) { 83 | throw "x and y properties of points should be numbers instead of " + 84 | (typeof x) + " and " + (typeof y) 85 | } 86 | 87 | }, this ); 88 | }; 89 | 90 | this.dataDomain = function() { 91 | 92 | // take from the first series 93 | var data = this.series[0].data; 94 | 95 | return [ data[0].x, data.slice(-1).shift().x ]; 96 | 97 | }; 98 | 99 | this.discoverRange = function() { 100 | 101 | var domain = this.renderer.domain(); 102 | 103 | this.x = d3.scale.linear().domain(domain.x).range([0, this.width]); 104 | 105 | this.y = d3.scale.linear().domain(domain.y).range([this.height, 0]); 106 | 107 | this.y.magnitude = d3.scale.linear() 108 | .domain([domain.y[0] - domain.y[0], domain.y[1] - domain.y[0]]) 109 | .range([0, this.height]); 110 | }; 111 | 112 | this.render = function() { 113 | 114 | var stackedData = this.stackData(); 115 | this.discoverRange(); 116 | 117 | this.renderer.render(); 118 | 119 | this.updateCallbacks.forEach( function(callback) { 120 | callback(); 121 | } ); 122 | }; 123 | 124 | this.update = this.render; 125 | 126 | this.stackData = function() { 127 | 128 | var data = this.series.active() 129 | .map( function(d) { return d.data } ) 130 | .map( function(d) { return d.filter( function(d) { return this._slice(d) }, this ) }, this); 131 | 132 | this.stackData.hooks.data.forEach( function(entry) { 133 | data = entry.f.apply(self, [data]); 134 | } ); 135 | 136 | var stackedData; 137 | 138 | if (!this.renderer.unstack) { 139 | 140 | this._validateStackable(); 141 | 142 | var layout = d3.layout.stack(); 143 | layout.offset( self.offset ); 144 | stackedData = layout(data); 145 | } 146 | 147 | stackedData = stackedData || data; 148 | 149 | this.stackData.hooks.after.forEach( function(entry) { 150 | stackedData = entry.f.apply(self, [data]); 151 | } ); 152 | 153 | var i = 0; 154 | this.series.forEach( function(series) { 155 | if (series.disabled) return; 156 | series.stack = stackedData[i++]; 157 | } ); 158 | 159 | this.stackedData = stackedData; 160 | return stackedData; 161 | }; 162 | 163 | this._validateStackable = function() { 164 | 165 | var series = this.series; 166 | var pointsCount; 167 | 168 | series.forEach( function(s) { 169 | 170 | pointsCount = pointsCount || s.data.length; 171 | 172 | if (pointsCount && s.data.length != pointsCount) { 173 | throw "stacked series cannot have differing numbers of points: " + 174 | pointsCount + " vs " + s.data.length + "; see Rickshaw.Series.fill()"; 175 | } 176 | 177 | }, this ); 178 | }; 179 | 180 | this.stackData.hooks = { data: [], after: [] }; 181 | 182 | this._slice = function(d) { 183 | 184 | if (this.window.xMin || this.window.xMax) { 185 | 186 | var isInRange = true; 187 | 188 | if (this.window.xMin && d.x < this.window.xMin) isInRange = false; 189 | if (this.window.xMax && d.x > this.window.xMax) isInRange = false; 190 | 191 | return isInRange; 192 | } 193 | 194 | return true; 195 | }; 196 | 197 | this.onUpdate = function(callback) { 198 | this.updateCallbacks.push(callback); 199 | }; 200 | 201 | this.registerRenderer = function(renderer) { 202 | this._renderers = this._renderers || {}; 203 | this._renderers[renderer.name] = renderer; 204 | }; 205 | 206 | this.configure = function(args) { 207 | 208 | if (args.width || args.height) { 209 | this.setSize(args); 210 | } 211 | 212 | Rickshaw.keys(this.defaults).forEach( function(k) { 213 | this[k] = k in args ? args[k] 214 | : k in this ? this[k] 215 | : this.defaults[k]; 216 | }, this ); 217 | 218 | this.setRenderer(args.renderer || this.renderer.name, args); 219 | }; 220 | 221 | this.setRenderer = function(name, args) { 222 | 223 | if (!this._renderers[name]) { 224 | throw "couldn't find renderer " + name; 225 | } 226 | this.renderer = this._renderers[name]; 227 | 228 | if (typeof args == 'object') { 229 | this.renderer.configure(args); 230 | } 231 | }; 232 | 233 | this.setSize = function(args) { 234 | 235 | args = args || {}; 236 | 237 | if (typeof window !== undefined) { 238 | var style = window.getComputedStyle(this.element, null); 239 | var elementWidth = parseInt(style.getPropertyValue('width')); 240 | var elementHeight = parseInt(style.getPropertyValue('height')); 241 | } 242 | 243 | this.width = args.width || elementWidth || 400; 244 | this.height = args.height || elementHeight || 250; 245 | 246 | this.vis && this.vis 247 | .attr('width', this.width) 248 | .attr('height', this.height); 249 | } 250 | 251 | this.initialize(args); 252 | }; 253 | --------------------------------------------------------------------------------