highstock-rails 1.3.10 → 2.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/assets/images/highstock/meteogram-symbols-30px.png +0 -0
- data/app/assets/javascripts/highstock.js +418 -369
- data/app/assets/javascripts/highstock/adapters/standalone-framework.js +12 -12
- data/app/assets/javascripts/highstock/adapters/standalone-framework.src.js +635 -0
- data/app/assets/javascripts/highstock/highcharts-3d.js +48 -0
- data/app/assets/javascripts/highstock/highcharts-3d.src.js +1711 -0
- data/app/assets/javascripts/highstock/highcharts-more.js +49 -45
- data/app/assets/javascripts/highstock/highstock-all.js +637 -0
- data/app/assets/javascripts/highstock/modules/boost.js +12 -0
- data/app/assets/javascripts/highstock/modules/boost.src.js +591 -0
- data/app/assets/javascripts/highstock/modules/canvas-tools.js +9 -9
- data/app/assets/javascripts/highstock/modules/canvas-tools.src.js +3114 -0
- data/app/assets/javascripts/highstock/modules/data.js +20 -10
- data/app/assets/javascripts/highstock/modules/data.src.js +957 -0
- data/app/assets/javascripts/highstock/modules/drilldown.js +17 -14
- data/app/assets/javascripts/highstock/modules/drilldown.src.js +717 -0
- data/app/assets/javascripts/highstock/modules/exporting.js +17 -15
- data/app/assets/javascripts/highstock/modules/exporting.src.js +780 -0
- data/app/assets/javascripts/highstock/modules/funnel.js +5 -5
- data/app/assets/javascripts/highstock/modules/funnel.src.js +322 -0
- data/app/assets/javascripts/highstock/modules/heatmap.js +23 -2
- data/app/assets/javascripts/highstock/modules/heatmap.src.js +711 -0
- data/app/assets/javascripts/highstock/modules/no-data-to-display.js +4 -4
- data/app/assets/javascripts/highstock/modules/no-data-to-display.src.js +143 -0
- data/app/assets/javascripts/highstock/modules/offline-exporting.js +14 -0
- data/app/assets/javascripts/highstock/modules/offline-exporting.src.js +280 -0
- data/app/assets/javascripts/highstock/modules/solid-gauge.js +14 -0
- data/app/assets/javascripts/highstock/modules/solid-gauge.src.js +273 -0
- data/app/assets/javascripts/highstock/modules/treemap.js +30 -0
- data/app/assets/javascripts/highstock/modules/treemap.src.js +868 -0
- data/app/assets/javascripts/highstock/themes/dark-blue.js +1 -1
- data/app/assets/javascripts/highstock/themes/dark-green.js +1 -1
- data/app/assets/javascripts/highstock/themes/dark-unica.js +213 -0
- data/app/assets/javascripts/highstock/themes/gray.js +1 -1
- data/app/assets/javascripts/highstock/themes/grid-light.js +74 -0
- data/app/assets/javascripts/highstock/themes/sand-signika.js +104 -0
- data/lib/highstock/rails/version.rb +1 -1
- metadata +26 -7
- data/app/assets/javascripts/highstock/adapters/mootools-adapter.js +0 -13
- data/app/assets/javascripts/highstock/adapters/prototype-adapter.js +0 -15
- data/app/assets/javascripts/highstock/modules/annotations.js +0 -7
- data/app/assets/javascripts/highstock/modules/map.js +0 -41
@@ -0,0 +1,12 @@
|
|
1
|
+
(function(c){typeof module==="object"&&module.exports?module.exports=c:c(Highcharts)})(function(c){function u(a,b,d,e,f){f=f||0;e=e||v;k(a.slice(f,f+e),b);f+e<a.length?setTimeout(function(){u(a,b,d,e,f+e)}):d&&d()}var L=function(){},M=c.Color,j=c.Series,h=c.seriesTypes,k=c.each,p=c.extend,N=c.addEvent,O=c.fireEvent,P=c.merge,Q=c.pick,g=c.wrap,n=c.getOptions().plotOptions,v=5E4;k(["area","arearange","column","line","scatter"],function(a){if(n[a])n[a].boostThreshold=5E3});k(["translate","generatePoints",
|
2
|
+
"drawTracker","drawPoints","render"],function(a){function b(b){var e=this.options.stacking&&(a==="translate"||a==="generatePoints");if((this.processedXData||this.options.data).length<(this.options.boostThreshold||Number.MAX_VALUE)||e){if(a==="render"&&this.image)this.image.attr({href:""}),this.animate=null;b.call(this)}else if(this[a+"Canvas"])this[a+"Canvas"]()}g(j.prototype,a,b);a==="translate"&&(h.column&&g(h.column.prototype,a,b),h.arearange&&g(h.arearange.prototype,a,b))});g(j.prototype,"getExtremes",
|
3
|
+
function(a){this.hasExtremes()||a.apply(this,Array.prototype.slice.call(arguments,1))});g(j.prototype,"setData",function(a){this.hasExtremes(!0)||a.apply(this,Array.prototype.slice.call(arguments,1))});g(j.prototype,"processData",function(a){this.hasExtremes(!0)||a.apply(this,Array.prototype.slice.call(arguments,1))});c.extend(j.prototype,{pointRange:0,hasExtremes:function(a){var b=this.options,d=this.xAxis&&this.xAxis.options,e=this.yAxis&&this.yAxis.options;return b.data.length>(b.boostThreshold||
|
4
|
+
Number.MAX_VALUE)&&typeof e.min==="number"&&typeof e.max==="number"&&(!a||typeof d.min==="number"&&typeof d.max==="number")},destroyGraphics:function(){var a=this,b=this.points,d,e;if(b)for(e=0;e<b.length;e+=1)if((d=b[e])&&d.graphic)d.graphic=d.graphic.destroy();k(["graph","area","tracker"],function(b){a[b]&&(a[b]=a[b].destroy())})},getContext:function(){var a=this.chart,b=a.plotWidth,d=a.plotHeight,e=this.ctx,f=function(a,b,d,e,f,c,i){a.call(this,d,b,e,f,c,i)};this.canvas?e.clearRect(0,0,b,d):(this.canvas=
|
5
|
+
document.createElement("canvas"),this.image=a.renderer.image("",0,0,b,d).add(this.group),this.ctx=e=this.canvas.getContext("2d"),a.inverted&&k(["moveTo","lineTo","rect","arc"],function(a){g(e,a,f)}));this.canvas.setAttribute("width",b);this.canvas.setAttribute("height",d);this.image.attr({width:b,height:d});return e},canvasToSVG:function(){this.image.attr({href:this.canvas.toDataURL("image/png")})},cvsLineTo:function(a,b,d){a.lineTo(b,d)},renderCanvas:function(){var a=this,b=a.options,d=a.chart,e=
|
6
|
+
this.xAxis,f=this.yAxis,c,h,g=0,j=a.processedXData,k=a.processedYData,n=b.data,i=e.getExtremes(),R=i.min,S=i.max,i=f.getExtremes(),T=i.min,U=i.max,w={},q,V=!!a.sampling,x,y=b.marker&&b.marker.radius,z=this.cvsDrawPoint,A=b.lineWidth?this.cvsLineTo:!1,B=y<=1?this.cvsMarkerSquare:this.cvsMarkerCircle,W=b.enableMouseTracking!==!1,C,i=b.threshold,l=f.getThreshold(i),D=typeof i==="number",E=l,X=this.fill,F=a.pointArrayMap&&a.pointArrayMap.join(",")==="low,high",G=!!b.stacking,Y=a.cropStart||0,i=d.options.loading,
|
7
|
+
Z=a.requireSorting,H,$=b.connectNulls,I=!j,r,s,m,o,aa=a.fillOpacity?(new M(a.color)).setOpacity(Q(b.fillOpacity,0.75)).get():a.color,J=function(){X?(c.fillStyle=aa,c.fill()):(c.strokeStyle=a.color,c.lineWidth=b.lineWidth,c.stroke())},K=function(a,b,d){g===0&&c.beginPath();H?c.moveTo(a,b):z?z(c,a,b,d,C):A?A(c,a,b):B&&B(c,a,b,y);g+=1;g===1E3&&(J(),g=0);C={clientX:a,plotY:b,yBottom:d}},t=function(a,b,c){W&&!w[a+","+b]&&(w[a+","+b]=!0,d.inverted&&(a=e.len-a,b=f.len-b),x.push({clientX:a,plotX:a,plotY:b,
|
8
|
+
i:Y+c}))};(this.points||this.graph)&&this.destroyGraphics();a.plotGroup("group","series",a.visible?"visible":"hidden",b.zIndex,d.seriesGroup);a.getAttribs();a.markerGroup=a.group;N(a,"destroy",function(){a.markerGroup=null});x=this.points=[];c=this.getContext();a.buildKDTree=L;if(n.length>99999)d.options.loading=P(i,{labelStyle:{backgroundColor:"rgba(255,255,255,0.75)",padding:"1em",borderRadius:"0.5em"},style:{backgroundColor:"none",opacity:1}}),d.showLoading("Drawing..."),d.options.loading=i,d.loadingShown===
|
9
|
+
!0?d.loadingShown=1:d.loadingShown+=1;h=0;u(G?a.data:j||n,function(b){var d,c,g,i=!0;I?(d=b[0],c=b[1]):(d=b,c=k[h]);if(F)I&&(c=b.slice(1,3)),g=c[0],c=c[1];else if(G)d=b.x,c=b.stackY,g=c-b.y;b=c===null;Z||(i=c>=T&&c<=U);if(!b&&d>=R&&d<=S&&i)if(d=Math.round(e.toPixels(d,!0)),V){if(m===void 0||d===q){F||(g=c);if(o===void 0||c>s)s=c,o=h;if(m===void 0||g<r)r=g,m=h}d!==q&&(m!==void 0&&(c=f.toPixels(s,!0),l=f.toPixels(r,!0),K(d,D?Math.min(c,E):c,D?Math.max(l,E):l),t(d,c,o),l!==c&&t(d,l,m)),m=o=void 0,q=
|
10
|
+
d)}else c=Math.round(f.toPixels(c,!0)),K(d,c,l),t(d,c,h);H=b&&!$;h+=1;h%v===0&&a.canvasToSVG()},function(){var b=d.loadingDiv,c=+d.loadingShown;J();a.canvasToSVG();O(a,"renderedCanvas");if(c===1)p(b.style,{transition:"opacity 250ms",opacity:0}),d.loadingShown=!1,setTimeout(function(){b.parentNode&&b.parentNode.removeChild(b);d.loadingDiv=d.loadingSpan=null},250);if(c)d.loadingShown=c-1;a.directTouch=!1;a.options.stickyTracking=!0;delete a.buildKDTree;a.buildKDTree()},d.renderer.forExport?Number.MAX_VALUE:
|
11
|
+
void 0)}});h.scatter.prototype.cvsMarkerCircle=function(a,b,d,c){a.moveTo(b,d);a.arc(b,d,c,0,2*Math.PI,!1)};h.scatter.prototype.cvsMarkerSquare=function(a,b,d,c){a.moveTo(b,d);a.rect(b-c,d-c,c*2,c*2)};h.scatter.prototype.fill=!0;p(h.area.prototype,{cvsDrawPoint:function(a,b,d,c,f){f&&b!==f.clientX&&(a.moveTo(f.clientX,f.yBottom),a.lineTo(f.clientX,f.plotY),a.lineTo(b,d),a.lineTo(b,c))},fill:!0,fillOpacity:!0,sampling:!0});p(h.column.prototype,{cvsDrawPoint:function(a,b,c,e){a.rect(b-1,c,1,e-c)},fill:!0,
|
12
|
+
sampling:!0});j.prototype.getPoint=function(a){var b=a;if(a&&!(a instanceof this.pointClass))b=(new this.pointClass).init(this,this.options.data[a.i]),b.dist=a.dist,b.category=b.x,b.plotX=a.plotX,b.plotY=a.plotY;return b};g(j.prototype,"searchPoint",function(a){return this.getPoint(a.apply(this,[].slice.call(arguments,1)))})});
|
@@ -0,0 +1,591 @@
|
|
1
|
+
/**
|
2
|
+
* This is an experimental Highcharts module that draws long data series on a canvas
|
3
|
+
* in order to increase performance of the initial load time and tooltip responsiveness.
|
4
|
+
*
|
5
|
+
* Compatible with HTML5 canvas compatible browsers (not IE < 9).
|
6
|
+
*
|
7
|
+
* Author: Torstein Honsi
|
8
|
+
*
|
9
|
+
*
|
10
|
+
* Development plan
|
11
|
+
* - Column range.
|
12
|
+
* - Heatmap.
|
13
|
+
* - Treemap.
|
14
|
+
* - Check how it works with Highstock and data grouping.
|
15
|
+
* - Check inverted charts.
|
16
|
+
* - Check reversed axes.
|
17
|
+
* - Chart callback should be async after last series is drawn. (But not necessarily, we don't do
|
18
|
+
that with initial series animation).
|
19
|
+
* - Cache full-size image so we don't have to redraw on hide/show and zoom up. But k-d-tree still
|
20
|
+
* needs to be built.
|
21
|
+
* - Test IE9 and IE10.
|
22
|
+
* - Stacking is not perhaps not correct since it doesn't use the translation given in
|
23
|
+
* the translate method. If this gets to complicated, a possible way out would be to
|
24
|
+
* have a simplified renderCanvas method that simply draws the areaPath on a canvas.
|
25
|
+
*
|
26
|
+
* If this module is taken in as part of the core
|
27
|
+
* - All the loading logic should be merged with core. Update styles in the core.
|
28
|
+
* - Most of the method wraps should probably be added directly in parent methods.
|
29
|
+
*
|
30
|
+
* Notes for boost mode
|
31
|
+
* - Area lines are not drawn
|
32
|
+
* - Point markers are not drawn
|
33
|
+
* - Zones and negativeColor don't work
|
34
|
+
* - Columns are always one pixel wide. Don't set the threshold too low.
|
35
|
+
*
|
36
|
+
* Optimizing tips for users
|
37
|
+
* - For scatter plots, use a marker.radius of 1 or less. It results in a rectangle being drawn, which is
|
38
|
+
* considerably faster than a circle.
|
39
|
+
* - Set extremes (min, max) explicitly on the axes in order for Highcharts to avoid computing extremes.
|
40
|
+
* - Set enableMouseTracking to false on the series to improve total rendering time.
|
41
|
+
* - The default threshold is set based on one series. If you have multiple, dense series, the combined
|
42
|
+
* number of points drawn gets higher, and you may want to set the threshold lower in order to
|
43
|
+
* use optimizations.
|
44
|
+
*/
|
45
|
+
|
46
|
+
/* eslint indent: [2, 4] */
|
47
|
+
(function (factory) {
|
48
|
+
if (typeof module === 'object' && module.exports) {
|
49
|
+
module.exports = factory;
|
50
|
+
} else {
|
51
|
+
factory(Highcharts);
|
52
|
+
}
|
53
|
+
}(function (H) {
|
54
|
+
|
55
|
+
'use strict';
|
56
|
+
|
57
|
+
var noop = function () {},
|
58
|
+
Color = H.Color,
|
59
|
+
Series = H.Series,
|
60
|
+
seriesTypes = H.seriesTypes,
|
61
|
+
each = H.each,
|
62
|
+
extend = H.extend,
|
63
|
+
addEvent = H.addEvent,
|
64
|
+
fireEvent = H.fireEvent,
|
65
|
+
merge = H.merge,
|
66
|
+
pick = H.pick,
|
67
|
+
wrap = H.wrap,
|
68
|
+
plotOptions = H.getOptions().plotOptions,
|
69
|
+
CHUNK_SIZE = 50000;
|
70
|
+
|
71
|
+
function eachAsync(arr, fn, finalFunc, chunkSize, i) {
|
72
|
+
i = i || 0;
|
73
|
+
chunkSize = chunkSize || CHUNK_SIZE;
|
74
|
+
each(arr.slice(i, i + chunkSize), fn);
|
75
|
+
if (i + chunkSize < arr.length) {
|
76
|
+
setTimeout(function () {
|
77
|
+
eachAsync(arr, fn, finalFunc, chunkSize, i + chunkSize);
|
78
|
+
});
|
79
|
+
} else if (finalFunc) {
|
80
|
+
finalFunc();
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
// Set default options
|
85
|
+
each(['area', 'arearange', 'column', 'line', 'scatter'], function (type) {
|
86
|
+
if (plotOptions[type]) {
|
87
|
+
plotOptions[type].boostThreshold = 5000;
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Override a bunch of methods the same way. If the number of points is below the threshold,
|
93
|
+
* run the original method. If not, check for a canvas version or do nothing.
|
94
|
+
*/
|
95
|
+
each(['translate', 'generatePoints', 'drawTracker', 'drawPoints', 'render'], function (method) {
|
96
|
+
function branch(proceed) {
|
97
|
+
var letItPass = this.options.stacking && (method === 'translate' || method === 'generatePoints');
|
98
|
+
if ((this.processedXData || this.options.data).length < (this.options.boostThreshold || Number.MAX_VALUE) ||
|
99
|
+
letItPass) {
|
100
|
+
|
101
|
+
// Clear image
|
102
|
+
if (method === 'render' && this.image) {
|
103
|
+
this.image.attr({ href: '' });
|
104
|
+
this.animate = null; // We're zooming in, don't run animation
|
105
|
+
}
|
106
|
+
|
107
|
+
proceed.call(this);
|
108
|
+
|
109
|
+
// If a canvas version of the method exists, like renderCanvas(), run
|
110
|
+
} else if (this[method + 'Canvas']) {
|
111
|
+
|
112
|
+
this[method + 'Canvas']();
|
113
|
+
}
|
114
|
+
}
|
115
|
+
wrap(Series.prototype, method, branch);
|
116
|
+
|
117
|
+
// A special case for some types - its translate method is already wrapped
|
118
|
+
if (method === 'translate') {
|
119
|
+
if (seriesTypes.column) {
|
120
|
+
wrap(seriesTypes.column.prototype, method, branch);
|
121
|
+
}
|
122
|
+
if (seriesTypes.arearange) {
|
123
|
+
wrap(seriesTypes.arearange.prototype, method, branch);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
});
|
127
|
+
|
128
|
+
/**
|
129
|
+
* Do not compute extremes when min and max are set.
|
130
|
+
* If we use this in the core, we can add the hook to hasExtremes to the methods directly.
|
131
|
+
*/
|
132
|
+
wrap(Series.prototype, 'getExtremes', function (proceed) {
|
133
|
+
if (!this.hasExtremes()) {
|
134
|
+
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
135
|
+
}
|
136
|
+
});
|
137
|
+
wrap(Series.prototype, 'setData', function (proceed) {
|
138
|
+
if (!this.hasExtremes(true)) {
|
139
|
+
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
140
|
+
}
|
141
|
+
});
|
142
|
+
wrap(Series.prototype, 'processData', function (proceed) {
|
143
|
+
if (!this.hasExtremes(true)) {
|
144
|
+
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
145
|
+
}
|
146
|
+
});
|
147
|
+
|
148
|
+
|
149
|
+
H.extend(Series.prototype, {
|
150
|
+
pointRange: 0,
|
151
|
+
|
152
|
+
hasExtremes: function (checkX) {
|
153
|
+
var options = this.options,
|
154
|
+
data = options.data,
|
155
|
+
xAxis = this.xAxis && this.xAxis.options,
|
156
|
+
yAxis = this.yAxis && this.yAxis.options;
|
157
|
+
return data.length > (options.boostThreshold || Number.MAX_VALUE) && typeof yAxis.min === 'number' && typeof yAxis.max === 'number' &&
|
158
|
+
(!checkX || (typeof xAxis.min === 'number' && typeof xAxis.max === 'number'));
|
159
|
+
},
|
160
|
+
|
161
|
+
/**
|
162
|
+
* If implemented in the core, parts of this can probably be shared with other similar
|
163
|
+
* methods in Highcharts.
|
164
|
+
*/
|
165
|
+
destroyGraphics: function () {
|
166
|
+
var series = this,
|
167
|
+
points = this.points,
|
168
|
+
point,
|
169
|
+
i;
|
170
|
+
|
171
|
+
if (points) {
|
172
|
+
for (i = 0; i < points.length; i = i + 1) {
|
173
|
+
point = points[i];
|
174
|
+
if (point && point.graphic) {
|
175
|
+
point.graphic = point.graphic.destroy();
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
each(['graph', 'area', 'tracker'], function (prop) {
|
181
|
+
if (series[prop]) {
|
182
|
+
series[prop] = series[prop].destroy();
|
183
|
+
}
|
184
|
+
});
|
185
|
+
},
|
186
|
+
|
187
|
+
/**
|
188
|
+
* Create a hidden canvas to draw the graph on. The contents is later copied over
|
189
|
+
* to an SVG image element.
|
190
|
+
*/
|
191
|
+
getContext: function () {
|
192
|
+
var chart = this.chart,
|
193
|
+
width = chart.plotWidth,
|
194
|
+
height = chart.plotHeight,
|
195
|
+
ctx = this.ctx,
|
196
|
+
swapXY = function (proceed, x, y, a, b, c, d) {
|
197
|
+
proceed.call(this, y, x, a, b, c, d);
|
198
|
+
};
|
199
|
+
|
200
|
+
if (!this.canvas) {
|
201
|
+
this.canvas = document.createElement('canvas');
|
202
|
+
this.image = chart.renderer.image('', 0, 0, width, height).add(this.group);
|
203
|
+
this.ctx = ctx = this.canvas.getContext('2d');
|
204
|
+
if (chart.inverted) {
|
205
|
+
each(['moveTo', 'lineTo', 'rect', 'arc'], function (fn) {
|
206
|
+
wrap(ctx, fn, swapXY);
|
207
|
+
});
|
208
|
+
}
|
209
|
+
} else {
|
210
|
+
ctx.clearRect(0, 0, width, height);
|
211
|
+
}
|
212
|
+
|
213
|
+
this.canvas.setAttribute('width', width);
|
214
|
+
this.canvas.setAttribute('height', height);
|
215
|
+
this.image.attr({
|
216
|
+
width: width,
|
217
|
+
height: height
|
218
|
+
});
|
219
|
+
|
220
|
+
return ctx;
|
221
|
+
},
|
222
|
+
|
223
|
+
/**
|
224
|
+
* Draw the canvas image inside an SVG image
|
225
|
+
*/
|
226
|
+
canvasToSVG: function () {
|
227
|
+
this.image.attr({ href: this.canvas.toDataURL('image/png') });
|
228
|
+
},
|
229
|
+
|
230
|
+
cvsLineTo: function (ctx, clientX, plotY) {
|
231
|
+
ctx.lineTo(clientX, plotY);
|
232
|
+
},
|
233
|
+
|
234
|
+
renderCanvas: function () {
|
235
|
+
var series = this,
|
236
|
+
options = series.options,
|
237
|
+
chart = series.chart,
|
238
|
+
xAxis = this.xAxis,
|
239
|
+
yAxis = this.yAxis,
|
240
|
+
ctx,
|
241
|
+
i,
|
242
|
+
c = 0,
|
243
|
+
xData = series.processedXData,
|
244
|
+
yData = series.processedYData,
|
245
|
+
rawData = options.data,
|
246
|
+
xExtremes = xAxis.getExtremes(),
|
247
|
+
xMin = xExtremes.min,
|
248
|
+
xMax = xExtremes.max,
|
249
|
+
yExtremes = yAxis.getExtremes(),
|
250
|
+
yMin = yExtremes.min,
|
251
|
+
yMax = yExtremes.max,
|
252
|
+
pointTaken = {},
|
253
|
+
lastClientX,
|
254
|
+
sampling = !!series.sampling,
|
255
|
+
points,
|
256
|
+
r = options.marker && options.marker.radius,
|
257
|
+
cvsDrawPoint = this.cvsDrawPoint,
|
258
|
+
cvsLineTo = options.lineWidth ? this.cvsLineTo : false,
|
259
|
+
cvsMarker = r <= 1 ? this.cvsMarkerSquare : this.cvsMarkerCircle,
|
260
|
+
enableMouseTracking = options.enableMouseTracking !== false,
|
261
|
+
lastPoint,
|
262
|
+
threshold = options.threshold,
|
263
|
+
yBottom = yAxis.getThreshold(threshold),
|
264
|
+
hasThreshold = typeof threshold === 'number',
|
265
|
+
translatedThreshold = yBottom,
|
266
|
+
doFill = this.fill,
|
267
|
+
isRange = series.pointArrayMap && series.pointArrayMap.join(',') === 'low,high',
|
268
|
+
isStacked = !!options.stacking,
|
269
|
+
cropStart = series.cropStart || 0,
|
270
|
+
loadingOptions = chart.options.loading,
|
271
|
+
requireSorting = series.requireSorting,
|
272
|
+
wasNull,
|
273
|
+
connectNulls = options.connectNulls,
|
274
|
+
useRaw = !xData,
|
275
|
+
minVal,
|
276
|
+
maxVal,
|
277
|
+
minI,
|
278
|
+
maxI,
|
279
|
+
fillColor = series.fillOpacity ?
|
280
|
+
new Color(series.color).setOpacity(pick(options.fillOpacity, 0.75)).get() :
|
281
|
+
series.color,
|
282
|
+
stroke = function () {
|
283
|
+
if (doFill) {
|
284
|
+
ctx.fillStyle = fillColor;
|
285
|
+
ctx.fill();
|
286
|
+
} else {
|
287
|
+
ctx.strokeStyle = series.color;
|
288
|
+
ctx.lineWidth = options.lineWidth;
|
289
|
+
ctx.stroke();
|
290
|
+
}
|
291
|
+
},
|
292
|
+
drawPoint = function (clientX, plotY, yBottom) {
|
293
|
+
if (c === 0) {
|
294
|
+
ctx.beginPath();
|
295
|
+
}
|
296
|
+
|
297
|
+
if (wasNull) {
|
298
|
+
ctx.moveTo(clientX, plotY);
|
299
|
+
} else {
|
300
|
+
if (cvsDrawPoint) {
|
301
|
+
cvsDrawPoint(ctx, clientX, plotY, yBottom, lastPoint);
|
302
|
+
} else if (cvsLineTo) {
|
303
|
+
cvsLineTo(ctx, clientX, plotY);
|
304
|
+
} else if (cvsMarker) {
|
305
|
+
cvsMarker(ctx, clientX, plotY, r);
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
// We need to stroke the line for every 1000 pixels. It will crash the browser
|
310
|
+
// memory use if we stroke too infrequently.
|
311
|
+
c = c + 1;
|
312
|
+
if (c === 1000) {
|
313
|
+
stroke();
|
314
|
+
c = 0;
|
315
|
+
}
|
316
|
+
|
317
|
+
// Area charts need to keep track of the last point
|
318
|
+
lastPoint = {
|
319
|
+
clientX: clientX,
|
320
|
+
plotY: plotY,
|
321
|
+
yBottom: yBottom
|
322
|
+
};
|
323
|
+
},
|
324
|
+
|
325
|
+
addKDPoint = function (clientX, plotY, i) {
|
326
|
+
|
327
|
+
// The k-d tree requires series points. Reduce the amount of points, since the time to build the
|
328
|
+
// tree increases exponentially.
|
329
|
+
if (enableMouseTracking && !pointTaken[clientX + ',' + plotY]) {
|
330
|
+
pointTaken[clientX + ',' + plotY] = true;
|
331
|
+
|
332
|
+
if (chart.inverted) {
|
333
|
+
clientX = xAxis.len - clientX;
|
334
|
+
plotY = yAxis.len - plotY;
|
335
|
+
}
|
336
|
+
|
337
|
+
points.push({
|
338
|
+
clientX: clientX,
|
339
|
+
plotX: clientX,
|
340
|
+
plotY: plotY,
|
341
|
+
i: cropStart + i
|
342
|
+
});
|
343
|
+
}
|
344
|
+
};
|
345
|
+
|
346
|
+
// If we are zooming out from SVG mode, destroy the graphics
|
347
|
+
if (this.points || this.graph) {
|
348
|
+
this.destroyGraphics();
|
349
|
+
}
|
350
|
+
|
351
|
+
// The group
|
352
|
+
series.plotGroup(
|
353
|
+
'group',
|
354
|
+
'series',
|
355
|
+
series.visible ? 'visible' : 'hidden',
|
356
|
+
options.zIndex,
|
357
|
+
chart.seriesGroup
|
358
|
+
);
|
359
|
+
|
360
|
+
series.getAttribs();
|
361
|
+
series.markerGroup = series.group;
|
362
|
+
addEvent(series, 'destroy', function () {
|
363
|
+
series.markerGroup = null;
|
364
|
+
});
|
365
|
+
|
366
|
+
points = this.points = [];
|
367
|
+
ctx = this.getContext();
|
368
|
+
series.buildKDTree = noop; // Do not start building while drawing
|
369
|
+
|
370
|
+
// Display a loading indicator
|
371
|
+
if (rawData.length > 99999) {
|
372
|
+
chart.options.loading = merge(loadingOptions, {
|
373
|
+
labelStyle: {
|
374
|
+
backgroundColor: 'rgba(255,255,255,0.75)',
|
375
|
+
padding: '1em',
|
376
|
+
borderRadius: '0.5em'
|
377
|
+
},
|
378
|
+
style: {
|
379
|
+
backgroundColor: 'none',
|
380
|
+
opacity: 1
|
381
|
+
}
|
382
|
+
});
|
383
|
+
chart.showLoading('Drawing...');
|
384
|
+
chart.options.loading = loadingOptions; // reset
|
385
|
+
if (chart.loadingShown === true) {
|
386
|
+
chart.loadingShown = 1;
|
387
|
+
} else {
|
388
|
+
chart.loadingShown = chart.loadingShown + 1;
|
389
|
+
}
|
390
|
+
}
|
391
|
+
|
392
|
+
// Loop over the points
|
393
|
+
i = 0;
|
394
|
+
eachAsync(isStacked ? series.data : (xData || rawData), function (d) {
|
395
|
+
|
396
|
+
var x,
|
397
|
+
y,
|
398
|
+
clientX,
|
399
|
+
plotY,
|
400
|
+
isNull,
|
401
|
+
low,
|
402
|
+
isYInside = true;
|
403
|
+
|
404
|
+
if (useRaw) {
|
405
|
+
x = d[0];
|
406
|
+
y = d[1];
|
407
|
+
} else {
|
408
|
+
x = d;
|
409
|
+
y = yData[i];
|
410
|
+
}
|
411
|
+
|
412
|
+
// Resolve low and high for range series
|
413
|
+
if (isRange) {
|
414
|
+
if (useRaw) {
|
415
|
+
y = d.slice(1, 3);
|
416
|
+
}
|
417
|
+
low = y[0];
|
418
|
+
y = y[1];
|
419
|
+
} else if (isStacked) {
|
420
|
+
x = d.x;
|
421
|
+
y = d.stackY;
|
422
|
+
low = y - d.y;
|
423
|
+
}
|
424
|
+
|
425
|
+
isNull = y === null;
|
426
|
+
|
427
|
+
// Optimize for scatter zooming
|
428
|
+
if (!requireSorting) {
|
429
|
+
isYInside = y >= yMin && y <= yMax;
|
430
|
+
}
|
431
|
+
|
432
|
+
if (!isNull && x >= xMin && x <= xMax && isYInside) {
|
433
|
+
|
434
|
+
clientX = Math.round(xAxis.toPixels(x, true));
|
435
|
+
|
436
|
+
if (sampling) {
|
437
|
+
if (minI === undefined || clientX === lastClientX) {
|
438
|
+
if (!isRange) {
|
439
|
+
low = y;
|
440
|
+
}
|
441
|
+
if (maxI === undefined || y > maxVal) {
|
442
|
+
maxVal = y;
|
443
|
+
maxI = i;
|
444
|
+
}
|
445
|
+
if (minI === undefined || low < minVal) {
|
446
|
+
minVal = low;
|
447
|
+
minI = i;
|
448
|
+
}
|
449
|
+
|
450
|
+
}
|
451
|
+
if (clientX !== lastClientX) { // Add points and reset
|
452
|
+
if (minI !== undefined) { // then maxI is also a number
|
453
|
+
plotY = yAxis.toPixels(maxVal, true);
|
454
|
+
yBottom = yAxis.toPixels(minVal, true);
|
455
|
+
drawPoint(
|
456
|
+
clientX,
|
457
|
+
hasThreshold ? Math.min(plotY, translatedThreshold) : plotY,
|
458
|
+
hasThreshold ? Math.max(yBottom, translatedThreshold) : yBottom
|
459
|
+
);
|
460
|
+
addKDPoint(clientX, plotY, maxI);
|
461
|
+
if (yBottom !== plotY) {
|
462
|
+
addKDPoint(clientX, yBottom, minI);
|
463
|
+
}
|
464
|
+
}
|
465
|
+
|
466
|
+
|
467
|
+
minI = maxI = undefined;
|
468
|
+
lastClientX = clientX;
|
469
|
+
}
|
470
|
+
} else {
|
471
|
+
plotY = Math.round(yAxis.toPixels(y, true));
|
472
|
+
drawPoint(clientX, plotY, yBottom);
|
473
|
+
addKDPoint(clientX, plotY, i);
|
474
|
+
}
|
475
|
+
}
|
476
|
+
wasNull = isNull && !connectNulls;
|
477
|
+
|
478
|
+
i = i + 1;
|
479
|
+
|
480
|
+
if (i % CHUNK_SIZE === 0) {
|
481
|
+
series.canvasToSVG();
|
482
|
+
}
|
483
|
+
|
484
|
+
}, function () {
|
485
|
+
|
486
|
+
var loadingDiv = chart.loadingDiv,
|
487
|
+
loadingShown = +chart.loadingShown;
|
488
|
+
|
489
|
+
stroke();
|
490
|
+
series.canvasToSVG();
|
491
|
+
|
492
|
+
fireEvent(series, 'renderedCanvas');
|
493
|
+
|
494
|
+
// Do not use chart.hideLoading, as it runs JS animation and will be blocked by buildKDTree.
|
495
|
+
// CSS animation looks good, but then it must be deleted in timeout. If we add the module to core,
|
496
|
+
// change hideLoading so we can skip this block.
|
497
|
+
if (loadingShown === 1) {
|
498
|
+
extend(loadingDiv.style, {
|
499
|
+
transition: 'opacity 250ms',
|
500
|
+
opacity: 0
|
501
|
+
});
|
502
|
+
|
503
|
+
chart.loadingShown = false;
|
504
|
+
setTimeout(function () {
|
505
|
+
if (loadingDiv.parentNode) { // In exporting it is falsy
|
506
|
+
loadingDiv.parentNode.removeChild(loadingDiv);
|
507
|
+
}
|
508
|
+
chart.loadingDiv = chart.loadingSpan = null;
|
509
|
+
}, 250);
|
510
|
+
}
|
511
|
+
if (loadingShown) {
|
512
|
+
chart.loadingShown = loadingShown - 1;
|
513
|
+
}
|
514
|
+
|
515
|
+
// Pass tests in Pointer.
|
516
|
+
// Replace this with a single property, and replace when zooming in
|
517
|
+
// below boostThreshold.
|
518
|
+
series.directTouch = false;
|
519
|
+
series.options.stickyTracking = true;
|
520
|
+
|
521
|
+
delete series.buildKDTree; // Go back to prototype, ready to build
|
522
|
+
series.buildKDTree();
|
523
|
+
|
524
|
+
// Don't do async on export, the exportChart, getSVGForExport and getSVG methods are not chained for it.
|
525
|
+
}, chart.renderer.forExport ? Number.MAX_VALUE : undefined);
|
526
|
+
}
|
527
|
+
});
|
528
|
+
|
529
|
+
seriesTypes.scatter.prototype.cvsMarkerCircle = function (ctx, clientX, plotY, r) {
|
530
|
+
ctx.moveTo(clientX, plotY);
|
531
|
+
ctx.arc(clientX, plotY, r, 0, 2 * Math.PI, false);
|
532
|
+
};
|
533
|
+
|
534
|
+
// Rect is twice as fast as arc, should be used for small markers
|
535
|
+
seriesTypes.scatter.prototype.cvsMarkerSquare = function (ctx, clientX, plotY, r) {
|
536
|
+
ctx.moveTo(clientX, plotY);
|
537
|
+
ctx.rect(clientX - r, plotY - r, r * 2, r * 2);
|
538
|
+
};
|
539
|
+
seriesTypes.scatter.prototype.fill = true;
|
540
|
+
|
541
|
+
extend(seriesTypes.area.prototype, {
|
542
|
+
cvsDrawPoint: function (ctx, clientX, plotY, yBottom, lastPoint) {
|
543
|
+
if (lastPoint && clientX !== lastPoint.clientX) {
|
544
|
+
ctx.moveTo(lastPoint.clientX, lastPoint.yBottom);
|
545
|
+
ctx.lineTo(lastPoint.clientX, lastPoint.plotY);
|
546
|
+
ctx.lineTo(clientX, plotY);
|
547
|
+
ctx.lineTo(clientX, yBottom);
|
548
|
+
}
|
549
|
+
},
|
550
|
+
fill: true,
|
551
|
+
fillOpacity: true,
|
552
|
+
sampling: true
|
553
|
+
});
|
554
|
+
|
555
|
+
extend(seriesTypes.column.prototype, {
|
556
|
+
cvsDrawPoint: function (ctx, clientX, plotY, yBottom) {
|
557
|
+
ctx.rect(clientX - 1, plotY, 1, yBottom - plotY);
|
558
|
+
},
|
559
|
+
fill: true,
|
560
|
+
sampling: true
|
561
|
+
});
|
562
|
+
|
563
|
+
/**
|
564
|
+
* Return a full Point object based on the index. The boost module uses stripped point objects
|
565
|
+
* for performance reasons.
|
566
|
+
* @param {Number} boostPoint A stripped-down point object
|
567
|
+
* @returns {Object} A Point object as per http://api.highcharts.com/highcharts#Point
|
568
|
+
*/
|
569
|
+
Series.prototype.getPoint = function (boostPoint) {
|
570
|
+
var point = boostPoint;
|
571
|
+
|
572
|
+
if (boostPoint && !(boostPoint instanceof this.pointClass)) {
|
573
|
+
point = (new this.pointClass()).init(this, this.options.data[boostPoint.i]);
|
574
|
+
point.dist = boostPoint.dist;
|
575
|
+
point.category = point.x;
|
576
|
+
point.plotX = boostPoint.plotX;
|
577
|
+
point.plotY = boostPoint.plotY;
|
578
|
+
}
|
579
|
+
|
580
|
+
return point;
|
581
|
+
};
|
582
|
+
|
583
|
+
/**
|
584
|
+
* Return a point instance from the k-d-tree
|
585
|
+
*/
|
586
|
+
wrap(Series.prototype, 'searchPoint', function (proceed) {
|
587
|
+
return this.getPoint(
|
588
|
+
proceed.apply(this, [].slice.call(arguments, 1))
|
589
|
+
);
|
590
|
+
});
|
591
|
+
}));
|