pghero 1.4.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pghero might be problematic. Click here for more details.

Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +8 -8
  4. data/app/assets/javascripts/pghero/Chart.bundle.js +183 -55
  5. data/app/assets/javascripts/pghero/chartkick.js +53 -20
  6. data/app/assets/stylesheets/pghero/application.css +7 -0
  7. data/app/controllers/pg_hero/home_controller.rb +61 -57
  8. data/app/views/layouts/pg_hero/application.html.erb +3 -3
  9. data/app/views/pg_hero/home/_connections_table.html.erb +1 -1
  10. data/app/views/pg_hero/home/_queries_table.html.erb +6 -1
  11. data/app/views/pg_hero/home/connections.html.erb +1 -1
  12. data/app/views/pg_hero/home/explain.html.erb +11 -2
  13. data/app/views/pg_hero/home/index.html.erb +4 -4
  14. data/app/views/pg_hero/home/maintenance.html.erb +2 -2
  15. data/app/views/pg_hero/home/system.html.erb +2 -2
  16. data/guides/Rails.md +8 -0
  17. data/lib/generators/pghero/space_stats_generator.rb +29 -0
  18. data/lib/generators/pghero/templates/space_stats.rb +13 -0
  19. data/lib/pghero.rb +109 -23
  20. data/lib/pghero/database.rb +46 -8
  21. data/lib/pghero/engine.rb +3 -1
  22. data/lib/pghero/methods/basic.rb +3 -42
  23. data/lib/pghero/methods/connections.rb +18 -1
  24. data/lib/pghero/methods/explain.rb +2 -0
  25. data/lib/pghero/methods/indexes.rb +2 -2
  26. data/lib/pghero/methods/kill.rb +1 -1
  27. data/lib/pghero/methods/queries.rb +2 -2
  28. data/lib/pghero/methods/query_stats.rb +81 -75
  29. data/lib/pghero/methods/space.rb +12 -1
  30. data/lib/pghero/methods/suggested_indexes.rb +71 -31
  31. data/lib/pghero/version.rb +1 -1
  32. data/lib/tasks/pghero.rake +5 -0
  33. metadata +4 -3
  34. data/lib/pghero/methods/databases.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9d9c1570453baf5503a063836e716a0ce38307a6
4
- data.tar.gz: 5a46751a5c8d2e95a801f963104256fbecbcb892
3
+ metadata.gz: 2f6dedb66d19e25c60630b97b92a84647ade3d2d
4
+ data.tar.gz: 0e5ca53ffe1bee7cb6a8be895cc974aacd58a1fa
5
5
  SHA512:
6
- metadata.gz: c745ed76512b199321bb518c3e914c9e96e1f89c521e72bd3f357db1c566b032fa1ddc5cc05978e24352f7e8a613e3479ea81065508b66b32cbaeca70846b2b0
7
- data.tar.gz: cdcd785bebe95c345c6152fa338fa617dad4f0efa7b287a2f692e2f5db5b8eba745ad4156612332784aa242c25cfcb139182aa71beb5fea0fb80959880bf80e8
6
+ metadata.gz: 6f971500f351580cdb9dfbdaaa9f4c5d9227c5a52672599b7553e20f2fdf5b14d3d8beb8d02a30e5a03aaaf8e105331bd559aef9100e0a34bd2e1292d1172a95
7
+ data.tar.gz: c464429ebfb5d25deee181b93586e7fe316351325253cc5c254fab6b76009df0f86916db4d3db615550a4c77a15902cfb789c36cca137237edca78a821d53496
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 1.5.0
2
+
3
+ - Added user to query stats (opt-in)
4
+ - Added user to connection sources
5
+ - Added `capture_space_stats` method and rake task
6
+ - Added visualize button to explain page
7
+ - Better charts for system stats
8
+
1
9
  ## 1.4.2
2
10
 
3
11
  - Fixed `wrong constant name` error in development
data/README.md CHANGED
@@ -12,17 +12,17 @@ A performance dashboard for Postgres
12
12
 
13
13
  PgHero can be installed as a standalone app or a Rails engine.
14
14
 
15
- ### Standalone
15
+ - [Linux](guides/Linux.md) - Ubuntu, Debian, and more
16
+ - [Docker](guides/Docker.md)
17
+ - [Heroku](guides/Heroku.md)
18
+ - [Rails](guides/Rails.md)
16
19
 
17
- [Linux](guides/Linux.md) - Ubuntu, Debian, and more
20
+ ## Related Projects
18
21
 
19
- [Docker](guides/Docker.md)
22
+ Also check out:
20
23
 
21
- [Heroku](guides/Heroku.md)
22
-
23
- ### Rails
24
-
25
- [Rails](guides/Rails.md)
24
+ - [pgsync](https://github.com/ankane/pgsync) - Sync Postgres data to your local machine
25
+ - [pgslice](https://github.com/ankane/pgslice) - Postgres partitioning as easy as pie
26
26
 
27
27
  ## Credits
28
28
 
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * Chart.js
3
3
  * http://chartjs.org/
4
- * Version: 2.2.1
4
+ * Version: 2.2.2
5
5
  *
6
6
  * Copyright 2016 Nick Downie
7
7
  * Released under the MIT license
@@ -6530,7 +6530,7 @@ module.exports = function(Chart) {
6530
6530
  categorySpacing: categorySpacing,
6531
6531
  fullBarHeight: fullBarHeight,
6532
6532
  barHeight: barHeight,
6533
- barSpacing: barSpacing,
6533
+ barSpacing: barSpacing
6534
6534
  };
6535
6535
  },
6536
6536
 
@@ -7111,6 +7111,7 @@ module.exports = function(Chart) {
7111
7111
  borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
7112
7112
  fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
7113
7113
  steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
7114
+ cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
7114
7115
  // Scale
7115
7116
  scaleTop: scale.top,
7116
7117
  scaleBottom: scale.bottom,
@@ -7194,6 +7195,8 @@ module.exports = function(Chart) {
7194
7195
  var xScale = me.getScaleForId(meta.xAxisID);
7195
7196
  var pointOptions = me.chart.options.elements.point;
7196
7197
  var x, y;
7198
+ var labels = me.chart.data.labels || [];
7199
+ var includeOffset = (labels.length === 1 || dataset.data.length === 1) || me.chart.isCombo;
7197
7200
 
7198
7201
  // Compatibility: If the properties are defined with only the old name, use those values
7199
7202
  if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
@@ -7203,7 +7206,7 @@ module.exports = function(Chart) {
7203
7206
  dataset.pointHitRadius = dataset.hitRadius;
7204
7207
  }
7205
7208
 
7206
- x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, me.chart.isCombo);
7209
+ x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, includeOffset);
7207
7210
  y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
7208
7211
 
7209
7212
  // Utility
@@ -7269,30 +7272,45 @@ module.exports = function(Chart) {
7269
7272
  var meta = me.getMeta();
7270
7273
  var area = me.chart.chartArea;
7271
7274
 
7272
- // only consider points that are drawn in case the spanGaps option is ued
7273
- var points = (meta.data || []).filter(function(pt) { return !pt._model.skip; });
7275
+ // Only consider points that are drawn in case the spanGaps option is used
7276
+ var points = (meta.data || []);
7277
+ if (meta.dataset._model.spanGaps) points = points.filter(function(pt) { return !pt._model.skip; });
7274
7278
  var i, ilen, point, model, controlPoints;
7275
7279
 
7276
- var needToCap = me.chart.options.elements.line.capBezierPoints;
7277
- function capIfNecessary(pt, min, max) {
7278
- return needToCap ? Math.max(Math.min(pt, max), min) : pt;
7280
+ function capControlPoint(pt, min, max) {
7281
+ return Math.max(Math.min(pt, max), min);
7279
7282
  }
7280
7283
 
7281
- for (i=0, ilen=points.length; i<ilen; ++i) {
7282
- point = points[i];
7283
- model = point._model;
7284
- controlPoints = helpers.splineCurve(
7285
- helpers.previousItem(points, i)._model,
7286
- model,
7287
- helpers.nextItem(points, i)._model,
7288
- meta.dataset._model.tension
7289
- );
7284
+ if (meta.dataset._model.cubicInterpolationMode == 'monotone') {
7285
+ helpers.splineCurveMonotone(points);
7286
+ }
7287
+ else {
7288
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
7289
+ point = points[i];
7290
+ model = point._model;
7291
+ controlPoints = helpers.splineCurve(
7292
+ helpers.previousItem(points, i)._model,
7293
+ model,
7294
+ helpers.nextItem(points, i)._model,
7295
+ meta.dataset._model.tension
7296
+ );
7297
+ model.controlPointPreviousX = controlPoints.previous.x;
7298
+ model.controlPointPreviousY = controlPoints.previous.y;
7299
+ model.controlPointNextX = controlPoints.next.x;
7300
+ model.controlPointNextY = controlPoints.next.y;
7301
+ }
7302
+ }
7290
7303
 
7291
- model.controlPointPreviousX = capIfNecessary(controlPoints.previous.x, area.left, area.right);
7292
- model.controlPointPreviousY = capIfNecessary(controlPoints.previous.y, area.top, area.bottom);
7293
- model.controlPointNextX = capIfNecessary(controlPoints.next.x, area.left, area.right);
7294
- model.controlPointNextY = capIfNecessary(controlPoints.next.y, area.top, area.bottom);
7304
+ if (me.chart.options.elements.line.capBezierPoints) {
7305
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
7306
+ model = points[i]._model;
7307
+ model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
7308
+ model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
7309
+ model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
7310
+ model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
7311
+ }
7295
7312
  }
7313
+
7296
7314
  },
7297
7315
 
7298
7316
  draw: function(ease) {
@@ -9298,6 +9316,77 @@ module.exports = function(Chart) {
9298
9316
  }
9299
9317
  };
9300
9318
  };
9319
+ helpers.EPSILON = Number.EPSILON || 1e-14;
9320
+ helpers.splineCurveMonotone = function(points) {
9321
+ // This function calculates Bézier control points in a similar way than |splineCurve|,
9322
+ // but preserves monotonicity of the provided data and ensures no local extremums are added
9323
+ // between the dataset discrete points due to the interpolation.
9324
+ // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
9325
+
9326
+ var pointsWithTangents = (points || []).map(function(point) {
9327
+ return {
9328
+ model: point._model,
9329
+ deltaK: 0,
9330
+ mK: 0
9331
+ };
9332
+ });
9333
+
9334
+ // Calculate slopes (deltaK) and initialize tangents (mK)
9335
+ var pointsLen = pointsWithTangents.length;
9336
+ var i, pointBefore, pointCurrent, pointAfter;
9337
+ for (i = 0; i < pointsLen; ++i) {
9338
+ pointCurrent = pointsWithTangents[i];
9339
+ if (pointCurrent.model.skip) continue;
9340
+ pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
9341
+ pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
9342
+ if (pointAfter && !pointAfter.model.skip) {
9343
+ pointCurrent.deltaK = (pointAfter.model.y - pointCurrent.model.y) / (pointAfter.model.x - pointCurrent.model.x);
9344
+ }
9345
+ if (!pointBefore || pointBefore.model.skip) pointCurrent.mK = pointCurrent.deltaK;
9346
+ else if (!pointAfter || pointAfter.model.skip) pointCurrent.mK = pointBefore.deltaK;
9347
+ else if (this.sign(pointBefore.deltaK) != this.sign(pointCurrent.deltaK)) pointCurrent.mK = 0;
9348
+ else pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
9349
+ }
9350
+
9351
+ // Adjust tangents to ensure monotonic properties
9352
+ var alphaK, betaK, tauK, squaredMagnitude;
9353
+ for (i = 0; i < pointsLen - 1; ++i) {
9354
+ pointCurrent = pointsWithTangents[i];
9355
+ pointAfter = pointsWithTangents[i + 1];
9356
+ if (pointCurrent.model.skip || pointAfter.model.skip) continue;
9357
+ if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON))
9358
+ {
9359
+ pointCurrent.mK = pointAfter.mK = 0;
9360
+ continue;
9361
+ }
9362
+ alphaK = pointCurrent.mK / pointCurrent.deltaK;
9363
+ betaK = pointAfter.mK / pointCurrent.deltaK;
9364
+ squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
9365
+ if (squaredMagnitude <= 9) continue;
9366
+ tauK = 3 / Math.sqrt(squaredMagnitude);
9367
+ pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
9368
+ pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
9369
+ }
9370
+
9371
+ // Compute control points
9372
+ var deltaX;
9373
+ for (i = 0; i < pointsLen; ++i) {
9374
+ pointCurrent = pointsWithTangents[i];
9375
+ if (pointCurrent.model.skip) continue;
9376
+ pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
9377
+ pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
9378
+ if (pointBefore && !pointBefore.model.skip) {
9379
+ deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
9380
+ pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
9381
+ pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
9382
+ }
9383
+ if (pointAfter && !pointAfter.model.skip) {
9384
+ deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
9385
+ pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
9386
+ pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
9387
+ }
9388
+ }
9389
+ };
9301
9390
  helpers.nextItem = function(collection, index, loop) {
9302
9391
  if (loop) {
9303
9392
  return index >= collection.length - 1 ? collection[0] : collection[index + 1];
@@ -9835,6 +9924,7 @@ module.exports = function(Chart) {
9835
9924
  }
9836
9925
 
9837
9926
  // Set the style
9927
+ hiddenIframe.tabIndex = -1;
9838
9928
  var style = hiddenIframe.style;
9839
9929
  style.width = '100%';
9840
9930
  style.display = 'block';
@@ -10961,7 +11051,9 @@ module.exports = function(Chart) {
10961
11051
  tickMarkLength: 10,
10962
11052
  zeroLineWidth: 1,
10963
11053
  zeroLineColor: "rgba(0,0,0,0.25)",
10964
- offsetGridLines: false
11054
+ offsetGridLines: false,
11055
+ borderDash: [],
11056
+ borderDashOffset: 0.0
10965
11057
  },
10966
11058
 
10967
11059
  // scale label
@@ -11217,6 +11309,7 @@ module.exports = function(Chart) {
11217
11309
  var globalDefaults = Chart.defaults.global;
11218
11310
  var tickOpts = opts.ticks;
11219
11311
  var scaleLabelOpts = opts.scaleLabel;
11312
+ var gridLineOpts = opts.gridLines;
11220
11313
  var display = opts.display;
11221
11314
  var isHorizontal = me.isHorizontal();
11222
11315
 
@@ -11234,12 +11327,12 @@ module.exports = function(Chart) {
11234
11327
  // subtract the margins to line up with the chartArea if we are a full width scale
11235
11328
  minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
11236
11329
  } else {
11237
- minSize.width = display ? tickMarkLength : 0;
11330
+ minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
11238
11331
  }
11239
11332
 
11240
11333
  // height
11241
11334
  if (isHorizontal) {
11242
- minSize.height = display ? tickMarkLength : 0;
11335
+ minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
11243
11336
  } else {
11244
11337
  minSize.height = me.maxHeight; // fill all the height
11245
11338
  }
@@ -11444,6 +11537,8 @@ module.exports = function(Chart) {
11444
11537
  var tickFontFamily = helpers.getValueOrDefault(optionTicks.fontFamily, globalDefaults.defaultFontFamily);
11445
11538
  var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
11446
11539
  var tl = gridLines.tickMarkLength;
11540
+ var borderDash = helpers.getValueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
11541
+ var borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
11447
11542
 
11448
11543
  var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
11449
11544
  var scaleLabelFontSize = helpers.getValueOrDefault(scaleLabel.fontSize, globalDefaults.defaultFontSize);
@@ -11583,6 +11678,8 @@ module.exports = function(Chart) {
11583
11678
  labelY: labelY,
11584
11679
  glWidth: lineWidth,
11585
11680
  glColor: lineColor,
11681
+ glBorderDash: borderDash,
11682
+ glBorderDashOffset: borderDashOffset,
11586
11683
  rotation: -1 * labelRotationRadians,
11587
11684
  label: label,
11588
11685
  textBaseline: textBaseline,
@@ -11593,8 +11690,13 @@ module.exports = function(Chart) {
11593
11690
  // Draw all of the tick labels, tick marks, and grid lines at the correct places
11594
11691
  helpers.each(itemsToDraw, function(itemToDraw) {
11595
11692
  if (gridLines.display) {
11693
+ context.save();
11596
11694
  context.lineWidth = itemToDraw.glWidth;
11597
11695
  context.strokeStyle = itemToDraw.glColor;
11696
+ if (context.setLineDash) {
11697
+ context.setLineDash(itemToDraw.glBorderDash);
11698
+ context.lineDashOffset = itemToDraw.glBorderDashOffset;
11699
+ }
11598
11700
 
11599
11701
  context.beginPath();
11600
11702
 
@@ -11609,6 +11711,7 @@ module.exports = function(Chart) {
11609
11711
  }
11610
11712
 
11611
11713
  context.stroke();
11714
+ context.restore();
11612
11715
  }
11613
11716
 
11614
11717
  if (optionTicks.display) {
@@ -12232,7 +12335,9 @@ module.exports = function(Chart) {
12232
12335
 
12233
12336
  // If the user provided a sorting function, use it to modify the tooltip items
12234
12337
  if (opts.itemSort) {
12235
- tooltipItems = tooltipItems.sort(opts.itemSort);
12338
+ tooltipItems = tooltipItems.sort(function(a,b) {
12339
+ return opts.itemSort(a,b, data);
12340
+ });
12236
12341
  }
12237
12342
 
12238
12343
  // If there is more than one item, show color items
@@ -12867,7 +12972,7 @@ module.exports = function(Chart) {
12867
12972
  }
12868
12973
  }
12869
12974
 
12870
- if (!loop) {
12975
+ if (!loop && lastDrawnIndex !== -1) {
12871
12976
  ctx.lineTo(points[lastDrawnIndex]._view.x, scaleZero);
12872
12977
  }
12873
12978
 
@@ -12901,9 +13006,7 @@ module.exports = function(Chart) {
12901
13006
 
12902
13007
  // First point moves to it's starting position no matter what
12903
13008
  if (index === 0) {
12904
- if (currentVM.skip) {
12905
-
12906
- } else {
13009
+ if (!currentVM.skip) {
12907
13010
  ctx.moveTo(currentVM.x, currentVM.y);
12908
13011
  lastDrawnIndex = index;
12909
13012
  }
@@ -13105,7 +13208,7 @@ module.exports = function(Chart) {
13105
13208
  // Implement this so that
13106
13209
  determineDataLimits: function() {
13107
13210
  var me = this;
13108
- var labels = me.getLabels();
13211
+ var labels = me.getLabels();
13109
13212
  me.minIndex = 0;
13110
13213
  me.maxIndex = labels.length - 1;
13111
13214
  var findIndex;
@@ -13143,7 +13246,7 @@ module.exports = function(Chart) {
13143
13246
  // 1 is added because we need the length but we have the indexes
13144
13247
  var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
13145
13248
 
13146
- if (value !== undefined) {
13249
+ if (value !== undefined && isNaN(index)) {
13147
13250
  var labels = me.getLabels();
13148
13251
  var idx = labels.indexOf(value);
13149
13252
  index = idx !== -1 ? idx : index;
@@ -13154,9 +13257,9 @@ module.exports = function(Chart) {
13154
13257
  var valueWidth = innerWidth / offsetAmt;
13155
13258
  var widthOffset = (valueWidth * (index - me.minIndex)) + me.paddingLeft;
13156
13259
 
13157
- if (me.options.gridLines.offsetGridLines && includeOffset) {
13260
+ if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) {
13158
13261
  widthOffset += (valueWidth / 2);
13159
- }
13262
+ }
13160
13263
 
13161
13264
  return me.left + Math.round(widthOffset);
13162
13265
  } else {
@@ -13528,7 +13631,7 @@ module.exports = function(Chart) {
13528
13631
  me.zeroLineIndex = me.ticks.indexOf(0);
13529
13632
 
13530
13633
  Chart.Scale.prototype.convertTicksToLabels.call(me);
13531
- },
13634
+ }
13532
13635
  });
13533
13636
  };
13534
13637
  },{}],42:[function(require,module,exports){
@@ -13546,7 +13649,9 @@ module.exports = function(Chart) {
13546
13649
  callback: function(value, index, arr) {
13547
13650
  var remain = value / (Math.pow(10, Math.floor(helpers.log10(value))));
13548
13651
 
13549
- if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
13652
+ if (value === 0){
13653
+ return '0';
13654
+ } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === arr.length - 1) {
13550
13655
  return value.toExponential();
13551
13656
  } else {
13552
13657
  return '';
@@ -13572,6 +13677,7 @@ module.exports = function(Chart) {
13572
13677
  // Calculate Range
13573
13678
  me.min = null;
13574
13679
  me.max = null;
13680
+ me.minNotZero = null;
13575
13681
 
13576
13682
  if (opts.stacked) {
13577
13683
  var valuesPerType = {};
@@ -13630,6 +13736,10 @@ module.exports = function(Chart) {
13630
13736
  } else if (value > me.max) {
13631
13737
  me.max = value;
13632
13738
  }
13739
+
13740
+ if(value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
13741
+ me.minNotZero = value;
13742
+ }
13633
13743
  });
13634
13744
  }
13635
13745
  });
@@ -13668,8 +13778,16 @@ module.exports = function(Chart) {
13668
13778
  while (tickVal < me.max) {
13669
13779
  ticks.push(tickVal);
13670
13780
 
13671
- var exp = Math.floor(helpers.log10(tickVal));
13672
- var significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
13781
+ var exp;
13782
+ var significand;
13783
+
13784
+ if(tickVal === 0){
13785
+ exp = Math.floor(helpers.log10(me.minNotZero));
13786
+ significand = Math.round(me.minNotZero / Math.pow(10, exp));
13787
+ } else {
13788
+ exp = Math.floor(helpers.log10(tickVal));
13789
+ significand = Math.floor(tickVal / Math.pow(10, exp)) + 1;
13790
+ }
13673
13791
 
13674
13792
  if (significand === 10) {
13675
13793
  significand = 1;
@@ -13721,13 +13839,15 @@ module.exports = function(Chart) {
13721
13839
 
13722
13840
  var start = me.start;
13723
13841
  var newVal = +me.getRightValue(value);
13724
- var range = helpers.log10(me.end) - helpers.log10(start);
13842
+ var range;
13725
13843
  var paddingTop = me.paddingTop;
13726
13844
  var paddingBottom = me.paddingBottom;
13727
13845
  var paddingLeft = me.paddingLeft;
13846
+ var opts = me.options;
13847
+ var tickOpts = opts.ticks;
13728
13848
 
13729
13849
  if (me.isHorizontal()) {
13730
-
13850
+ range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
13731
13851
  if (newVal === 0) {
13732
13852
  pixel = me.left + paddingLeft;
13733
13853
  } else {
@@ -13737,14 +13857,31 @@ module.exports = function(Chart) {
13737
13857
  }
13738
13858
  } else {
13739
13859
  // Bottom - top since pixels increase downard on a screen
13740
- if (newVal === 0) {
13741
- pixel = me.top + paddingTop;
13860
+ innerDimension = me.height - (paddingTop + paddingBottom);
13861
+ if(start === 0 && !tickOpts.reverse){
13862
+ range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
13863
+ if (newVal === start) {
13864
+ pixel = me.bottom - paddingBottom;
13865
+ } else if(newVal === me.minNotZero){
13866
+ pixel = me.bottom - paddingBottom - innerDimension * 0.02;
13867
+ } else {
13868
+ pixel = me.bottom - paddingBottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
13869
+ }
13870
+ } else if (me.end === 0 && tickOpts.reverse){
13871
+ range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
13872
+ if (newVal === me.end) {
13873
+ pixel = me.top + paddingTop;
13874
+ } else if(newVal === me.minNotZero){
13875
+ pixel = me.top + paddingTop + innerDimension * 0.02;
13876
+ } else {
13877
+ pixel = me.top + paddingTop + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
13878
+ }
13742
13879
  } else {
13880
+ range = helpers.log10(me.end) - helpers.log10(start);
13743
13881
  innerDimension = me.height - (paddingTop + paddingBottom);
13744
13882
  pixel = (me.bottom - paddingBottom) - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
13745
- }
13883
+ }
13746
13884
  }
13747
-
13748
13885
  return pixel;
13749
13886
  },
13750
13887
  getValueForPixel: function(pixel) {
@@ -13755,11 +13892,10 @@ module.exports = function(Chart) {
13755
13892
  if (me.isHorizontal()) {
13756
13893
  innerDimension = me.width - (me.paddingLeft + me.paddingRight);
13757
13894
  value = me.start * Math.pow(10, (pixel - me.left - me.paddingLeft) * range / innerDimension);
13758
- } else {
13895
+ } else { // todo: if start === 0
13759
13896
  innerDimension = me.height - (me.paddingTop + me.paddingBottom);
13760
13897
  value = Math.pow(10, (me.bottom - me.paddingBottom - pixel) * range / innerDimension) / me.start;
13761
13898
  }
13762
-
13763
13899
  return value;
13764
13900
  }
13765
13901
  });
@@ -14443,14 +14579,6 @@ module.exports = function(Chart) {
14443
14579
  me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
14444
14580
  }
14445
14581
 
14446
- me.smallestLabelSeparation = me.width;
14447
-
14448
- helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
14449
- for (var i = 1; i < me.labelMoments[datasetIndex].length; i++) {
14450
- me.smallestLabelSeparation = Math.min(me.smallestLabelSeparation, me.labelMoments[datasetIndex][i].diff(me.labelMoments[datasetIndex][i - 1], me.tickUnit, true));
14451
- }
14452
- }, me);
14453
-
14454
14582
  // Tick displayFormat override
14455
14583
  if (me.options.time.displayFormat) {
14456
14584
  me.displayFormat = me.options.time.displayFormat;
@@ -14481,7 +14609,7 @@ module.exports = function(Chart) {
14481
14609
  if (me.options.time.max) {
14482
14610
  me.ticks.push(me.lastTick.clone());
14483
14611
  me.scaleSizeInUnits = me.lastTick.diff(me.ticks[0], me.tickUnit, true);
14484
- } else {
14612
+ } else if (me.scaleSizeInUnits === 0) {
14485
14613
  me.ticks.push(me.lastTick.clone());
14486
14614
  me.scaleSizeInUnits = me.lastTick.diff(me.firstTick, me.tickUnit, true);
14487
14615
  }
@@ -14526,7 +14654,7 @@ module.exports = function(Chart) {
14526
14654
  var me = this;
14527
14655
  if (!value || !value.isValid) {
14528
14656
  // not already a moment object
14529
- value = moment(me.getRightValue(value));
14657
+ value = me.parseTime(me.getRightValue(value));
14530
14658
  }
14531
14659
  var labelMoment = value && value.isValid && value.isValid() ? value : me.getLabelMoment(datasetIndex, index);
14532
14660