]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/sparkline/Line.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / sparkline / Line.js
CommitLineData
6527f429
DM
1/**\r
2 * @class Ext.sparkline.Line\r
3 *\r
4 * Plots a line graph based upon the input {@link #values} array.\r
5 *\r
6 * See {@link Ext.sparkline.Base the base class} for a simple example.\r
7 */\r
8Ext.define('Ext.sparkline.Line', {\r
9 extend: 'Ext.sparkline.Base',\r
10 requires: [\r
11 'Ext.sparkline.RangeMap'\r
12 ],\r
13\r
14 alias: 'widget.sparklineline',\r
15\r
16 config: {\r
17\r
18 /**\r
19 * @cfg {String} [spotColor=#f80] The colour of the final value marker. Set to false or an empty string to hide it.\r
20 */\r
21 spotColor: '#f80',\r
22 \r
23 /**\r
24 * @cfg {String} [highlightSpotColor=#5f5] The colour of value marker spots when mouseovered.\r
25 */\r
26 highlightSpotColor: '#5f5',\r
27 \r
28 /**\r
29 * @cfg {String} [highlightLineColor=#f22] The colour of value line shown when the graph is mouseovered.\r
30 */\r
31 highlightLineColor: '#f22',\r
32 \r
33 /**\r
34 * @cfg {Number} [spotRadius=1.5] The pixel radius of min, max and final value dots.\r
35 */\r
36 spotRadius: 1.5,\r
37 \r
38 /**\r
39 * @cfg {String} [minSpotColor=#f80] The colour of the mimimum value marker. Set to false or an empty string to hide it.\r
40 */\r
41 minSpotColor: '#f80',\r
42 \r
43 /**\r
44 * @cfg {String} [maxSpotColor=#f80] The colour of the maximum value marker. Set to false or an empty string to hide it.\r
45 */\r
46 maxSpotColor: '#f80',\r
47 \r
48 /**\r
49 * @cfg {Number} [lineWidth=1] The pixel width of the line plotted.\r
50 */\r
51 lineWidth: 1,\r
52 \r
53 /**\r
54 * @cfg {Number} [normalRangeMin] See {@link #normalRangeMax} The minimum value to overlay a "normal range bar" over the graph using the {@link #normalRangeColor}.\r
55 */\r
56 normalRangeMin: null,\r
57 \r
58 /**\r
59 * @cfg {Number} [normalRangeMax] See {@link #normalRangeMin} The maximum value to overlay a "normal range bar" over the graph using the {@link #normalRangeColor}.\r
60 */\r
61 normalRangeMax: null,\r
62 \r
63 /**\r
64 * @cfg {String} [normalRangeColor=#ccc] See {@link #normalRangeMin} and {@link #normalRangeMax} The color of the undererlayed "normal range bar".\r
65 */\r
66 normalRangeColor: '#ccc',\r
67 \r
68 /**\r
69 * @cfg {Boolean} [drawNormalOnTop=false] Configure as `true` to draw the normal range overlaying the chart.\r
70 */\r
71 drawNormalOnTop: false,\r
72 \r
73 /**\r
74 * @cfg {Number} [chartRangeMin] The minimum value to use for the range of Y values of the chart - Defaults to the minimum value supplied.\r
75 */\r
76 chartRangeMin: null,\r
77 \r
78 /**\r
79 * @cfg {Number} [chartRangeMax] The maximum value to use for the range of Y values of the chart - Defaults to the minimum value supplied.\r
80 */\r
81 chartRangeMax: null,\r
82 \r
83 /**\r
84 * @cfg {Number} [chartRangeMinX] The minimum value to use for the X value of the chart.\r
85 */\r
86 chartRangeMinX: null,\r
87 \r
88 /**\r
89 * @cfg {Number} [chartRangeMaxX] The maximum value to use for the X value of the chart.\r
90 */\r
91 chartRangeMaxX: null,\r
92 \r
93 tipTpl: new Ext.XTemplate('● {prefix}{y}{suffix}'),\r
94 \r
95 /**\r
96 * @cfg {Object} [valueSpots] An object which uses range specifiers as keys to indicate spot color values\r
97 * for range of values. A range specifier is of the form `[number]:[number]` indicating start and end range.\r
98 * Omitting aither means an open ended range. For example to render green spots on all values less than 50\r
99 * and red on values higher than 50 use:\r
100 *\r
101 * {\r
102 * // Open ended range, with max value 49\r
103 * ":49": "green",\r
104 *\r
105 * // Open ended range, with min value 50\r
106 * "50:": "red"\r
107 * }\r
108 */\r
109 valueSpots: null\r
110 },\r
111\r
112 applyValueSpots: function(valueSpots) {\r
113 if (valueSpots && !valueSpots.get) {\r
114 valueSpots = new Ext.sparkline.RangeMap(valueSpots);\r
115 }\r
116 return valueSpots;\r
117 },\r
118\r
119 onUpdate: function () {\r
120 this.vertices = [];\r
121 this.regionMap = [];\r
122 this.xvalues = [];\r
123 this.yvalues = [];\r
124 this.yminmax = [];\r
125 },\r
126\r
127 getRegion: function(x, y) {\r
128 var i,\r
129 regionMap = this.regionMap; // maps regions to value positions\r
130\r
131 for (i = regionMap.length; i--;) {\r
132 if (regionMap[i] !== null && x >= regionMap[i][0] && x <= regionMap[i][1]) {\r
133 return regionMap[i][2];\r
134 }\r
135 }\r
136 return undefined;\r
137 },\r
138\r
139 getRegionFields: function(region) {\r
140 return {\r
141 isNull: this.yvalues[region] === null,\r
142 x: this.xvalues[region],\r
143 y: this.yvalues[region],\r
144 color: this.getLineColor(),\r
145 fillColor: this.getFillColor(),\r
146 offset: region\r
147 };\r
148 },\r
149\r
150 renderHighlight: function(region) {\r
151 var me = this,\r
152 canvas = me.canvas,\r
153 vertex = me.vertices[region],\r
154 spotRadius = me.getSpotRadius(),\r
155 highlightSpotColor = me.getHighlightSpotColor(),\r
156 highlightLineColor = me.getHighlightLineColor();\r
157\r
158 if (!vertex) {\r
159 return;\r
160 }\r
161 if (spotRadius && highlightSpotColor) {\r
162 canvas.drawCircle(vertex[0], vertex[1], spotRadius, null, highlightSpotColor).append();\r
163 }\r
164 if (highlightLineColor) {\r
165 canvas.drawLine(vertex[0], me.canvasTop, vertex[0], me.canvasTop + me.getHeight(), highlightLineColor).append();\r
166 }\r
167 },\r
168\r
169 scanValues: function () {\r
170 var me = this,\r
171 values = me.values,\r
172 valcount = values.length,\r
173 xvalues = me.xvalues,\r
174 yvalues = me.yvalues,\r
175 yminmax = me.yminmax,\r
176 i, val, isStr, isArray, sp;\r
177\r
178 for (i = 0; i < valcount; i++) {\r
179 val = values[i];\r
180 isStr = typeof(values[i]) === 'string';\r
181 isArray = typeof(values[i]) === 'object' && values[i] instanceof Array;\r
182 sp = isStr && values[i].split(':');\r
183\r
184 if (isStr && sp.length === 2) { // x:y\r
185 xvalues.push(Number(sp[0]));\r
186 yvalues.push(Number(sp[1]));\r
187 yminmax.push(Number(sp[1]));\r
188 } else if (isArray) {\r
189 xvalues.push(val[0]);\r
190 yvalues.push(val[1]);\r
191 yminmax.push(val[1]);\r
192 } else {\r
193 xvalues.push(i);\r
194 if (values[i] === null || values[i] === 'null') {\r
195 yvalues.push(null);\r
196 } else {\r
197 yvalues.push(Number(val));\r
198 yminmax.push(Number(val));\r
199 }\r
200 }\r
201 }\r
202 if (me.xvalues) {\r
203 xvalues = me.xvalues;\r
204 }\r
205\r
206 me.maxy = me.maxyorg = Math.max.apply(Math, yminmax);\r
207 me.miny = me.minyorg = Math.min.apply(Math, yminmax);\r
208\r
209 me.maxx = Math.max.apply(Math, xvalues);\r
210 me.minx = Math.min.apply(Math, xvalues);\r
211\r
212 me.xvalues = xvalues;\r
213 me.yvalues = yvalues;\r
214 me.yminmax = yminmax;\r
215 },\r
216\r
217 processRangeOptions: function () {\r
218 var me = this,\r
219 normalRangeMin = me.getNormalRangeMin(),\r
220 normalRangeMax = me.getNormalRangeMax(),\r
221 chartRangeMin = me.getChartRangeMin(),\r
222 chartRangeMinX = me.getChartRangeMinX(),\r
223 chartRangeMax = me.getChartRangeMax(),\r
224 chartRangeMaxX = me.getChartRangeMaxX();\r
225\r
226 if (normalRangeMin != null) {\r
227 if (normalRangeMin < me.miny) {\r
228 me.miny = normalRangeMin;\r
229 }\r
230 if (normalRangeMax > me.maxy) {\r
231 me.maxy = normalRangeMax;\r
232 }\r
233 }\r
234 if (chartRangeMin != null && (me.chartRangeClip || chartRangeMin < me.miny)) {\r
235 me.miny = chartRangeMin;\r
236 }\r
237 if (chartRangeMax != null && (me.chartRangeClip || chartRangeMax > me.maxy)) {\r
238 this.maxy = chartRangeMax;\r
239 }\r
240 if (chartRangeMinX != null && (me.chartRangeClipX || chartRangeMinX < me.minx)) {\r
241 me.minx = chartRangeMinX;\r
242 }\r
243 if (chartRangeMaxX != null && (me.chartRangeClipX || chartRangeMaxX > me.maxx)) {\r
244 me.maxx = chartRangeMaxX;\r
245 }\r
246\r
247 },\r
248\r
249 drawNormalRange: function (canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey) {\r
250 var normalRangeMin = this.getNormalRangeMin(),\r
251 normalRangeMax = this.getNormalRangeMax(),\r
252 ytop = canvasTop + Math.round(canvasHeight - (canvasHeight * ((normalRangeMax - this.miny) / rangey))),\r
253 height = Math.round((canvasHeight * (normalRangeMax - normalRangeMin)) / rangey);\r
254 this.canvas.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.normalRangeColor).append();\r
255 },\r
256\r
257 renderGraph: function () {\r
258 var me = this,\r
259 canvas = me.canvas,\r
260 canvasWidth = me.getWidth(),\r
261 canvasHeight = me.getHeight(),\r
262 vertices = me.vertices,\r
263 spotRadius = me.getSpotRadius(),\r
264 regionMap = me.regionMap,\r
265 rangeX, Y, yvallast,\r
266 canvasTop, canvasLeft,\r
267 vertex, path, paths, x, y, xNext, xPos, xPosNext,\r
268 last, next, yValCount, lineShapes, fillShapes, plen,\r
269 valueSpots = me.getValueSpots(), hlSpotsEnabled, color, xValues, yValues, i,\r
270 spotColor = me.getSpotColor(),\r
271 minSpotColor = me.getMinSpotColor(),\r
272 maxSpotColor = me.getMaxSpotColor(),\r
273 normalRangeMin = me.getNormalRangeMin(),\r
274 drawNormalOnTop = me.getDrawNormalOnTop();\r
275\r
276 if (!me.callParent()) {\r
277 return;\r
278 }\r
279\r
280 me.scanValues();\r
281 me.processRangeOptions();\r
282\r
283 xValues = me.xvalues;\r
284 yValues = me.yvalues;\r
285\r
286 if (!me.yminmax.length || me.yvalues.length < 2) {\r
287 // empty or all null valuess\r
288 return;\r
289 }\r
290\r
291 canvasTop = canvasLeft = 0;\r
292\r
293 rangeX = me.maxx - me.minx === 0 ? 1 : me.maxx - me.minx;\r
294 Y = me.maxy - me.miny === 0 ? 1 : me.maxy - me.miny;\r
295 yvallast = me.yvalues.length - 1;\r
296\r
297 if (spotRadius && (canvasWidth < (spotRadius * 4) || canvasHeight < (spotRadius * 4))) {\r
298 spotRadius = 0;\r
299 }\r
300 if (spotRadius) {\r
301 // adjust the canvas size as required so that spots will fit\r
302 hlSpotsEnabled = me.getHighlightSpotColor() && !me.disableInteraction;\r
303 if (hlSpotsEnabled || minSpotColor || (spotColor && yValues[yvallast] === me.miny)) {\r
304 canvasHeight -= Math.ceil(spotRadius);\r
305 }\r
306 if (hlSpotsEnabled || maxSpotColor || (spotColor && yValues[yvallast] === me.maxy)) {\r
307 canvasHeight -= Math.ceil(spotRadius);\r
308 canvasTop += Math.ceil(spotRadius);\r
309 }\r
310 if (hlSpotsEnabled ||\r
311 ((minSpotColor || maxSpotColor) && (yValues[0] === me.miny || yValues[0] === me.maxy))) {\r
312 canvasLeft += Math.ceil(spotRadius);\r
313 canvasWidth -= Math.ceil(spotRadius);\r
314 }\r
315 if (hlSpotsEnabled || spotColor ||\r
316 (minSpotColor || maxSpotColor &&\r
317 (yValues[yvallast] === me.miny || yValues[yvallast] === me.maxy))) {\r
318 canvasWidth -= Math.ceil(spotRadius);\r
319 }\r
320 }\r
321\r
322 canvasHeight--;\r
323\r
324 if (normalRangeMin != null && !drawNormalOnTop) {\r
325 me.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, Y);\r
326 }\r
327\r
328 path = [];\r
329 paths = [path];\r
330 last = next = null;\r
331 yValCount = yValues.length;\r
332 for (i = 0; i < yValCount; i++) {\r
333 x = xValues[i];\r
334 xNext = xValues[i + 1];\r
335 y = yValues[i];\r
336 xPos = canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX));\r
337 xPosNext = i < yValCount - 1 ? canvasLeft + Math.round((xNext - me.minx) * (canvasWidth / rangeX)) : canvasWidth;\r
338 next = xPos + ((xPosNext - xPos) / 2);\r
339 regionMap[i] = [last || 0, next, i];\r
340 last = next;\r
341 if (y === null) {\r
342 if (i) {\r
343 if (yValues[i - 1] !== null) {\r
344 path = [];\r
345 paths.push(path);\r
346 }\r
347 vertices.push(null);\r
348 }\r
349 } else {\r
350 if (y < me.miny) {\r
351 y = me.miny;\r
352 }\r
353 if (y > me.maxy) {\r
354 y = me.maxy;\r
355 }\r
356 if (!path.length) {\r
357 // previous value was null\r
358 path.push([xPos, canvasTop + canvasHeight]);\r
359 }\r
360 vertex = [xPos, canvasTop + Math.round(canvasHeight - (canvasHeight * ((y - this.miny) / Y)))];\r
361 path.push(vertex);\r
362 vertices.push(vertex);\r
363 }\r
364 }\r
365\r
366 lineShapes = [];\r
367 fillShapes = [];\r
368 plen = paths.length;\r
369 for (i = 0; i < plen; i++) {\r
370 path = paths[i];\r
371 if (path.length) {\r
372 if (me.fillColor) {\r
373 path.push([path[path.length - 1][0], (canvasTop + canvasHeight)]);\r
374 fillShapes.push(path.slice(0));\r
375 path.pop();\r
376 }\r
377 // if there's only a single point in this path, then we want to display it\r
378 // as a vertical line which means we keep path[0] as is\r
379 if (path.length > 2) {\r
380 // else we want the first value\r
381 path[0] = [path[0][0], path[1][1]];\r
382 }\r
383 lineShapes.push(path);\r
384 }\r
385 }\r
386\r
387 // draw the fill first, then optionally the normal range, then the line on top of that\r
388 plen = fillShapes.length;\r
389 for (i = 0; i < plen; i++) {\r
390 canvas.drawShape(fillShapes[i],\r
391 me.fillColor, me.fillColor).append();\r
392 }\r
393\r
394 if (normalRangeMin != null && drawNormalOnTop) {\r
395 me.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, Y);\r
396 }\r
397\r
398 plen = lineShapes.length;\r
399 for (i = 0; i < plen; i++) {\r
400 canvas.drawShape(lineShapes[i], me.getLineColor(), null, me.getLineWidth()).append();\r
401 }\r
402\r
403 if (spotRadius && valueSpots) {\r
404 if (valueSpots.get == null) {\r
405 valueSpots = new Ext.sparkline.RangeMap(valueSpots);\r
406 }\r
407 for (i = 0; i < yValCount; i++) {\r
408 color = valueSpots.get(yValues[i]);\r
409 if (color) {\r
410 canvas.drawCircle(canvasLeft + Math.round((xValues[i] - me.minx) * (canvasWidth / rangeX)),\r
411 canvasTop + Math.round(canvasHeight - (canvasHeight * ((yValues[i] - me.miny) / Y))),\r
412 spotRadius, null,\r
413 color).append();\r
414 }\r
415 }\r
416\r
417 }\r
418 if (spotRadius && spotColor && yValues[yvallast] != null) {\r
419 canvas.drawCircle(canvasLeft + Math.round((xValues[xValues.length - 1] - me.minx) * (canvasWidth / rangeX)),\r
420 canvasTop + Math.round(canvasHeight - (canvasHeight * ((yValues[yvallast] - me.miny) / Y))),\r
421 spotRadius, null,\r
422 spotColor).append();\r
423 }\r
424 if (me.maxy !== me.minyorg) {\r
425 if (spotRadius && minSpotColor) {\r
426 x = xValues[Ext.Array.indexOf(yValues, me.minyorg)];\r
427 canvas.drawCircle(canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX)),\r
428 canvasTop + Math.round(canvasHeight - (canvasHeight * ((me.minyorg - me.miny) / Y))),\r
429 spotRadius, null,\r
430 minSpotColor).append();\r
431 }\r
432 if (spotRadius && maxSpotColor) {\r
433 x = xValues[Ext.Array.indexOf(yValues, me.maxyorg)];\r
434 canvas.drawCircle(canvasLeft + Math.round((x - me.minx) * (canvasWidth / rangeX)),\r
435 canvasTop + Math.round(canvasHeight - (canvasHeight * ((me.maxyorg - me.miny) / Y))),\r
436 spotRadius, null,\r
437 maxSpotColor).append();\r
438 }\r
439 }\r
440\r
441 me.canvasTop = canvasTop;\r
442\r
443 // If mouse is over, apply the highlight\r
444 if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) {\r
445 me.updateDisplay();\r
446 }\r
447 canvas.render();\r
448 }\r
449});