rickshaw_rails 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@ var Rickshaw = {
7
7
  var parent = Rickshaw;
8
8
 
9
9
  for(var i = 1, length = parts.length; i < length; i++) {
10
- currentPart = parts[i];
10
+ var currentPart = parts[i];
11
11
  parent[currentPart] = parent[currentPart] || {};
12
12
  parent = parent[currentPart];
13
13
  }
@@ -377,6 +377,8 @@ Rickshaw.namespace('Rickshaw.Graph');
377
377
 
378
378
  Rickshaw.Graph = function(args) {
379
379
 
380
+ if (!args.element) throw "Rickshaw.Graph needs a reference to an element";
381
+
380
382
  this.element = args.element;
381
383
  this.series = args.series;
382
384
 
@@ -449,21 +451,15 @@ Rickshaw.Graph = function(args) {
449
451
  throw "series data is not an array: " + JSON.stringify(s.data);
450
452
  }
451
453
 
452
- pointsCount = pointsCount || s.data.length;
453
-
454
- if (pointsCount && s.data.length != pointsCount) {
455
- throw "series cannot have differing numbers of points: " +
456
- pointsCount + " vs " + s.data.length + "; see Rickshaw.Series.zeroFill()";
457
- }
458
-
459
- var dataTypeX = typeof s.data[0].x;
460
- var dataTypeY = typeof s.data[0].y;
454
+ var x = s.data[0].x;
455
+ var y = s.data[0].y;
461
456
 
462
- if (dataTypeX != 'number' || dataTypeY != 'number') {
457
+ if (typeof x != 'number' || ( typeof y != 'number' && y !== null ) ) {
463
458
  throw "x and y properties of points should be numbers instead of " +
464
- dataTypeX + " and " + dataTypeY;
459
+ (typeof x) + " and " + (typeof y)
465
460
  }
466
- } );
461
+
462
+ }, this );
467
463
  };
468
464
 
469
465
  this.dataDomain = function() {
@@ -512,10 +508,18 @@ Rickshaw.Graph = function(args) {
512
508
  data = entry.f.apply(self, [data]);
513
509
  } );
514
510
 
515
- var layout = d3.layout.stack();
516
- layout.offset( self.offset );
511
+ var stackedData;
517
512
 
518
- var stackedData = layout(data);
513
+ if (!this.renderer.unstack) {
514
+
515
+ this._validateStackable();
516
+
517
+ var layout = d3.layout.stack();
518
+ layout.offset( self.offset );
519
+ stackedData = layout(data);
520
+ }
521
+
522
+ stackedData = stackedData || data;
519
523
 
520
524
  this.stackData.hooks.after.forEach( function(entry) {
521
525
  stackedData = entry.f.apply(self, [data]);
@@ -531,6 +535,23 @@ Rickshaw.Graph = function(args) {
531
535
  return stackedData;
532
536
  };
533
537
 
538
+ this._validateStackable = function() {
539
+
540
+ var series = this.series;
541
+ var pointsCount;
542
+
543
+ series.forEach( function(s) {
544
+
545
+ pointsCount = pointsCount || s.data.length;
546
+
547
+ if (pointsCount && s.data.length != pointsCount) {
548
+ throw "stacked series cannot have differing numbers of points: " +
549
+ pointsCount + " vs " + s.data.length + "; see Rickshaw.Series.fill()";
550
+ }
551
+
552
+ }, this );
553
+ };
554
+
534
555
  this.stackData.hooks = { data: [], after: [] };
535
556
 
536
557
  this._slice = function(d) {
@@ -905,23 +926,25 @@ Rickshaw.Fixtures.Time = function() {
905
926
  Rickshaw.namespace('Rickshaw.Fixtures.Number');
906
927
 
907
928
  Rickshaw.Fixtures.Number.formatKMBT = function(y) {
908
- if (y >= 1000000000000) { return y / 1000000000000 + "T" }
909
- else if (y >= 1000000000) { return y / 1000000000 + "B" }
910
- else if (y >= 1000000) { return y / 1000000 + "M" }
911
- else if (y >= 1000) { return y / 1000 + "K" }
912
- else if (y < 1 && y > 0) { return y.toFixed(2) }
913
- else if (y == 0) { return '' }
929
+ abs_y = Math.abs(y);
930
+ if (abs_y >= 1000000000000) { return y / 1000000000000 + "T" }
931
+ else if (abs_y >= 1000000000) { return y / 1000000000 + "B" }
932
+ else if (abs_y >= 1000000) { return y / 1000000 + "M" }
933
+ else if (abs_y >= 1000) { return y / 1000 + "K" }
934
+ else if (abs_y < 1 && y > 0) { return y.toFixed(2) }
935
+ else if (abs_y == 0) { return '' }
914
936
  else { return y }
915
937
  };
916
938
 
917
939
  Rickshaw.Fixtures.Number.formatBase1024KMGTP = function(y) {
918
- if (y >= 1125899906842624) { return y / 1125899906842624 + "P" }
919
- else if (y >= 1099511627776){ return y / 1099511627776 + "T" }
920
- else if (y >= 1073741824) { return y / 1073741824 + "G" }
921
- else if (y >= 1048576) { return y / 1048576 + "M" }
922
- else if (y >= 1024) { return y / 1024 + "K" }
923
- else if (y < 1 && y > 0) { return y.toFixed(2) }
924
- else if (y == 0) { return '' }
940
+ abs_y = Math.abs(y);
941
+ if (abs_y >= 1125899906842624) { return y / 1125899906842624 + "P" }
942
+ else if (abs_y >= 1099511627776){ return y / 1099511627776 + "T" }
943
+ else if (abs_y >= 1073741824) { return y / 1073741824 + "G" }
944
+ else if (abs_y >= 1048576) { return y / 1048576 + "M" }
945
+ else if (abs_y >= 1024) { return y / 1024 + "K" }
946
+ else if (abs_y < 1 && y > 0) { return y.toFixed(2) }
947
+ else if (abs_y == 0) { return '' }
925
948
  else { return y }
926
949
  };
927
950
  Rickshaw.namespace("Rickshaw.Color.Palette");
@@ -1010,7 +1033,7 @@ Rickshaw.Graph.Ajax = Rickshaw.Class.create( {
1010
1033
  data = this.onData(data);
1011
1034
  this.args.series = this._splice({ data: data, series: this.args.series });
1012
1035
 
1013
- this.graph = new Rickshaw.Graph(this.args);
1036
+ this.graph = this.graph || new Rickshaw.Graph(this.args);
1014
1037
  this.graph.render();
1015
1038
 
1016
1039
  this.onComplete(this);
@@ -1036,7 +1059,7 @@ Rickshaw.Graph.Ajax = Rickshaw.Class.create( {
1036
1059
  if (seriesKey == dataKey) {
1037
1060
  var properties = ['color', 'name', 'data'];
1038
1061
  properties.forEach( function(p) {
1039
- s[p] = s[p] || d[p];
1062
+ if (d[p]) s[p] = d[p];
1040
1063
  } );
1041
1064
  }
1042
1065
  } );
@@ -1194,7 +1217,7 @@ Rickshaw.Graph.Axis.Time = function(args) {
1194
1217
 
1195
1218
  for (var i = 0; i < count; i++) {
1196
1219
 
1197
- tickValue = time.ceil(runningTick, unit);
1220
+ var tickValue = time.ceil(runningTick, unit);
1198
1221
  runningTick = tickValue + unit.seconds / 2;
1199
1222
 
1200
1223
  offsets.push( { value: tickValue, unit: unit } );
@@ -1236,6 +1259,116 @@ Rickshaw.Graph.Axis.Time = function(args) {
1236
1259
  this.graph.onUpdate( function() { self.render() } );
1237
1260
  };
1238
1261
 
1262
+ Rickshaw.namespace('Rickshaw.Graph.Axis.X');
1263
+
1264
+ Rickshaw.Graph.Axis.X = function(args) {
1265
+
1266
+ var self = this;
1267
+ var berthRate = 0.10;
1268
+
1269
+ this.initialize = function(args) {
1270
+
1271
+ this.graph = args.graph;
1272
+ this.orientation = args.orientation || 'top';
1273
+
1274
+ var pixelsPerTick = args.pixelsPerTick || 75;
1275
+ this.ticks = args.ticks || Math.floor(this.graph.width / pixelsPerTick);
1276
+ this.tickSize = args.tickSize || 4;
1277
+ this.ticksTreatment = args.ticksTreatment || 'plain';
1278
+
1279
+ if (args.element) {
1280
+
1281
+ this.element = args.element;
1282
+ this._discoverSize(args.element, args);
1283
+
1284
+ this.vis = d3.select(args.element)
1285
+ .append("svg:svg")
1286
+ .attr('height', this.height)
1287
+ .attr('width', this.width)
1288
+ .attr('class', 'rickshaw_graph x_axis_d3');
1289
+
1290
+ this.element = this.vis[0][0];
1291
+ this.element.style.position = 'relative';
1292
+
1293
+ this.setSize({ width: args.width, height: args.height });
1294
+
1295
+ } else {
1296
+ this.vis = this.graph.vis;
1297
+ }
1298
+
1299
+ this.graph.onUpdate( function() { self.render() } );
1300
+ };
1301
+
1302
+ this.setSize = function(args) {
1303
+
1304
+ args = args || {};
1305
+ if (!this.element) return;
1306
+
1307
+ this._discoverSize(this.element.parentNode, args);
1308
+
1309
+ this.vis
1310
+ .attr('height', this.height)
1311
+ .attr('width', this.width * (1 + berthRate));
1312
+
1313
+ var berth = Math.floor(this.width * berthRate / 2);
1314
+ this.element.style.left = -1 * berth + 'px';
1315
+ };
1316
+
1317
+ this.render = function() {
1318
+
1319
+ if (this.graph.width !== this._renderWidth) this.setSize({ auto: true });
1320
+
1321
+ var axis = d3.svg.axis().scale(this.graph.x).orient(this.orientation);
1322
+ axis.tickFormat( args.tickFormat || function(x) { return x } );
1323
+
1324
+ var berth = Math.floor(this.width * berthRate / 2) || 0;
1325
+
1326
+ if (this.orientation == 'top') {
1327
+ var yOffset = this.height || this.graph.height;
1328
+ var transform = 'translate(' + berth + ',' + yOffset + ')';
1329
+ } else {
1330
+ var transform = 'translate(' + berth + ', 0)';
1331
+ }
1332
+
1333
+ if (this.element) {
1334
+ this.vis.selectAll('*').remove();
1335
+ }
1336
+
1337
+ this.vis
1338
+ .append("svg:g")
1339
+ .attr("class", ["x_ticks_d3", this.ticksTreatment].join(" "))
1340
+ .attr("transform", transform)
1341
+ .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));
1342
+
1343
+ var gridSize = (this.orientation == 'bottom' ? 1 : -1) * this.graph.height;
1344
+
1345
+ this.graph.vis
1346
+ .append("svg:g")
1347
+ .attr("class", "x_grid_d3")
1348
+ .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize));
1349
+
1350
+ this._renderHeight = this.graph.height;
1351
+ };
1352
+
1353
+ this._discoverSize = function(element, args) {
1354
+
1355
+ if (typeof window !== 'undefined') {
1356
+
1357
+ var style = window.getComputedStyle(element, null);
1358
+ var elementHeight = parseInt(style.getPropertyValue('height'));
1359
+
1360
+ if (!args.auto) {
1361
+ var elementWidth = parseInt(style.getPropertyValue('width'));
1362
+ }
1363
+ }
1364
+
1365
+ this.width = (args.width || elementWidth || this.graph.width) * (1 + berthRate);
1366
+ this.height = args.height || elementHeight || 40;
1367
+ };
1368
+
1369
+ this.initialize(args);
1370
+ };
1371
+
1239
1372
  Rickshaw.namespace('Rickshaw.Graph.Axis.Y');
1240
1373
 
1241
1374
  Rickshaw.Graph.Axis.Y = function(args) {
@@ -1344,12 +1477,31 @@ Rickshaw.Graph.Behavior.Series.Highlight = function(args) {
1344
1477
  var self = this;
1345
1478
 
1346
1479
  var colorSafe = {};
1480
+ var activeLine = null;
1347
1481
 
1348
1482
  this.addHighlightEvents = function (l) {
1483
+
1349
1484
  l.element.addEventListener( 'mouseover', function(e) {
1350
1485
 
1351
- self.legend.lines.forEach( function(line) {
1352
- if (l === line) return;
1486
+ if (activeLine) return;
1487
+ else activeLine = l;
1488
+
1489
+ self.legend.lines.forEach( function(line, index) {
1490
+
1491
+ if (l === line) {
1492
+
1493
+ // if we're not in a stacked renderer bring active line to the top
1494
+ if (index > 0 && self.graph.renderer.unstack) {
1495
+
1496
+ var seriesIndex = self.graph.series.length - index - 1;
1497
+ line.originalIndex = seriesIndex;
1498
+
1499
+ var series = self.graph.series.splice(seriesIndex, 1)[0];
1500
+ self.graph.series.push(series);
1501
+ }
1502
+ return;
1503
+ }
1504
+
1353
1505
  colorSafe[line.series.name] = colorSafe[line.series.name] || line.series.color;
1354
1506
  line.series.color = d3.interpolateRgb(line.series.color, d3.rgb('#d8d8d8'))(0.8).toString();
1355
1507
  } );
@@ -1360,7 +1512,19 @@ Rickshaw.Graph.Behavior.Series.Highlight = function(args) {
1360
1512
 
1361
1513
  l.element.addEventListener( 'mouseout', function(e) {
1362
1514
 
1515
+ if (!activeLine) return;
1516
+ else activeLine = null;
1517
+
1363
1518
  self.legend.lines.forEach( function(line) {
1519
+
1520
+ // return reordered series to its original place
1521
+ if (l === line && line.hasOwnProperty('originalIndex')) {
1522
+
1523
+ var series = self.graph.series.pop();
1524
+ self.graph.series.splice(line.originalIndex, 0, series);
1525
+ delete line['originalIndex'];
1526
+ }
1527
+
1364
1528
  if (colorSafe[line.series.name]) {
1365
1529
  line.series.color = colorSafe[line.series.name];
1366
1530
  }
@@ -1546,7 +1710,7 @@ Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
1546
1710
  };
1547
1711
 
1548
1712
  this.yFormatter = args.yFormatter || function(y) {
1549
- return y.toFixed(2);
1713
+ return y === null ? y : y.toFixed(2);
1550
1714
  };
1551
1715
 
1552
1716
  var element = this.element = document.createElement('div');
@@ -1563,6 +1727,7 @@ Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
1563
1727
  this.onRender = args.onRender;
1564
1728
 
1565
1729
  this.formatter = args.formatter || this.formatter;
1730
+
1566
1731
  },
1567
1732
 
1568
1733
  formatter: function(series, x, y, formattedX, formattedY, d) {
@@ -1582,75 +1747,76 @@ Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
1582
1747
  var eventX = e.offsetX || e.layerX;
1583
1748
  var eventY = e.offsetY || e.layerY;
1584
1749
 
1585
- var domainX = graph.x.invert(eventX);
1586
- var stackedData = graph.stackedData;
1750
+ var j = 0;
1751
+ var points = [];
1752
+ var nearestPoint;
1587
1753
 
1588
- var topSeriesData = stackedData.slice(-1).shift();
1754
+ this.graph.series.active().forEach( function(series) {
1589
1755
 
1590
- var domainIndexScale = d3.scale.linear()
1591
- .domain([topSeriesData[0].x, topSeriesData.slice(-1).shift().x])
1592
- .range([0, topSeriesData.length]);
1756
+ var data = this.graph.stackedData[j++];
1593
1757
 
1594
- var approximateIndex = Math.floor(domainIndexScale(domainX));
1595
- var dataIndex = Math.min(approximateIndex || 0, stackedData[0].length - 1);
1758
+ var domainX = graph.x.invert(eventX);
1596
1759
 
1597
- for (var i = approximateIndex; i < stackedData[0].length - 1;) {
1760
+ var domainIndexScale = d3.scale.linear()
1761
+ .domain([data[0].x, data.slice(-1)[0].x])
1762
+ .range([0, data.length]);
1598
1763
 
1599
- if (!stackedData[0][i] || !stackedData[0][i + 1]) {
1600
- break;
1601
- }
1764
+ var approximateIndex = Math.floor(domainIndexScale(domainX));
1765
+ var dataIndex = Math.min(approximateIndex || 0, data.length - 1);
1602
1766
 
1603
- if (stackedData[0][i].x <= domainX && stackedData[0][i + 1].x > domainX) {
1604
- dataIndex = i;
1605
- break;
1767
+ for (var i = approximateIndex; i < data.length - 1;) {
1768
+
1769
+ if (!data[i] || !data[i + 1]) break;
1770
+ if (data[i].x <= domainX && data[i + 1].x > domainX) { dataIndex = i; break }
1771
+
1772
+ if (data[i + 1].x <= domainX) { i++ } else { i-- }
1606
1773
  }
1607
- if (stackedData[0][i + 1] <= domainX) { i++ } else { i-- }
1608
- }
1609
1774
 
1610
- var domainX = stackedData[0][dataIndex].x;
1611
- var formattedXValue = this.xFormatter(domainX);
1612
- var graphX = graph.x(domainX);
1613
- var order = 0;
1775
+ var value = data[dataIndex];
1614
1776
 
1615
- var detail = graph.series.active()
1616
- .map( function(s) { return { order: order++, series: s, name: s.name, value: s.stack[dataIndex] } } );
1777
+ var distance = Math.sqrt(
1778
+ Math.pow(Math.abs(graph.x(value.x) - eventX), 2) +
1779
+ Math.pow(Math.abs(graph.y(value.y + value.y0) - eventY), 2)
1780
+ );
1617
1781
 
1618
- var activeItem;
1782
+ var xFormatter = series.xFormatter || this.xFormatter;
1783
+ var yFormatter = series.yFormatter || this.yFormatter;
1619
1784
 
1620
- var sortFn = function(a, b) {
1621
- return (a.value.y0 + a.value.y) - (b.value.y0 + b.value.y);
1622
- };
1785
+ var point = {
1786
+ formattedXValue: xFormatter(value.x),
1787
+ formattedYValue: yFormatter(value.y),
1788
+ series: series,
1789
+ value: value,
1790
+ distance: distance,
1791
+ order: j,
1792
+ name: series.name
1793
+ };
1623
1794
 
1624
- var domainMouseY = graph.y.magnitude.invert(graph.element.offsetHeight - eventY);
1795
+ if (!nearestPoint || distance < nearestPoint.distance) {
1796
+ nearestPoint = point;
1797
+ }
1625
1798
 
1626
- detail.sort(sortFn).forEach( function(d) {
1799
+ points.push(point);
1627
1800
 
1628
- d.formattedYValue = (this.yFormatter.constructor == Array) ?
1629
- this.yFormatter[detail.indexOf(d)](d.value.y) :
1630
- this.yFormatter(d.value.y);
1801
+ }, this );
1631
1802
 
1632
- d.graphX = graphX;
1633
- d.graphY = graph.y(d.value.y0 + d.value.y);
1634
1803
 
1635
- if (domainMouseY > d.value.y0 && domainMouseY < d.value.y0 + d.value.y && !activeItem) {
1636
- activeItem = d;
1637
- d.active = true;
1638
- }
1804
+ nearestPoint.active = true;
1639
1805
 
1640
- }, this );
1806
+ var domainX = nearestPoint.value.x;
1807
+ var formattedXValue = nearestPoint.formattedXValue;
1641
1808
 
1642
1809
  this.element.innerHTML = '';
1643
1810
  this.element.style.left = graph.x(domainX) + 'px';
1644
1811
 
1645
- if (this.visible) {
1646
- this.render( {
1647
- detail: detail,
1648
- domainX: domainX,
1649
- formattedXValue: formattedXValue,
1650
- mouseX: eventX,
1651
- mouseY: eventY
1652
- } );
1653
- }
1812
+ this.visible && this.render( {
1813
+ points: points,
1814
+ detail: points, // for backwards compatibility
1815
+ mouseX: eventX,
1816
+ mouseY: eventY,
1817
+ formattedXValue: formattedXValue,
1818
+ domainX: domainX
1819
+ } );
1654
1820
  },
1655
1821
 
1656
1822
  hide: function() {
@@ -1673,41 +1839,44 @@ Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
1673
1839
 
1674
1840
  render: function(args) {
1675
1841
 
1676
- var detail = args.detail;
1677
- var domainX = args.domainX;
1842
+ var graph = this.graph;
1843
+ var points = args.points;
1844
+ var point = points.filter( function(p) { return p.active } ).shift();
1678
1845
 
1679
- var mouseX = args.mouseX;
1680
- var mouseY = args.mouseY;
1846
+ if (point.value.y === null) return;
1681
1847
 
1682
- var formattedXValue = args.formattedXValue;
1848
+ var formattedXValue = this.xFormatter(point.value.x);
1849
+ var formattedYValue = this.yFormatter(point.value.y);
1850
+
1851
+ this.element.innerHTML = '';
1852
+ this.element.style.left = graph.x(point.value.x) + 'px';
1683
1853
 
1684
1854
  var xLabel = document.createElement('div');
1855
+
1685
1856
  xLabel.className = 'x_label';
1686
1857
  xLabel.innerHTML = formattedXValue;
1687
1858
  this.element.appendChild(xLabel);
1688
1859
 
1689
- detail.forEach( function(d) {
1860
+ var item = document.createElement('div');
1690
1861
 
1691
- var item = document.createElement('div');
1692
- item.className = 'item';
1693
- item.innerHTML = this.formatter(d.series, domainX, d.value.y, formattedXValue, d.formattedYValue, d);
1694
- item.style.top = this.graph.y(d.value.y0 + d.value.y) + 'px';
1862
+ item.className = 'item';
1863
+ item.innerHTML = this.formatter(point.series, point.value.x, point.value.y, formattedXValue, formattedYValue, point);
1864
+ item.style.top = this.graph.y(point.value.y0 + point.value.y) + 'px';
1695
1865
 
1696
- this.element.appendChild(item);
1866
+ this.element.appendChild(item);
1697
1867
 
1698
- var dot = document.createElement('div');
1699
- dot.className = 'dot';
1700
- dot.style.top = item.style.top;
1701
- dot.style.borderColor = d.series.color;
1868
+ var dot = document.createElement('div');
1702
1869
 
1703
- this.element.appendChild(dot);
1870
+ dot.className = 'dot';
1871
+ dot.style.top = item.style.top;
1872
+ dot.style.borderColor = point.series.color;
1704
1873
 
1705
- if (d.active) {
1706
- item.className = 'item active';
1707
- dot.className = 'dot active';
1708
- }
1874
+ this.element.appendChild(dot);
1709
1875
 
1710
- }, this );
1876
+ if (point.active) {
1877
+ item.className = 'item active';
1878
+ dot.className = 'dot active';
1879
+ }
1711
1880
 
1712
1881
  this.show();
1713
1882
 
@@ -1771,7 +1940,10 @@ Rickshaw.Graph.Legend = function(args) {
1771
1940
 
1772
1941
  var series = graph.series
1773
1942
  .map( function(s) { return s } )
1774
- .reverse();
1943
+
1944
+ if (!args.naturalOrder) {
1945
+ series = series.reverse();
1946
+ }
1775
1947
 
1776
1948
  this.lines = [];
1777
1949
 
@@ -1902,25 +2074,36 @@ Rickshaw.Graph.Renderer = Rickshaw.Class.create( {
1902
2074
 
1903
2075
  domain: function() {
1904
2076
 
1905
- var values = [];
2077
+ var values = { xMin: [], xMax: [], y: [] };
2078
+
1906
2079
  var stackedData = this.graph.stackedData || this.graph.stackData();
2080
+ var firstPoint = stackedData[0][0];
2081
+
2082
+ var xMin = firstPoint.x;
2083
+ var xMax = firstPoint.x
2084
+
2085
+ var yMin = firstPoint.y + firstPoint.y0;
2086
+ var yMax = firstPoint.y + firstPoint.y0;
1907
2087
 
1908
- var topSeriesData = this.unstack ? stackedData : [ stackedData.slice(-1).shift() ];
2088
+ stackedData.forEach( function(series) {
1909
2089
 
1910
- topSeriesData.forEach( function(series) {
1911
2090
  series.forEach( function(d) {
1912
- values.push( d.y + d.y0 );
2091
+
2092
+ var y = d.y + d.y0;
2093
+
2094
+ if (y < yMin) yMin = y;
2095
+ if (y > yMax) yMax = y;
1913
2096
  } );
1914
- } );
1915
2097
 
1916
- var xMin = stackedData[0][0].x;
1917
- var xMax = stackedData[0][ stackedData[0].length - 1 ].x;
2098
+ if (series[0].x < xMin) xMin = series[0].x;
2099
+ if (series[series.length - 1].x > xMax) xMax = series[series.length - 1].x;
2100
+ } );
1918
2101
 
1919
2102
  xMin -= (xMax - xMin) * this.padding.left;
1920
2103
  xMax += (xMax - xMin) * this.padding.right;
1921
2104
 
1922
- var yMin = this.graph.min === 'auto' ? d3.min( values ) : this.graph.min || 0;
1923
- var yMax = this.graph.max || d3.max( values );
2105
+ yMin = this.graph.min === 'auto' ? yMin : this.graph.min || 0;
2106
+ yMax = this.graph.max || yMax;
1924
2107
 
1925
2108
  if (this.graph.min === 'auto' || yMin < 0) {
1926
2109
  yMin -= (yMax - yMin) * this.padding.bottom;
@@ -2027,10 +2210,13 @@ Rickshaw.Graph.Renderer.Line = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2027
2210
 
2028
2211
  var graph = this.graph;
2029
2212
 
2030
- return d3.svg.line()
2213
+ var factory = d3.svg.line()
2031
2214
  .x( function(d) { return graph.x(d.x) } )
2032
2215
  .y( function(d) { return graph.y(d.y) } )
2033
- .interpolate(this.graph.interpolation).tension(this.tension);
2216
+ .interpolate(this.graph.interpolation).tension(this.tension)
2217
+
2218
+ factory.defined && factory.defined( function(d) { return d.y !== null } );
2219
+ return factory;
2034
2220
  }
2035
2221
  } );
2036
2222
 
@@ -2053,11 +2239,14 @@ Rickshaw.Graph.Renderer.Stack = Rickshaw.Class.create( Rickshaw.Graph.Renderer,
2053
2239
 
2054
2240
  var graph = this.graph;
2055
2241
 
2056
- return d3.svg.area()
2242
+ var factory = d3.svg.area()
2057
2243
  .x( function(d) { return graph.x(d.x) } )
2058
2244
  .y0( function(d) { return graph.y(d.y0) } )
2059
2245
  .y1( function(d) { return graph.y(d.y + d.y0) } )
2060
2246
  .interpolate(this.graph.interpolation).tension(this.tension);
2247
+
2248
+ factory.defined && factory.defined( function(d) { return d.y !== null } );
2249
+ return factory;
2061
2250
  }
2062
2251
  } );
2063
2252
 
@@ -2128,7 +2317,7 @@ Rickshaw.Graph.Renderer.Bar = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2128
2317
  if (series.disabled) return;
2129
2318
 
2130
2319
  var nodes = graph.vis.selectAll("path")
2131
- .data(series.stack)
2320
+ .data(series.stack.filter( function(d) { return d.y !== null } ))
2132
2321
  .enter().append("svg:rect")
2133
2322
  .attr("x", function(d) { return graph.x(d.x) + barXOffset })
2134
2323
  .attr("y", function(d) { return (graph.y(d.y0 + Math.abs(d.y))) * (d.y < 0 ? -1 : 1 ) })
@@ -2195,21 +2384,27 @@ Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2195
2384
 
2196
2385
  var graph = this.graph;
2197
2386
 
2198
- return d3.svg.area()
2387
+ var factory = d3.svg.area()
2199
2388
  .x( function(d) { return graph.x(d.x) } )
2200
2389
  .y0( function(d) { return graph.y(d.y0) } )
2201
2390
  .y1( function(d) { return graph.y(d.y + d.y0) } )
2202
- .interpolate(graph.interpolation).tension(this.tension);
2391
+ .interpolate(graph.interpolation).tension(this.tension)
2392
+
2393
+ factory.defined && factory.defined( function(d) { return d.y !== null } );
2394
+ return factory;
2203
2395
  },
2204
2396
 
2205
2397
  seriesStrokeFactory: function() {
2206
2398
 
2207
2399
  var graph = this.graph;
2208
2400
 
2209
- return d3.svg.line()
2401
+ var factory = d3.svg.line()
2210
2402
  .x( function(d) { return graph.x(d.x) } )
2211
2403
  .y( function(d) { return graph.y(d.y + d.y0) } )
2212
- .interpolate(graph.interpolation).tension(this.tension);
2404
+ .interpolate(graph.interpolation).tension(this.tension)
2405
+
2406
+ factory.defined && factory.defined( function(d) { return d.y !== null } );
2407
+ return factory;
2213
2408
  },
2214
2409
 
2215
2410
  render: function() {
@@ -2218,9 +2413,12 @@ Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2218
2413
 
2219
2414
  graph.vis.selectAll('*').remove();
2220
2415
 
2416
+ // insert or stacked areas so strokes lay on top of areas
2417
+ var method = this.unstack ? 'append' : 'insert';
2418
+
2221
2419
  var nodes = graph.vis.selectAll("path")
2222
2420
  .data(this.graph.stackedData)
2223
- .enter().insert("svg:g", 'g');
2421
+ .enter()[method]("svg:g", 'g');
2224
2422
 
2225
2423
  nodes.append("svg:path")
2226
2424
  .attr("d", this.seriesPathFactory())
@@ -2292,7 +2490,7 @@ Rickshaw.Graph.Renderer.ScatterPlot = Rickshaw.Class.create( Rickshaw.Graph.Rend
2292
2490
  if (series.disabled) return;
2293
2491
 
2294
2492
  var nodes = graph.vis.selectAll("path")
2295
- .data(series.stack)
2493
+ .data(series.stack.filter( function(d) { return d.y !== null } ))
2296
2494
  .enter().append("svg:circle")
2297
2495
  .attr("cx", function(d) { return graph.x(d.x) })
2298
2496
  .attr("cy", function(d) { return graph.y(d.y) })
@@ -2335,10 +2533,12 @@ Rickshaw.Graph.Smoother = function(args) {
2335
2533
  orderPosition: 50,
2336
2534
  f: function(data) {
2337
2535
 
2536
+ if (self.aggregationScale == 1) return data;
2537
+
2338
2538
  var aggregatedData = [];
2339
2539
 
2340
2540
  data.forEach( function(seriesData) {
2341
-
2541
+
2342
2542
  var aggregatedSeriesData = [];
2343
2543
 
2344
2544
  while (seriesData.length) {
@@ -2533,6 +2733,10 @@ Rickshaw.Series = Rickshaw.Class.create( Array, {
2533
2733
  } );
2534
2734
 
2535
2735
  Rickshaw.Series.zeroFill = function(series) {
2736
+ Rickshaw.Series.fill(series, 0);
2737
+ };
2738
+
2739
+ Rickshaw.Series.fill = function(series, fill) {
2536
2740
 
2537
2741
  var x;
2538
2742
  var i = 0;
@@ -2549,13 +2753,14 @@ Rickshaw.Series.zeroFill = function(series) {
2549
2753
 
2550
2754
  data.forEach( function(d) {
2551
2755
  if (!d[i] || d[i].x != x) {
2552
- d.splice(i, 0, { x: x, y: 0 });
2756
+ d.splice(i, 0, { x: x, y: fill });
2553
2757
  }
2554
2758
  } );
2555
2759
 
2556
2760
  i++;
2557
2761
  }
2558
2762
  };
2763
+
2559
2764
  Rickshaw.namespace('Rickshaw.Series.FixedDuration');
2560
2765
 
2561
2766
  Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, {
@@ -2635,4 +2840,3 @@ Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, {
2635
2840
  return this.currentIndex;
2636
2841
  }
2637
2842
  } );
2638
-