@@ -658,9 +658,18 @@ axes.calcTicks = function calcTicks(ax) {
658
658
// show the exponent only on the last one
659
659
ax . _tmax = vals [ vals . length - 1 ] ;
660
660
661
+ // for showing date suffixes: ax._prevSuffix holds what we showed most
662
+ // recently. Start with it cleared and mark that we're in calcTicks (ie
663
+ // calculating a whole string of these so we should care what the previous
664
+ // suffix was!)
665
+ ax . _prevSuffix = '' ;
666
+ ax . _inCalcTicks = true ;
667
+
661
668
var ticksOut = new Array ( vals . length ) ;
662
669
for ( var i = 0 ; i < vals . length ; i ++ ) ticksOut [ i ] = axes . tickText ( ax , vals [ i ] ) ;
663
670
671
+ ax . _inCalcTicks = false ;
672
+
664
673
return ticksOut ;
665
674
} ;
666
675
@@ -815,6 +824,11 @@ axes.autoTicks = function(ax, roughDTick) {
815
824
}
816
825
} ;
817
826
827
+ var ONEDAY = 86400000 ,
828
+ ONEHOUR = 3600000 ,
829
+ ONEMIN = 60000 ,
830
+ ONESEC = 1000 ;
831
+
818
832
// after dtick is already known, find tickround = precision
819
833
// to display in tick labels
820
834
// for numeric ticks, integer # digits after . to round to
@@ -831,33 +845,43 @@ function autoTickRound(ax) {
831
845
if ( ax . type === 'category' ) {
832
846
ax . _tickround = null ;
833
847
}
848
+ if ( ax . type === 'date' ) {
849
+ // If tick0 is unusual, give tickround a bit more information
850
+ // not necessarily *all* the information in tick0 though, if it's really odd
851
+ // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19
852
+ // take off a leading minus (year < 0 so length is consistent)
853
+ var tick0str = Lib . ms2DateTime ( Lib . dateTime2ms ( ax . tick0 ) ) . replace ( / ^ - / , '' ) ,
854
+ tick0len = tick0str . length ;
855
+
856
+ if ( String ( dtick ) . charAt ( 0 ) === 'M' ) {
857
+ // any tick0 more specific than a year: alway show the full date
858
+ if ( tick0len > 10 || tick0str . substr ( 5 ) !== '01-01' ) ax . _tickround = 'd' ;
859
+ // show the month unless ticks are full multiples of a year
860
+ else ax . _tickround = ( + ( dtick . substr ( 1 ) ) % 12 === 0 ) ? 'y' : 'm' ;
861
+ }
862
+ else if ( ( dtick >= ONEDAY && tick0len <= 10 ) || ( dtick >= ONEDAY * 15 ) ) ax . _tickround = 'd' ;
863
+ else if ( ( dtick >= ONEMIN && tick0len <= 16 ) || ( dtick >= ONEHOUR ) ) ax . _tickround = 'M' ;
864
+ else if ( ( dtick >= ONESEC && tick0len <= 19 ) || ( dtick >= ONEMIN ) ) ax . _tickround = 'S' ;
865
+ else ax . _tickround = Math . max ( 3 - Math . round ( Math . log ( dtick / 2 ) / Math . LN10 ) , tick0len - 20 ) ;
866
+ }
834
867
else if ( isNumeric ( dtick ) || dtick . charAt ( 0 ) === 'L' ) {
835
- if ( ax . type === 'date' ) {
836
- if ( dtick >= 86400000 ) ax . _tickround = 'd' ;
837
- else if ( dtick >= 3600000 ) ax . _tickround = 'H' ;
838
- else if ( dtick >= 60000 ) ax . _tickround = 'M' ;
839
- else if ( dtick >= 1000 ) ax . _tickround = 'S' ;
840
- else ax . _tickround = 3 - Math . round ( Math . log ( dtick / 2 ) / Math . LN10 ) ;
841
- }
842
- else {
843
- // linear or log
844
- var rng = ax . range . map ( ax . r2d || Number ) ;
845
- if ( ! isNumeric ( dtick ) ) dtick = Number ( dtick . substr ( 1 ) ) ;
846
- // 2 digits past largest digit of dtick
847
- ax . _tickround = 2 - Math . floor ( Math . log ( dtick ) / Math . LN10 + 0.01 ) ;
848
-
849
- var maxend = Math . max ( Math . abs ( rng [ 0 ] ) , Math . abs ( rng [ 1 ] ) ) ;
850
-
851
- var rangeexp = Math . floor ( Math . log ( maxend ) / Math . LN10 + 0.01 ) ;
852
- if ( Math . abs ( rangeexp ) > 3 ) {
853
- if ( ax . exponentformat === 'SI' || ax . exponentformat === 'B' ) {
854
- ax . _tickexponent = 3 * Math . round ( ( rangeexp - 1 ) / 3 ) ;
855
- }
856
- else ax . _tickexponent = rangeexp ;
868
+ // linear or log (except D1, D2)
869
+ var rng = ax . range . map ( ax . r2d || Number ) ;
870
+ if ( ! isNumeric ( dtick ) ) dtick = Number ( dtick . substr ( 1 ) ) ;
871
+ // 2 digits past largest digit of dtick
872
+ ax . _tickround = 2 - Math . floor ( Math . log ( dtick ) / Math . LN10 + 0.01 ) ;
873
+
874
+ var maxend = Math . max ( Math . abs ( rng [ 0 ] ) , Math . abs ( rng [ 1 ] ) ) ;
875
+
876
+ var rangeexp = Math . floor ( Math . log ( maxend ) / Math . LN10 + 0.01 ) ;
877
+ if ( Math . abs ( rangeexp ) > 3 ) {
878
+ if ( ax . exponentformat === 'SI' || ax . exponentformat === 'B' ) {
879
+ ax . _tickexponent = 3 * Math . round ( ( rangeexp - 1 ) / 3 ) ;
857
880
}
881
+ else ax . _tickexponent = rangeexp ;
858
882
}
859
883
}
860
- else if ( dtick . charAt ( 0 ) === 'M' ) ax . _tickround = ( dtick . length === 2 ) ? 'm' : 'y' ;
884
+ // D1 or D2 (log)
861
885
else ax . _tickround = null ;
862
886
}
863
887
@@ -961,7 +985,7 @@ axes.tickFirst = function(ax) {
961
985
var yearFormat = d3 . time . format ( '%Y' ) ,
962
986
monthFormat = d3 . time . format ( '%b %Y' ) ,
963
987
dayFormat = d3 . time . format ( '%b %-d' ) ,
964
- hourFormat = d3 . time . format ( '%b %-d %Hh ' ) ,
988
+ yearMonthDayFormat = d3 . time . format ( '%b %-d, %Y ' ) ,
965
989
minuteFormat = d3 . time . format ( '%H:%M' ) ,
966
990
secondFormat = d3 . time . format ( ':%S' ) ;
967
991
@@ -1053,10 +1077,12 @@ function tickTextObj(ax, x, text) {
1053
1077
function formatDate ( ax , out , hover , extraPrecision ) {
1054
1078
var x = out . x ,
1055
1079
tr = ax . _tickround ,
1080
+ trOriginal = tr ,
1056
1081
d = new Date ( x ) ,
1057
1082
// suffix completes the full date info, to be included
1058
- // with only the first tick
1059
- suffix = '' ,
1083
+ // with only the first tick or if any info before what's
1084
+ // shown has changed
1085
+ suffix ,
1060
1086
tt ;
1061
1087
if ( hover && ax . hoverformat ) {
1062
1088
tt = modDateFormat ( ax . hoverformat , x ) ;
@@ -1069,21 +1095,18 @@ function formatDate(ax, out, hover, extraPrecision) {
1069
1095
else {
1070
1096
if ( extraPrecision ) {
1071
1097
if ( isNumeric ( tr ) ) tr += 2 ;
1072
- else tr = { y : 'm' , m : 'd' , d : 'H' , H : ' M', M : 'S' , S : 2 } [ tr ] ;
1098
+ else tr = { y : 'm' , m : 'd' , d : 'M' , M : 'S' , S : 2 } [ tr ] ;
1073
1099
}
1074
1100
if ( tr === 'y' ) tt = yearFormat ( d ) ;
1075
1101
else if ( tr === 'm' ) tt = monthFormat ( d ) ;
1076
1102
else {
1077
- if ( x === ax . _tmin && ! hover ) {
1078
- suffix = '<br>' + yearFormat ( d ) ;
1079
- }
1103
+ if ( tr === 'd' ) {
1104
+ if ( ! hover ) suffix = '<br>' + yearFormat ( d ) ;
1080
1105
1081
- if ( tr === 'd' ) tt = dayFormat ( d ) ;
1082
- else if ( tr === 'H' ) tt = hourFormat ( d ) ;
1106
+ tt = dayFormat ( d ) ;
1107
+ }
1083
1108
else {
1084
- if ( x === ax . _tmin && ! hover ) {
1085
- suffix = '<br>' + dayFormat ( d ) + ', ' + yearFormat ( d ) ;
1086
- }
1109
+ if ( ! hover ) suffix = '<br>' + yearMonthDayFormat ( d ) ;
1087
1110
1088
1111
tt = minuteFormat ( d ) ;
1089
1112
if ( tr !== 'M' ) {
@@ -1093,10 +1116,19 @@ function formatDate(ax, out, hover, extraPrecision) {
1093
1116
. substr ( 1 ) ;
1094
1117
}
1095
1118
}
1119
+ else if ( trOriginal === 'd' ) {
1120
+ // for hover on axes with day ticks, minuteFormat (which
1121
+ // only includes %H:%M) isn't enough, you want the date too
1122
+ tt = dayFormat ( d ) + ' ' + tt ;
1123
+ }
1096
1124
}
1097
1125
}
1098
1126
}
1099
- out . text = tt + suffix ;
1127
+ if ( suffix && ( ! ax . _inCalcTicks || ( suffix !== ax . _prevSuffix ) ) ) {
1128
+ tt += suffix ;
1129
+ ax . _prevSuffix = suffix ;
1130
+ }
1131
+ out . text = tt ;
1100
1132
}
1101
1133
1102
1134
function formatLog ( ax , out , hover , extraPrecision , hideexp ) {
0 commit comments