Mercurial > hg > digilib
comparison client/digitallibrary/jquery/svg/jquery.svggraph.js @ 749:4b492b7900fb jquery
added jQuery ui and svg javascripts
author | hertzhaft |
---|---|
date | Sun, 06 Feb 2011 22:17:41 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
748:fb4ffac2950d | 749:4b492b7900fb |
---|---|
1 /* http://keith-wood.name/svg.html | |
2 SVG graphing extension for jQuery v1.4.3. | |
3 Written by Keith Wood (kbwood{at}iinet.com.au) August 2007. | |
4 Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and | |
5 MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. | |
6 Please attribute the author if you use it. */ | |
7 | |
8 (function($) { // Hide scope, no $ conflict | |
9 | |
10 $.svg.addExtension('graph', SVGGraph); | |
11 | |
12 // Singleton primary SVG graphing interface | |
13 $.svg.graphing = new SVGGraphing(); | |
14 | |
15 function SVGGraphing() { | |
16 this.regional = []; | |
17 this.regional[''] = {percentageText: 'Percentage'}; | |
18 this.region = this.regional['']; | |
19 } | |
20 | |
21 $.extend(SVGGraphing.prototype, { | |
22 _chartTypes: [], | |
23 | |
24 /* Add a new chart rendering type to the package. | |
25 The rendering object must implement the following functions: | |
26 getTitle(), getDescription(), getOptions(), drawChart(graph). | |
27 @param id (string) the ID of this graph renderer | |
28 @param chartType (object) the object implementing this chart type */ | |
29 addChartType: function(id, chartType) { | |
30 this._chartTypes[id] = chartType; | |
31 }, | |
32 | |
33 /* Retrieve the list of chart types. | |
34 @return (object[string]) the array of chart types indexed by ID */ | |
35 chartTypes: function() { | |
36 return this._chartTypes; | |
37 } | |
38 }); | |
39 | |
40 /* Extension point for SVG graphing. | |
41 Access through svg.graph. */ | |
42 function SVGGraph(wrapper) { | |
43 this._wrapper = wrapper; // The attached SVG wrapper object | |
44 this._drawNow = false; // True for immediate update, false to wait for redraw call | |
45 for (var id in $.svg.graphing._chartTypes) { | |
46 this._chartType = $.svg.graphing._chartTypes[id]; // Use first graph renderer | |
47 break; | |
48 } | |
49 this._chartOptions = {}; // Extra options for the graph type | |
50 // The graph title and settings | |
51 this._title = {value: '', offset: 25, settings: {textAnchor: 'middle'}}; | |
52 this._area = [0.1, 0.1, 0.8, 0.9]; // The chart area: left, top, right, bottom, | |
53 // > 1 in pixels, <= 1 as proportion | |
54 this._chartFormat = {fill: 'none', stroke: 'black'}; // The formatting for the chart area | |
55 this._gridlines = []; // The formatting of the x- and y-gridlines | |
56 this._series = []; // The series to be plotted, each is an object | |
57 this._onstatus = null; // The callback function for status updates | |
58 this._chartCont = this._wrapper.svg(0, 0, 0, 0, {class_: 'svg-graph'}); // The main container for the graph | |
59 | |
60 this.xAxis = new SVGGraphAxis(this); // The main x-axis | |
61 this.xAxis.title('', 40); | |
62 this.yAxis = new SVGGraphAxis(this); // The main y-axis | |
63 this.yAxis.title('', 40); | |
64 this.x2Axis = null; // The secondary x-axis | |
65 this.y2Axis = null; // The secondary y-axis | |
66 this.legend = new SVGGraphLegend(this); // The chart legend | |
67 this._drawNow = true; | |
68 } | |
69 | |
70 $.extend(SVGGraph.prototype, { | |
71 | |
72 /* Useful indexes. */ | |
73 X: 0, | |
74 Y: 1, | |
75 W: 2, | |
76 H: 3, | |
77 L: 0, | |
78 T: 1, | |
79 R: 2, | |
80 B: 3, | |
81 | |
82 /* Standard percentage axis. */ | |
83 _percentageAxis: new SVGGraphAxis(this, $.svg.graphing.region.percentageText, 0, 100, 10, 0), | |
84 | |
85 /* Set or retrieve the container for the graph. | |
86 @param cont (SVG element) the container for the graph | |
87 @return (SVGGraph) this graph object or | |
88 (SVG element) the current container (if no parameters) */ | |
89 container: function(cont) { | |
90 if (arguments.length == 0) { | |
91 return this._chartCont; | |
92 } | |
93 this._chartCont = cont; | |
94 return this; | |
95 }, | |
96 | |
97 /* Set or retrieve the type of chart to be rendered. | |
98 See $.svg.graphing.getChartTypes() for the list of available types. | |
99 @param id (string) the ID of the chart type | |
100 @param options (object) additional settings for this chart type (optional) | |
101 @return (SVGGraph) this graph object or | |
102 (string) the chart type (if no parameters) | |
103 @deprecated use type() */ | |
104 chartType: function(id, options) { | |
105 return (arguments.length == 0 ? this.type() : this.type(id, options)); | |
106 }, | |
107 | |
108 /* Set or retrieve the type of chart to be rendered. | |
109 See $.svg.graphing.getChartTypes() for the list of available types. | |
110 @param id (string) the ID of the chart type | |
111 @param options (object) additional settings for this chart type (optional) | |
112 @return (SVGGraph) this graph object or | |
113 (string) the chart type (if no parameters) */ | |
114 type: function(id, options) { | |
115 if (arguments.length == 0) { | |
116 return this._chartType; | |
117 } | |
118 var chartType = $.svg.graphing._chartTypes[id]; | |
119 if (chartType) { | |
120 this._chartType = chartType; | |
121 this._chartOptions = $.extend({}, options || {}); | |
122 } | |
123 this._drawGraph(); | |
124 return this; | |
125 }, | |
126 | |
127 /* Set or retrieve additional options for the particular chart type. | |
128 @param options (object) the extra options | |
129 @return (SVGGraph) this graph object or | |
130 (object) the chart options (if no parameters) | |
131 @deprecated use options() */ | |
132 chartOptions: function(options) { | |
133 return(arguments.length == 0 ? this.options() : this.options(options)); | |
134 }, | |
135 | |
136 /* Set or retrieve additional options for the particular chart type. | |
137 @param options (object) the extra options | |
138 @return (SVGGraph) this graph object or | |
139 (object) the chart options (if no parameters) */ | |
140 options: function(options) { | |
141 if (arguments.length == 0) { | |
142 return this._chartOptions; | |
143 } | |
144 this._chartOptions = $.extend({}, options); | |
145 this._drawGraph(); | |
146 return this; | |
147 }, | |
148 | |
149 /* Set or retrieve the background of the graph chart. | |
150 @param fill (string) how to fill the chart background | |
151 @param stroke (string) the colour of the outline (optional) | |
152 @param settings (object) additional formatting for the chart background (optional) | |
153 @return (SVGGraph) this graph object or | |
154 (object) the chart format (if no parameters) | |
155 @deprecated use format() */ | |
156 chartFormat: function(fill, stroke, settings) { | |
157 return (arguments.length == 0 ? this.format() : this.format(fill, stroke, settings)); | |
158 }, | |
159 | |
160 /* Set or retrieve the background of the graph chart. | |
161 @param fill (string) how to fill the chart background | |
162 @param stroke (string) the colour of the outline (optional) | |
163 @param settings (object) additional formatting for the chart background (optional) | |
164 @return (SVGGraph) this graph object or | |
165 (object) the chart format (if no parameters) */ | |
166 format: function(fill, stroke, settings) { | |
167 if (arguments.length == 0) { | |
168 return this._chartFormat; | |
169 } | |
170 if (typeof stroke == 'object') { | |
171 settings = stroke; | |
172 stroke = null; | |
173 } | |
174 this._chartFormat = $.extend({fill: fill}, | |
175 (stroke ? {stroke: stroke} : {}), settings || {}); | |
176 this._drawGraph(); | |
177 return this; | |
178 }, | |
179 | |
180 /* Set or retrieve the main chart area. | |
181 @param left (number) > 1 is pixels, <= 1 is proportion of width or | |
182 (number[4]) for left, top, right, bottom | |
183 @param top (number) > 1 is pixels, <= 1 is proportion of height | |
184 @param right (number) > 1 is pixels, <= 1 is proportion of width | |
185 @param bottom (number) > 1 is pixels, <= 1 is proportion of height | |
186 @return (SVGGraph) this graph object or | |
187 (number[4]) the chart area: left, top, right, bottom (if no parameters) | |
188 @deprecated use area() */ | |
189 chartArea: function(left, top, right, bottom) { | |
190 return (arguments.length == 0 ? this.area() : this.area(left, top, right, bottom)); | |
191 }, | |
192 | |
193 /* Set or retrieve the main chart area. | |
194 @param left (number) > 1 is pixels, <= 1 is proportion of width or | |
195 (number[4]) for left, top, right, bottom | |
196 @param top (number) > 1 is pixels, <= 1 is proportion of height | |
197 @param right (number) > 1 is pixels, <= 1 is proportion of width | |
198 @param bottom (number) > 1 is pixels, <= 1 is proportion of height | |
199 @return (SVGGraph) this graph object or | |
200 (number[4]) the chart area: left, top, right, bottom (if no parameters) */ | |
201 area: function(left, top, right, bottom) { | |
202 if (arguments.length == 0) { | |
203 return this._area; | |
204 } | |
205 this._area = (isArray(left) ? left : [left, top, right, bottom]); | |
206 this._drawGraph(); | |
207 return this; | |
208 }, | |
209 | |
210 /* Set or retrieve the gridlines formatting for the graph chart. | |
211 @param xSettings (string) the colour of the gridlines along the x-axis, or | |
212 (object) formatting for the gridlines along the x-axis, or | |
213 null for none | |
214 @param ySettings (string) the colour of the gridlines along the y-axis, or | |
215 (object) formatting for the gridlines along the y-axis, or | |
216 null for none | |
217 @return (SVGGraph) this graph object or | |
218 (object[2]) the gridlines formatting (if no parameters) */ | |
219 gridlines: function(xSettings, ySettings) { | |
220 if (arguments.length == 0) { | |
221 return this._gridlines; | |
222 } | |
223 this._gridlines = [(typeof xSettings == 'string' ? {stroke: xSettings} : xSettings), | |
224 (typeof ySettings == 'string' ? {stroke: ySettings} : ySettings)]; | |
225 if (this._gridlines[0] == null && this._gridlines[1] == null) { | |
226 this._gridlines = []; | |
227 } | |
228 this._drawGraph(); | |
229 return this; | |
230 }, | |
231 | |
232 /* Set or retrieve the title of the graph and its formatting. | |
233 @param value (string) the title | |
234 @param offset (number) the vertical positioning of the title | |
235 > 1 is pixels, <= 1 is proportion of width (optional) | |
236 @param colour (string) the colour of the title (optional) | |
237 @param settings (object) formatting for the title (optional) | |
238 @return (SVGGraph) this graph object or | |
239 (object) value, offset, and settings for the title (if no parameters) */ | |
240 title: function(value, offset, colour, settings) { | |
241 if (arguments.length == 0) { | |
242 return this._title; | |
243 } | |
244 if (typeof offset != 'number') { | |
245 settings = colour; | |
246 colour = offset; | |
247 offset = null; | |
248 } | |
249 if (typeof colour != 'string') { | |
250 settings = colour; | |
251 colour = null; | |
252 } | |
253 this._title = {value: value, offset: offset || this._title.offset, | |
254 settings: $.extend({textAnchor: 'middle'}, | |
255 (colour ? {fill: colour} : {}), settings || {})}; | |
256 this._drawGraph(); | |
257 return this; | |
258 }, | |
259 | |
260 /* Add a series of values to be plotted on the graph. | |
261 @param name (string) the name of this series (optional) | |
262 @param values (number[]) the values to be plotted | |
263 @param fill (string) how the plotted values are filled | |
264 @param stroke (string) the colour of the plotted lines (optional) | |
265 @param strokeWidth (number) the width of the plotted lines (optional) | |
266 @param settings (object) additional settings for the plotted values (optional) | |
267 @return (SVGGraph) this graph object */ | |
268 addSeries: function(name, values, fill, stroke, strokeWidth, settings) { | |
269 this._series.push(new SVGGraphSeries( | |
270 this, name, values, fill, stroke, strokeWidth, settings)); | |
271 this._drawGraph(); | |
272 return this; | |
273 }, | |
274 | |
275 /* Retrieve the series wrappers. | |
276 @param i (number) the series index (optional) | |
277 @return (SVGGraphSeries) the specified series or | |
278 (SVGGraphSeries[]) the list of series */ | |
279 series: function(i) { | |
280 return (arguments.length > 0 ? this._series[i] : null) || this._series; | |
281 }, | |
282 | |
283 /* Suppress drawing of the graph until redraw() is called. | |
284 @return (SVGGraph) this graph object */ | |
285 noDraw: function() { | |
286 this._drawNow = false; | |
287 return this; | |
288 }, | |
289 | |
290 /* Redraw the entire graph with the current settings and values. | |
291 @return (SVGGraph) this graph object */ | |
292 redraw: function() { | |
293 this._drawNow = true; | |
294 this._drawGraph(); | |
295 return this; | |
296 }, | |
297 | |
298 /* Set the callback function for status updates. | |
299 @param onstatus (function) the callback function | |
300 @return (SVGGraph) this graph object */ | |
301 status: function(onstatus) { | |
302 this._onstatus = onstatus; | |
303 return this; | |
304 }, | |
305 | |
306 /* Actually draw the graph (if allowed) based on the graph type set. */ | |
307 _drawGraph: function() { | |
308 if (!this._drawNow) { | |
309 return; | |
310 } | |
311 while (this._chartCont.firstChild) { | |
312 this._chartCont.removeChild(this._chartCont.firstChild); | |
313 } | |
314 if (!this._chartCont.parent) { | |
315 this._wrapper._svg.appendChild(this._chartCont); | |
316 } | |
317 // Set sizes if not already there | |
318 if (!this._chartCont.width) { | |
319 this._chartCont.setAttribute('width', | |
320 parseInt(this._chartCont.getAttribute('width'), 10) || this._wrapper._width()); | |
321 } | |
322 else if (this._chartCont.width.baseVal) { | |
323 this._chartCont.width.baseVal.value = | |
324 this._chartCont.width.baseVal.value || this._wrapper._width(); | |
325 } | |
326 else { | |
327 this._chartCont.width = this._chartCont.width || this._wrapper._width(); | |
328 } | |
329 if (!this._chartCont.height) { | |
330 this._chartCont.setAttribute('height', | |
331 parseInt(this._chartCont.getAttribute('height'), 10) || this._wrapper._height()); | |
332 } | |
333 else if (this._chartCont.height.baseVal) { | |
334 this._chartCont.height.baseVal.value = | |
335 this._chartCont.height.baseVal.value || this._wrapper._height(); | |
336 } | |
337 else { | |
338 this._chartCont.height = this._chartCont.height || this._wrapper._height(); | |
339 } | |
340 this._chartType.drawGraph(this); | |
341 }, | |
342 | |
343 /* Decode an attribute value. | |
344 @param node the node to examine | |
345 @param name the attribute name | |
346 @return the actual value */ | |
347 _getValue: function(node, name) { | |
348 return (!node[name] ? parseInt(node.getAttribute(name), 10) : | |
349 (node[name].baseVal ? node[name].baseVal.value : node[name])); | |
350 }, | |
351 | |
352 /* Draw the graph title - centred. */ | |
353 _drawTitle: function() { | |
354 this._wrapper.text(this._chartCont, this._getValue(this._chartCont, 'width') / 2, | |
355 this._title.offset, this._title.value, this._title.settings); | |
356 }, | |
357 | |
358 /* Calculate the actual dimensions of the chart area. | |
359 @param area (number[4]) the area values to evaluate (optional) | |
360 @return (number[4]) an array of dimension values: left, top, width, height */ | |
361 _getDims: function(area) { | |
362 area = area || this._area; | |
363 var availWidth = this._getValue(this._chartCont, 'width'); | |
364 var availHeight = this._getValue(this._chartCont, 'height'); | |
365 var left = (area[this.L] > 1 ? area[this.L] : availWidth * area[this.L]); | |
366 var top = (area[this.T] > 1 ? area[this.T] : availHeight * area[this.T]); | |
367 var width = (area[this.R] > 1 ? area[this.R] : availWidth * area[this.R]) - left; | |
368 var height = (area[this.B] > 1 ? area[this.B] : availHeight * area[this.B]) - top; | |
369 return [left, top, width, height]; | |
370 }, | |
371 | |
372 /* Draw the chart background, including gridlines. | |
373 @param noXGrid (boolean) true to suppress the x-gridlines, false to draw them (optional) | |
374 @param noYGrid (boolean) true to suppress the y-gridlines, false to draw them (optional) | |
375 @return (element) the background group element */ | |
376 _drawChartBackground: function(noXGrid, noYGrid) { | |
377 var bg = this._wrapper.group(this._chartCont, {class_: 'background'}); | |
378 var dims = this._getDims(); | |
379 this._wrapper.rect(bg, dims[this.X], dims[this.Y], dims[this.W], dims[this.H], this._chartFormat); | |
380 if (this._gridlines[0] && this.yAxis._ticks.major && !noYGrid) { | |
381 this._drawGridlines(bg, this.yAxis, true, dims, this._gridlines[0]); | |
382 } | |
383 if (this._gridlines[1] && this.xAxis._ticks.major && !noXGrid) { | |
384 this._drawGridlines(bg, this.xAxis, false, dims, this._gridlines[1]); | |
385 } | |
386 return bg; | |
387 }, | |
388 | |
389 /* Draw one set of gridlines. | |
390 @param bg (element) the background group element | |
391 @param axis (SVGGraphAxis) the axis definition | |
392 @param horiz (boolean) true if horizontal, false if vertical | |
393 @param dims (number[]) the left, top, width, height of the chart area | |
394 @param format (object) additional settings for the gridlines */ | |
395 _drawGridlines: function(bg, axis, horiz, dims, format) { | |
396 var g = this._wrapper.group(bg, format); | |
397 var scale = (horiz ? dims[this.H] : dims[this.W]) / (axis._scale.max - axis._scale.min); | |
398 var major = Math.floor(axis._scale.min / axis._ticks.major) * axis._ticks.major; | |
399 major = (major < axis._scale.min ? major + axis._ticks.major : major); | |
400 while (major <= axis._scale.max) { | |
401 var v = (horiz ? axis._scale.max - major : major - axis._scale.min) * scale + | |
402 (horiz ? dims[this.Y] : dims[this.X]); | |
403 this._wrapper.line(g, (horiz ? dims[this.X] : v), (horiz ? v : dims[this.Y]), | |
404 (horiz ? dims[this.X] + dims[this.W] : v), (horiz ? v : dims[this.Y] + dims[this.H])); | |
405 major += axis._ticks.major; | |
406 } | |
407 }, | |
408 | |
409 /* Draw the axes in their standard configuration. | |
410 @param noX (boolean) true to suppress the x-axes, false to draw it (optional) */ | |
411 _drawAxes: function(noX) { | |
412 var dims = this._getDims(); | |
413 if (this.xAxis && !noX) { | |
414 if (this.xAxis._title) { | |
415 this._wrapper.text(this._chartCont, dims[this.X] + dims[this.W] / 2, | |
416 dims[this.Y] + dims[this.H] + this.xAxis._titleOffset, | |
417 this.xAxis._title, this.xAxis._titleFormat); | |
418 } | |
419 this._drawAxis(this.xAxis, 'xAxis', dims[this.X], dims[this.Y] + dims[this.H], | |
420 dims[this.X] + dims[this.W], dims[this.Y] + dims[this.H]); | |
421 } | |
422 if (this.yAxis) { | |
423 if (this.yAxis._title) { | |
424 this._wrapper.text(this._chartCont, 0, 0, this.yAxis._title, $.extend({textAnchor: 'middle', | |
425 transform: 'translate(' + (dims[this.X] - this.yAxis._titleOffset) + ',' + | |
426 (dims[this.Y] + dims[this.H] / 2) + ') rotate(-90)'}, this.yAxis._titleFormat || {})); | |
427 } | |
428 this._drawAxis(this.yAxis, 'yAxis', dims[this.X], dims[this.Y], | |
429 dims[this.X], dims[this.Y] + dims[this.H]); | |
430 } | |
431 if (this.x2Axis && !noX) { | |
432 if (this.x2Axis._title) { | |
433 this._wrapper.text(this._chartCont, dims[this.X] + dims[this.W] / 2, | |
434 dims[this.X] - this.x2Axis._titleOffset, this.x2Axis._title, this.x2Axis._titleFormat); | |
435 } | |
436 this._drawAxis(this.x2Axis, 'x2Axis', dims[this.X], dims[this.Y], | |
437 dims[this.X] + dims[this.W], dims[this.Y]); | |
438 } | |
439 if (this.y2Axis) { | |
440 if (this.y2Axis._title) { | |
441 this._wrapper.text(this._chartCont, 0, 0, this.y2Axis._title, $.extend({textAnchor: 'middle', | |
442 transform: 'translate(' + (dims[this.X] + dims[this.W] + this.y2Axis._titleOffset) + | |
443 ',' + (dims[this.Y] + dims[this.H] / 2) + ') rotate(-90)'}, this.y2Axis._titleFormat || {})); | |
444 } | |
445 this._drawAxis(this.y2Axis, 'y2Axis', dims[this.X] + dims[this.W], dims[this.Y], | |
446 dims[this.X] + dims[this.W], dims[this.Y] + dims[this.H]); | |
447 } | |
448 }, | |
449 | |
450 /* Draw an axis and its tick marks. | |
451 @param axis (SVGGraphAxis) the axis definition | |
452 @param id (string) the identifier for the axis group element | |
453 @param x1 (number) starting x-coodinate for the axis | |
454 @param y1 (number) starting y-coodinate for the axis | |
455 @param x2 (number) ending x-coodinate for the axis | |
456 @param y2 (number) ending y-coodinate for the axis */ | |
457 _drawAxis: function(axis, id, x1, y1, x2, y2) { | |
458 var horiz = (y1 == y2); | |
459 var gl = this._wrapper.group(this._chartCont, $.extend({class_: id}, axis._lineFormat)); | |
460 var gt = this._wrapper.group(this._chartCont, $.extend({class_: id + 'Labels', | |
461 textAnchor: (horiz ? 'middle' : 'end')}, axis._labelFormat)); | |
462 this._wrapper.line(gl, x1, y1, x2, y2); | |
463 if (axis._ticks.major) { | |
464 var bottomRight = (x2 > (this._getValue(this._chartCont, 'width') / 2) && | |
465 y2 > (this._getValue(this._chartCont, 'height') / 2)); | |
466 var scale = (horiz ? x2 - x1 : y2 - y1) / (axis._scale.max - axis._scale.min); | |
467 var size = axis._ticks.size; | |
468 var major = Math.floor(axis._scale.min / axis._ticks.major) * axis._ticks.major; | |
469 major = (major < axis._scale.min ? major + axis._ticks.major : major); | |
470 var minor = (!axis._ticks.minor ? axis._scale.max + 1 : | |
471 Math.floor(axis._scale.min / axis._ticks.minor) * axis._ticks.minor); | |
472 minor = (minor < axis._scale.min ? minor + axis._ticks.minor : minor); | |
473 var offsets = this._getTickOffsets(axis, bottomRight); | |
474 var count = 0; | |
475 while (major <= axis._scale.max || minor <= axis._scale.max) { | |
476 var cur = Math.min(major, minor); | |
477 var len = (cur == major ? size : size / 2); | |
478 var v = (horiz ? x1 : y1) + | |
479 (horiz ? cur - axis._scale.min : axis._scale.max - cur) * scale; | |
480 this._wrapper.line(gl, (horiz ? v : x1 + len * offsets[0]), | |
481 (horiz ? y1 + len * offsets[0] : v), | |
482 (horiz ? v : x1 + len * offsets[1]), | |
483 (horiz ? y1 + len * offsets[1] : v)); | |
484 if (cur == major) { | |
485 this._wrapper.text(gt, (horiz ? v : x1 - size), (horiz ? y1 + 2 * size : v), | |
486 (axis._labels ? axis._labels[count++] : '' + cur)); | |
487 } | |
488 major += (cur == major ? axis._ticks.major : 0); | |
489 minor += (cur == minor ? axis._ticks.minor : 0); | |
490 } | |
491 } | |
492 }, | |
493 | |
494 /* Calculate offsets based on axis and tick positions. | |
495 @param axis (SVGGraphAxis) the axis definition | |
496 @param bottomRight (boolean) true if this axis is appearing on the bottom or | |
497 right of the chart area, false if to the top or left | |
498 @return (number[2]) the array of offset multipliers (-1..+1) */ | |
499 _getTickOffsets: function(axis, bottomRight) { | |
500 return [(axis._ticks.position == (bottomRight ? 'in' : 'out') || | |
501 axis._ticks.position == 'both' ? -1 : 0), | |
502 (axis._ticks.position == (bottomRight ? 'out' : 'in') || | |
503 axis._ticks.position == 'both' ? +1 : 0), ]; | |
504 }, | |
505 | |
506 /* Retrieve the standard percentage axis. | |
507 @return (SVGGraphAxis) percentage axis */ | |
508 _getPercentageAxis: function() { | |
509 this._percentageAxis._title = $.svg.graphing.region.percentageText; | |
510 return this._percentageAxis; | |
511 }, | |
512 | |
513 /* Calculate the column totals across all the series. */ | |
514 _getTotals: function() { | |
515 var totals = []; | |
516 var numVal = (this._series.length ? this._series[0]._values.length : 0); | |
517 for (var i = 0; i < numVal; i++) { | |
518 totals[i] = 0; | |
519 for (var j = 0; j < this._series.length; j++) { | |
520 totals[i] += this._series[j]._values[i]; | |
521 } | |
522 } | |
523 return totals; | |
524 }, | |
525 | |
526 /* Draw the chart legend. */ | |
527 _drawLegend: function() { | |
528 if (!this.legend._show) { | |
529 return; | |
530 } | |
531 var g = this._wrapper.group(this._chartCont, {class_: 'legend'}); | |
532 var dims = this._getDims(this.legend._area); | |
533 this._wrapper.rect(g, dims[this.X], dims[this.Y], dims[this.W], dims[this.H], | |
534 this.legend._bgSettings); | |
535 var horiz = dims[this.W] > dims[this.H]; | |
536 var numSer = this._series.length; | |
537 var offset = (horiz ? dims[this.W] : dims[this.H]) / numSer; | |
538 var xBase = dims[this.X] + 5; | |
539 var yBase = dims[this.Y] + ((horiz ? dims[this.H] : offset) + this.legend._sampleSize) / 2; | |
540 for (var i = 0; i < numSer; i++) { | |
541 var series = this._series[i]; | |
542 this._wrapper.rect(g, xBase + (horiz ? i * offset : 0), | |
543 yBase + (horiz ? 0 : i * offset) - this.legend._sampleSize, | |
544 this.legend._sampleSize, this.legend._sampleSize, | |
545 {fill: series._fill, stroke: series._stroke, strokeWidth: 1}); | |
546 this._wrapper.text(g, xBase + (horiz ? i * offset : 0) + this.legend._sampleSize + 5, | |
547 yBase + (horiz ? 0 : i * offset), series._name, this.legend._textSettings); | |
548 } | |
549 }, | |
550 | |
551 /* Show the current value status on hover. */ | |
552 _showStatus: function(elem, label, value) { | |
553 var status = this._onstatus; | |
554 if (this._onstatus) { | |
555 $(elem).hover(function() { status.apply(this, [label, value]); }, | |
556 function() { status.apply(this, ['', 0]); }); | |
557 } | |
558 } | |
559 }); | |
560 | |
561 /* Details about each graph series. | |
562 @param graph (SVGGraph) the owning graph | |
563 @param name (string) the name of this series (optional) | |
564 @param values (number[]) the list of values to be plotted | |
565 @param fill (string) how the series should be displayed | |
566 @param stroke (string) the colour of the (out)line for the series (optional) | |
567 @param strokeWidth (number) the width of the (out)line for the series (optional) | |
568 @param settings (object) additional formatting settings (optional) | |
569 @return (SVGGraphSeries) the new series object */ | |
570 function SVGGraphSeries(graph, name, values, fill, stroke, strokeWidth, settings) { | |
571 if (typeof name != 'string') { | |
572 settings = strokeWidth; | |
573 strokeWidth = stroke; | |
574 stroke = fill; | |
575 fill = values; | |
576 values = name; | |
577 name = null; | |
578 } | |
579 if (typeof stroke != 'string') { | |
580 settings = strokeWidth; | |
581 strokeWidth = stroke; | |
582 stroke = null; | |
583 } | |
584 if (typeof strokeWidth != 'number') { | |
585 settings = strokeWidth; | |
586 strokeWidth = null; | |
587 } | |
588 this._graph = graph; // The owning graph | |
589 this._name = name || ''; // The name of this series | |
590 this._values = values || []; // The list of values for this series | |
591 this._axis = 1; // Which axis this series applies to: 1 = primary, 2 = secondary | |
592 this._fill = fill || 'green'; // How the series is plotted | |
593 this._stroke = stroke || 'black'; // The colour for the (out)line | |
594 this._strokeWidth = strokeWidth || 1; // The (out)line width | |
595 this._settings = settings || {}; // Additional formatting settings for the series | |
596 } | |
597 | |
598 $.extend(SVGGraphSeries.prototype, { | |
599 | |
600 /* Set or retrieve the name for this series. | |
601 @param name (string) the series' name | |
602 @return (SVGGraphSeries) this series object or | |
603 (string) the series name (if no parameters) */ | |
604 name: function(name) { | |
605 if (arguments.length == 0) { | |
606 return this._name; | |
607 } | |
608 this._name = name; | |
609 this._graph._drawGraph(); | |
610 return this; | |
611 }, | |
612 | |
613 /* Set or retrieve the values for this series. | |
614 @param name (string) the series' name (optional) | |
615 @param values (number[]) the values to be graphed | |
616 @return (SVGGraphSeries) this series object or | |
617 (number[]) the series values (if no parameters) */ | |
618 values: function(name, values) { | |
619 if (arguments.length == 0) { | |
620 return this._values; | |
621 } | |
622 if (isArray(name)) { | |
623 values = name; | |
624 name = null; | |
625 } | |
626 this._name = name || this._name; | |
627 this._values = values; | |
628 this._graph._drawGraph(); | |
629 return this; | |
630 }, | |
631 | |
632 /* Set or retrieve the formatting for this series. | |
633 @param fill (string) how the values are filled when plotted | |
634 @param stroke (string) the (out)line colour (optional) | |
635 @param strokeWidth (number) the line's width (optional) | |
636 @param settings (object) additional formatting settings for the series (optional) | |
637 @return (SVGGraphSeries) this series object or | |
638 (object) formatting settings (if no parameters) */ | |
639 format: function(fill, stroke, strokeWidth, settings) { | |
640 if (arguments.length == 0) { | |
641 return $.extend({fill: this._fill, stroke: this._stroke, | |
642 strokeWidth: this._strokeWidth}, this._settings); | |
643 } | |
644 if (typeof stroke != 'string') { | |
645 settings = strokeWidth; | |
646 strokeWidth = stroke; | |
647 stroke = null; | |
648 } | |
649 if (typeof strokeWidth != 'number') { | |
650 settings = strokeWidth; | |
651 strokeWidth = null; | |
652 } | |
653 this._fill = fill || this._fill; | |
654 this._stroke = stroke || this._stroke; | |
655 this._strokeWidth = strokeWidth || this._strokeWidth; | |
656 $.extend(this._settings, settings || {}); | |
657 this._graph._drawGraph(); | |
658 return this; | |
659 }, | |
660 | |
661 /* Return to the parent graph. */ | |
662 end: function() { | |
663 return this._graph; | |
664 } | |
665 }); | |
666 | |
667 /* Details about each graph axis. | |
668 @param graph (SVGGraph) the owning graph | |
669 @param title (string) the title of the axis | |
670 @param min (number) the minimum value displayed on this axis | |
671 @param max (number) the maximum value displayed on this axis | |
672 @param major (number) the distance between major ticks | |
673 @param minor (number) the distance between minor ticks (optional) | |
674 @return (SVGGraphAxis) the new axis object */ | |
675 function SVGGraphAxis(graph, title, min, max, major, minor) { | |
676 this._graph = graph; // The owning graph | |
677 this._title = title || ''; // Title of this axis | |
678 this._titleFormat = {}; // Formatting settings for the title | |
679 this._titleOffset = 0; // The offset for positioning the title | |
680 this._labels = null; // List of labels for this axis - one per possible value across all series | |
681 this._labelFormat = {}; // Formatting settings for the labels | |
682 this._lineFormat = {stroke: 'black', strokeWidth: 1}; // Formatting settings for the axis lines | |
683 this._ticks = {major: major || 10, minor: minor || 0, size: 10, position: 'out'}; // Tick mark options | |
684 this._scale = {min: min || 0, max: max || 100}; // Axis scale settings | |
685 this._crossAt = 0; // Where this axis crosses the other one | |
686 } | |
687 | |
688 $.extend(SVGGraphAxis.prototype, { | |
689 | |
690 /* Set or retrieve the scale for this axis. | |
691 @param min (number) the minimum value shown | |
692 @param max (number) the maximum value shown | |
693 @return (SVGGraphAxis) this axis object or | |
694 (object) min and max values (if no parameters) */ | |
695 scale: function(min, max) { | |
696 if (arguments.length == 0) { | |
697 return this._scale; | |
698 } | |
699 this._scale.min = min; | |
700 this._scale.max = max; | |
701 this._graph._drawGraph(); | |
702 return this; | |
703 }, | |
704 | |
705 /* Set or retrieve the ticks for this axis. | |
706 @param major (number) the distance between major ticks | |
707 @param minor (number) the distance between minor ticks | |
708 @param size (number) the length of the major ticks (minor are half) (optional) | |
709 @param position (string) the location of the ticks: | |
710 'in', 'out', 'both' (optional) | |
711 @return (SVGGraphAxis) this axis object or | |
712 (object) major, minor, size, and position values (if no parameters) */ | |
713 ticks: function(major, minor, size, position) { | |
714 if (arguments.length == 0) { | |
715 return this._ticks; | |
716 } | |
717 if (typeof size == 'string') { | |
718 position = size; | |
719 size = null; | |
720 } | |
721 this._ticks.major = major; | |
722 this._ticks.minor = minor; | |
723 this._ticks.size = size || this._ticks.size; | |
724 this._ticks.position = position || this._ticks.position; | |
725 this._graph._drawGraph(); | |
726 return this; | |
727 }, | |
728 | |
729 /* Set or retrieve the title for this axis. | |
730 @param title (string) the title text | |
731 @param offset (number) the distance to offset the title position (optional) | |
732 @param colour (string) how to colour the title (optional) | |
733 @param format (object) formatting settings for the title (optional) | |
734 @return (SVGGraphAxis) this axis object or | |
735 (object) title, offset, and format values (if no parameters) */ | |
736 title: function(title, offset, colour, format) { | |
737 if (arguments.length == 0) { | |
738 return {title: this._title, offset: this._titleOffset, format: this._titleFormat}; | |
739 } | |
740 if (typeof offset != 'number') { | |
741 format = colour; | |
742 colour = offset; | |
743 offset = null; | |
744 } | |
745 if (typeof colour != 'string') { | |
746 format = colour; | |
747 colour = null; | |
748 } | |
749 this._title = title; | |
750 this._titleOffset = (offset != null ? offset : this._titleOffset); | |
751 if (colour || format) { | |
752 this._titleFormat = $.extend(format || {}, (colour ? {fill: colour} : {})); | |
753 } | |
754 this._graph._drawGraph(); | |
755 return this; | |
756 }, | |
757 | |
758 /* Set or retrieve the labels for this axis. | |
759 @param labels (string[]) the text for each entry | |
760 @param colour (string) how to colour the labels (optional) | |
761 @param format (object) formatting settings for the labels (optional) | |
762 @return (SVGGraphAxis) this axis object or | |
763 (object) labels and format values (if no parameters) */ | |
764 labels: function(labels, colour, format) { | |
765 if (arguments.length == 0) { | |
766 return {labels: this._labels, format: this._labelFormat}; | |
767 } | |
768 if (typeof colour != 'string') { | |
769 format = colour; | |
770 colour = null; | |
771 } | |
772 this._labels = labels; | |
773 if (colour || format) { | |
774 this._labelFormat = $.extend(format || {}, (colour ? {fill: colour} : {})); | |
775 } | |
776 this._graph._drawGraph(); | |
777 return this; | |
778 }, | |
779 | |
780 /* Set or retrieve the line formatting for this axis. | |
781 @param colour (string) the line's colour | |
782 @param width (number) the line's width (optional) | |
783 @param settings (object) additional formatting settings for the line (optional) | |
784 @return (SVGGraphAxis) this axis object or | |
785 (object) line formatting values (if no parameters) */ | |
786 line: function(colour, width, settings) { | |
787 if (arguments.length == 0) { | |
788 return this._lineFormat; | |
789 } | |
790 if (typeof width == 'object') { | |
791 settings = width; | |
792 width = null; | |
793 } | |
794 $.extend(this._lineFormat, {stroke: colour}, | |
795 (width ? {strokeWidth: width} : {}), settings || {}); | |
796 this._graph._drawGraph(); | |
797 return this; | |
798 }, | |
799 | |
800 /* Return to the parent graph. */ | |
801 end: function() { | |
802 return this._graph; | |
803 } | |
804 }); | |
805 | |
806 /* Details about the graph legend. | |
807 @param graph (SVGGraph) the owning graph | |
808 @param bgSettings (object) additional formatting settings for the legend background (optional) | |
809 @param textSettings (object) additional formatting settings for the legend text (optional) | |
810 @return (SVGGraphLegend) the new legend object */ | |
811 function SVGGraphLegend(graph, bgSettings, textSettings) { | |
812 this._graph = graph; // The owning graph | |
813 this._show = true; // Show the legend? | |
814 this._area = [0.9, 0.1, 1.0, 0.9]; // The legend area: left, top, right, bottom, | |
815 // > 1 in pixels, <= 1 as proportion | |
816 this._sampleSize = 15; // Size of sample box | |
817 this._bgSettings = bgSettings || {stroke: 'gray'}; // Additional formatting settings for the legend background | |
818 this._textSettings = textSettings || {}; // Additional formatting settings for the text | |
819 } | |
820 | |
821 $.extend(SVGGraphLegend.prototype, { | |
822 | |
823 /* Set or retrieve whether the legend should be shown. | |
824 @param show (boolean) true to display it, false to hide it | |
825 @return (SVGGraphLegend) this legend object or | |
826 (boolean) show the legend? (if no parameters) */ | |
827 show: function(show) { | |
828 if (arguments.length == 0) { | |
829 return this._show; | |
830 } | |
831 this._show = show; | |
832 this._graph._drawGraph(); | |
833 return this; | |
834 }, | |
835 | |
836 /* Set or retrieve the legend area. | |
837 @param left (number) > 1 is pixels, <= 1 is proportion of width or | |
838 (number[4]) for left, top, right, bottom | |
839 @param top (number) > 1 is pixels, <= 1 is proportion of height | |
840 @param right (number) > 1 is pixels, <= 1 is proportion of width | |
841 @param bottom (number) > 1 is pixels, <= 1 is proportion of height | |
842 @return (SVGGraphLegend) this legend object or | |
843 (number[4]) the legend area: left, top, right, bottom (if no parameters) */ | |
844 area: function(left, top, right, bottom) { | |
845 if (arguments.length == 0) { | |
846 return this._area; | |
847 } | |
848 this._area = (isArray(left) ? left : [left, top, right, bottom]); | |
849 this._graph._drawGraph(); | |
850 return this; | |
851 }, | |
852 | |
853 /* Set or retrieve additional settings for the legend area. | |
854 @param sampleSize (number) the size of the sample box to display (optional) | |
855 @param bgSettings (object) additional formatting settings for the legend background | |
856 @param textSettings (object) additional formatting settings for the legend text (optional) | |
857 @return (SVGGraphLegend) this legend object or | |
858 (object) bgSettings and textSettings for the legend (if no parameters) */ | |
859 settings: function(sampleSize, bgSettings, textSettings) { | |
860 if (arguments.length == 0) { | |
861 return {sampleSize: this._sampleSize, bgSettings: this._bgSettings, | |
862 textSettings: this._textSettings}; | |
863 } | |
864 if (typeof sampleSize != 'number') { | |
865 textSettings = bgSettings; | |
866 bgSettings = sampleSize; | |
867 sampleSize = null; | |
868 } | |
869 this._sampleSize = sampleSize || this._sampleSize; | |
870 this._bgSettings = bgSettings; | |
871 this._textSettings = textSettings || this._textSettings; | |
872 this._graph._drawGraph(); | |
873 return this; | |
874 }, | |
875 | |
876 /* Return to the parent graph. */ | |
877 end: function() { | |
878 return this._graph; | |
879 } | |
880 }); | |
881 | |
882 //============================================================================== | |
883 | |
884 /* Round a number to a given number of decimal points. */ | |
885 function roundNumber(num, dec) { | |
886 return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec); | |
887 } | |
888 | |
889 var barOptions = ['barWidth (number) - the width of each bar', | |
890 'barGap (number) - the gap between sets of bars']; | |
891 | |
892 //------------------------------------------------------------------------------ | |
893 | |
894 /* Draw a standard grouped column bar chart. */ | |
895 function SVGColumnChart() { | |
896 } | |
897 | |
898 $.extend(SVGColumnChart.prototype, { | |
899 | |
900 /* Retrieve the display title for this chart type. | |
901 @return the title */ | |
902 title: function() { | |
903 return 'Basic column chart'; | |
904 }, | |
905 | |
906 /* Retrieve a description of this chart type. | |
907 @return its description */ | |
908 description: function() { | |
909 return 'Compare sets of values as vertical bars with grouped categories.'; | |
910 }, | |
911 | |
912 /* Retrieve a list of the options that may be set for this chart type. | |
913 @return options list */ | |
914 options: function() { | |
915 return barOptions; | |
916 }, | |
917 | |
918 /* Actually draw the graph in this type's style. | |
919 @param graph (object) the SVGGraph object */ | |
920 drawGraph: function(graph) { | |
921 graph._drawChartBackground(true); | |
922 var barWidth = graph._chartOptions.barWidth || 10; | |
923 var barGap = graph._chartOptions.barGap || 10; | |
924 var numSer = graph._series.length; | |
925 var numVal = (numSer ? (graph._series[0])._values.length : 0); | |
926 var dims = graph._getDims(); | |
927 var xScale = dims[graph.W] / ((numSer * barWidth + barGap) * numVal + barGap); | |
928 var yScale = dims[graph.H] / (graph.yAxis._scale.max - graph.yAxis._scale.min); | |
929 this._chart = graph._wrapper.group(graph._chartCont, {class_: 'chart'}); | |
930 for (var i = 0; i < numSer; i++) { | |
931 this._drawSeries(graph, i, numSer, barWidth, barGap, dims, xScale, yScale); | |
932 } | |
933 graph._drawTitle(); | |
934 graph._drawAxes(true); | |
935 this._drawXAxis(graph, numSer, numVal, barWidth, barGap, dims, xScale); | |
936 graph._drawLegend(); | |
937 }, | |
938 | |
939 /* Plot an individual series. */ | |
940 _drawSeries: function(graph, cur, numSer, barWidth, barGap, dims, xScale, yScale) { | |
941 var series = graph._series[cur]; | |
942 var g = graph._wrapper.group(this._chart, | |
943 $.extend({class_: 'series' + cur, fill: series._fill, stroke: series._stroke, | |
944 strokeWidth: series._strokeWidth}, series._settings || {})); | |
945 for (var i = 0; i < series._values.length; i++) { | |
946 var r = graph._wrapper.rect(g, | |
947 dims[graph.X] + xScale * (barGap + i * (numSer * barWidth + barGap) + (cur * barWidth)), | |
948 dims[graph.Y] + yScale * (graph.yAxis._scale.max - series._values[i]), | |
949 xScale * barWidth, yScale * series._values[i]); | |
950 graph._showStatus(r, series._name, series._values[i]); | |
951 } | |
952 }, | |
953 | |
954 /* Draw the x-axis and its ticks. */ | |
955 _drawXAxis: function(graph, numSer, numVal, barWidth, barGap, dims, xScale) { | |
956 var axis = graph.xAxis; | |
957 if (axis._title) { | |
958 graph._wrapper.text(graph._chartCont, dims[graph.X] + dims[graph.W] / 2, | |
959 dims[graph.Y] + dims[graph.H] + axis._titleOffset, | |
960 axis._title, $.extend({textAnchor: 'middle'}, axis._titleFormat || {})); | |
961 } | |
962 var gl = graph._wrapper.group(graph._chartCont, $.extend({class_: 'xAxis'}, axis._lineFormat)); | |
963 var gt = graph._wrapper.group(graph._chartCont, $.extend({class_: 'xAxisLabels', | |
964 textAnchor: 'middle'}, axis._labelFormat)); | |
965 graph._wrapper.line(gl, dims[graph.X], dims[graph.Y] + dims[graph.H], | |
966 dims[graph.X] + dims[graph.W], dims[graph.Y] + dims[graph.H]); | |
967 if (axis._ticks.major) { | |
968 var offsets = graph._getTickOffsets(axis, true); | |
969 for (var i = 1; i < numVal; i++) { | |
970 var x = dims[graph.X] + xScale * (barGap / 2 + i * (numSer * barWidth + barGap)); | |
971 graph._wrapper.line(gl, x, dims[graph.Y] + dims[graph.H] + offsets[0] * axis._ticks.size, | |
972 x, dims[graph.Y] + dims[graph.H] + offsets[1] * axis._ticks.size); | |
973 } | |
974 for (var i = 0; i < numVal; i++) { | |
975 var x = dims[graph.X] + xScale * (barGap / 2 + (i + 0.5) * (numSer * barWidth + barGap)); | |
976 graph._wrapper.text(gt, x, dims[graph.Y] + dims[graph.H] + 2 * axis._ticks.size, | |
977 (axis._labels ? axis._labels[i] : '' + i)); | |
978 } | |
979 } | |
980 } | |
981 }); | |
982 | |
983 //------------------------------------------------------------------------------ | |
984 | |
985 /* Draw a stacked column bar chart. */ | |
986 function SVGStackedColumnChart() { | |
987 } | |
988 | |
989 $.extend(SVGStackedColumnChart.prototype, { | |
990 | |
991 /* Retrieve the display title for this chart type. | |
992 @return the title */ | |
993 title: function() { | |
994 return 'Stacked column chart'; | |
995 }, | |
996 | |
997 /* Retrieve a description of this chart type. | |
998 @return its description */ | |
999 description: function() { | |
1000 return 'Compare sets of values as vertical bars showing ' + | |
1001 'relative contributions to the whole for each category.'; | |
1002 }, | |
1003 | |
1004 /* Retrieve a list of the options that may be set for this chart type. | |
1005 @return options list */ | |
1006 options: function() { | |
1007 return barOptions; | |
1008 }, | |
1009 | |
1010 /* Actually draw the graph in this type's style. | |
1011 @param graph (object) the SVGGraph object */ | |
1012 drawGraph: function(graph) { | |
1013 var bg = graph._drawChartBackground(true, true); | |
1014 var dims = graph._getDims(); | |
1015 if (graph._gridlines[0] && graph.xAxis._ticks.major) { | |
1016 graph._drawGridlines(bg, graph._getPercentageAxis(), true, dims, graph._gridlines[0]); | |
1017 } | |
1018 var barWidth = graph._chartOptions.barWidth || 10; | |
1019 var barGap = graph._chartOptions.barGap || 10; | |
1020 var numSer = graph._series.length; | |
1021 var numVal = (numSer ? (graph._series[0])._values.length : 0); | |
1022 var xScale = dims[graph.W] / ((barWidth + barGap) * numVal + barGap); | |
1023 var yScale = dims[graph.H]; | |
1024 this._chart = graph._wrapper.group(graph._chartCont, {class_: 'chart'}); | |
1025 this._drawColumns(graph, numSer, numVal, barWidth, barGap, dims, xScale, yScale); | |
1026 graph._drawTitle(); | |
1027 graph._wrapper.text(graph._chartCont, 0, 0, $.svg.graphing.region.percentageText, | |
1028 $.extend({textAnchor: 'middle', transform: 'translate(' + | |
1029 (dims[graph.X] - graph.yAxis._titleOffset) + ',' + | |
1030 (dims[graph.Y] + dims[graph.H] / 2) + ') rotate(-90)'}, graph.yAxis._titleFormat || {})); | |
1031 var pAxis = $.extend({}, graph._getPercentageAxis()); | |
1032 $.extend(pAxis._labelFormat, graph.yAxis._labelFormat || {}); | |
1033 graph._drawAxis(pAxis, 'yAxis', dims[graph.X], dims[graph.Y], | |
1034 dims[graph.X], dims[graph.Y] + dims[graph.H]); | |
1035 this._drawXAxis(graph, numVal, barWidth, barGap, dims, xScale); | |
1036 graph._drawLegend(); | |
1037 }, | |
1038 | |
1039 /* Plot all of the columns. */ | |
1040 _drawColumns: function(graph, numSer, numVal, barWidth, barGap, dims, xScale, yScale) { | |
1041 var totals = graph._getTotals(); | |
1042 var accum = []; | |
1043 for (var i = 0; i < numVal; i++) { | |
1044 accum[i] = 0; | |
1045 } | |
1046 for (var s = 0; s < numSer; s++) { | |
1047 var series = graph._series[s]; | |
1048 var g = graph._wrapper.group(this._chart, | |
1049 $.extend({class_: 'series' + s, fill: series._fill, | |
1050 stroke: series._stroke, strokeWidth: series._strokeWidth}, | |
1051 series._settings || {})); | |
1052 for (var i = 0; i < series._values.length; i++) { | |
1053 accum[i] += series._values[i]; | |
1054 var r = graph._wrapper.rect(g, | |
1055 dims[graph.X] + xScale * (barGap + i * (barWidth + barGap)), | |
1056 dims[graph.Y] + yScale * (totals[i] - accum[i]) / totals[i], | |
1057 xScale * barWidth, yScale * series._values[i] / totals[i]); | |
1058 graph._showStatus(r, series._name, | |
1059 roundNumber(series._values[i] / totals[i] * 100, 2)); | |
1060 } | |
1061 } | |
1062 }, | |
1063 | |
1064 /* Draw the x-axis and its ticks. */ | |
1065 _drawXAxis: function(graph, numVal, barWidth, barGap, dims, xScale) { | |
1066 var axis = graph.xAxis; | |
1067 if (axis._title) { | |
1068 graph._wrapper.text(graph._chartCont, dims[graph.X] + dims[graph.W] / 2, | |
1069 dims[graph.Y] + dims[graph.H] + axis._titleOffset, | |
1070 axis._title, $.extend({textAnchor: 'middle'}, axis._titleFormat || {})); | |
1071 } | |
1072 var gl = graph._wrapper.group(graph._chartCont, $.extend({class_: 'xAxis'}, axis._lineFormat)); | |
1073 var gt = graph._wrapper.group(graph._chartCont, $.extend({class_: 'xAxisLabels', | |
1074 textAnchor: 'middle'}, axis._labelFormat)); | |
1075 graph._wrapper.line(gl, dims[graph.X], dims[graph.Y] + dims[graph.H], | |
1076 dims[graph.X] + dims[graph.W], dims[graph.Y] + dims[graph.H]); | |
1077 if (axis._ticks.major) { | |
1078 var offsets = graph._getTickOffsets(axis, true); | |
1079 for (var i = 1; i < numVal; i++) { | |
1080 var x = dims[graph.X] + xScale * (barGap / 2 + i * (barWidth + barGap)); | |
1081 graph._wrapper.line(gl, x, dims[graph.Y] + dims[graph.H] + offsets[0] * axis._ticks.size, | |
1082 x, dims[graph.Y] + dims[graph.H] + offsets[1] * axis._ticks.size); | |
1083 } | |
1084 for (var i = 0; i < numVal; i++) { | |
1085 var x = dims[graph.X] + xScale * (barGap / 2 + (i + 0.5) * (barWidth + barGap)); | |
1086 graph._wrapper.text(gt, x, dims[graph.Y] + dims[graph.H] + 2 * axis._ticks.size, | |
1087 (axis._labels ? axis._labels[i] : '' + i)); | |
1088 } | |
1089 } | |
1090 } | |
1091 }); | |
1092 | |
1093 //------------------------------------------------------------------------------ | |
1094 | |
1095 /* Draw a standard grouped row bar chart. */ | |
1096 function SVGRowChart() { | |
1097 } | |
1098 | |
1099 $.extend(SVGRowChart.prototype, { | |
1100 | |
1101 /* Retrieve the display title for this chart type. | |
1102 @return the title */ | |
1103 title: function() { | |
1104 return 'Basic row chart'; | |
1105 }, | |
1106 | |
1107 /* Retrieve a description of this chart type. | |
1108 @return its description */ | |
1109 description: function() { | |
1110 return 'Compare sets of values as horizontal rows with grouped categories.'; | |
1111 }, | |
1112 | |
1113 /* Retrieve a list of the options that may be set for this chart type. | |
1114 @return options list */ | |
1115 options: function() { | |
1116 return barOptions; | |
1117 }, | |
1118 | |
1119 /* Actually draw the graph in this type's style. | |
1120 @param graph (object) the SVGGraph object */ | |
1121 drawGraph: function(graph) { | |
1122 var bg = graph._drawChartBackground(true, true); | |
1123 var dims = graph._getDims(); | |
1124 graph._drawGridlines(bg, graph.yAxis, false, dims, graph._gridlines[0]); | |
1125 var barWidth = graph._chartOptions.barWidth || 10; | |
1126 var barGap = graph._chartOptions.barGap || 10; | |
1127 var numSer = graph._series.length; | |
1128 var numVal = (numSer ? (graph._series[0])._values.length : 0); | |
1129 var xScale = dims[graph.W] / (graph.yAxis._scale.max - graph.yAxis._scale.min); | |
1130 var yScale = dims[graph.H] / ((numSer * barWidth + barGap) * numVal + barGap); | |
1131 this._chart = graph._wrapper.group(graph._chartCont, {class_: 'chart'}); | |
1132 for (var i = 0; i < numSer; i++) { | |
1133 this._drawSeries(graph, i, numSer, barWidth, barGap, dims, xScale, yScale); | |
1134 } | |
1135 graph._drawTitle(); | |
1136 this._drawAxes(graph, numSer, numVal, barWidth, barGap, dims, yScale); | |
1137 graph._drawLegend(); | |
1138 }, | |
1139 | |
1140 /* Plot an individual series. */ | |
1141 _drawSeries: function(graph, cur, numSer, barWidth, barGap, dims, xScale, yScale) { | |
1142 var series = graph._series[cur]; | |
1143 var g = graph._wrapper.group(this._chart, | |
1144 $.extend({class_: 'series' + cur, fill: series._fill, | |
1145 stroke: series._stroke, strokeWidth: series._strokeWidth}, | |
1146 series._settings || {})); | |
1147 for (var i = 0; i < series._values.length; i++) { | |
1148 var r = graph._wrapper.rect(g, | |
1149 dims[graph.X] + xScale * (0 - graph.yAxis._scale.min), | |
1150 dims[graph.Y] + yScale * (barGap + i * (numSer * barWidth + barGap) + (cur * barWidth)), | |
1151 xScale * series._values[i], yScale * barWidth); | |
1152 graph._showStatus(r, series._name, series._values[i]); | |
1153 } | |
1154 }, | |
1155 | |
1156 /* Draw the axes for this graph. */ | |
1157 _drawAxes: function(graph, numSer, numVal, barWidth, barGap, dims, yScale) { | |
1158 // X-axis | |
1159 var axis = graph.yAxis; | |
1160 if (axis) { | |
1161 if (axis._title) { | |
1162 graph._wrapper.text(graph._chartCont, dims[graph.X] + dims[graph.W] / 2, | |
1163 dims[graph.Y] + dims[graph.H] + axis._titleOffset, axis._title, axis._titleFormat); | |
1164 } | |
1165 graph._drawAxis(axis, 'xAxis', dims[graph.X], dims[graph.Y] + dims[graph.H], | |
1166 dims[graph.X] + dims[graph.W], dims[graph.Y] + dims[graph.H]); | |
1167 } | |
1168 // Y-axis | |
1169 var axis = graph.xAxis; | |
1170 if (axis._title) { | |
1171 graph._wrapper.text(graph._chartCont, 0, 0, axis._title, $.extend({textAnchor: 'middle', | |
1172 transform: 'translate(' + (dims[graph.X] - axis._titleOffset) + ',' + | |
1173 (dims[graph.Y] + dims[graph.H] / 2) + ') rotate(-90)'}, axis._titleFormat || {})); | |
1174 } | |
1175 var gl = graph._wrapper.group(graph._chartCont, $.extend({class_: 'yAxis'}, axis._lineFormat)); | |
1176 var gt = graph._wrapper.group(graph._chartCont, $.extend( | |
1177 {class_: 'yAxisLabels', textAnchor: 'end'}, axis._labelFormat)); | |
1178 graph._wrapper.line(gl, dims[graph.X], dims[graph.Y], dims[graph.X], dims[graph.Y] + dims[graph.H]); | |
1179 if (axis._ticks.major) { | |
1180 var offsets = graph._getTickOffsets(axis, false); | |
1181 for (var i = 1; i < numVal; i++) { | |
1182 var y = dims[graph.Y] + yScale * (barGap / 2 + i * (numSer * barWidth + barGap)); | |
1183 graph._wrapper.line(gl, dims[graph.X] + offsets[0] * axis._ticks.size, y, | |
1184 dims[graph.X] + offsets[1] * axis._ticks.size, y); | |
1185 } | |
1186 for (var i = 0; i < numVal; i++) { | |
1187 var y = dims[graph.Y] + yScale * (barGap / 2 + (i + 0.5) * (numSer * barWidth + barGap)); | |
1188 graph._wrapper.text(gt, dims[graph.X] - axis._ticks.size, y, | |
1189 (axis._labels ? axis._labels[i] : '' + i)); | |
1190 } | |
1191 } | |
1192 } | |
1193 }); | |
1194 | |
1195 //------------------------------------------------------------------------------ | |
1196 | |
1197 /* Draw a stacked row bar chart. */ | |
1198 function SVGStackedRowChart() { | |
1199 } | |
1200 | |
1201 $.extend(SVGStackedRowChart.prototype, { | |
1202 | |
1203 /* Retrieve the display title for this chart type. | |
1204 @return the title */ | |
1205 title: function() { | |
1206 return 'Stacked row chart'; | |
1207 }, | |
1208 | |
1209 /* Retrieve a description of this chart type. | |
1210 @return its description */ | |
1211 description: function() { | |
1212 return 'Compare sets of values as horizontal bars showing ' + | |
1213 'relative contributions to the whole for each category.'; | |
1214 }, | |
1215 | |
1216 /* Retrieve a list of the options that may be set for this chart type. | |
1217 @return options list */ | |
1218 options: function() { | |
1219 return barOptions; | |
1220 }, | |
1221 | |
1222 /* Actually draw the graph in this type's style. | |
1223 @param graph (object) the SVGGraph object */ | |
1224 drawGraph: function(graph) { | |
1225 var bg = graph._drawChartBackground(true, true); | |
1226 var dims = graph._getDims(); | |
1227 if (graph._gridlines[0] && graph.xAxis._ticks.major) { | |
1228 graph._drawGridlines(bg, graph._getPercentageAxis(), false, dims, graph._gridlines[0]); | |
1229 } | |
1230 var barWidth = graph._chartOptions.barWidth || 10; | |
1231 var barGap = graph._chartOptions.barGap || 10; | |
1232 var numSer = graph._series.length; | |
1233 var numVal = (numSer ? (graph._series[0])._values.length : 0); | |
1234 var xScale = dims[graph.W]; | |
1235 var yScale = dims[graph.H] / ((barWidth + barGap) * numVal + barGap); | |
1236 this._chart = graph._wrapper.group(graph._chartCont, {class_: 'chart'}); | |
1237 this._drawRows(graph, numSer, numVal, barWidth, barGap, dims, xScale, yScale); | |
1238 graph._drawTitle(); | |
1239 graph._wrapper.text(graph._chartCont, dims[graph.X] + dims[graph.W] / 2, | |
1240 dims[graph.Y] + dims[graph.H] + graph.xAxis._titleOffset, | |
1241 $.svg.graphing.region.percentageText, | |
1242 $.extend({textAnchor: 'middle'}, graph.yAxis._titleFormat || {})); | |
1243 var pAxis = $.extend({}, graph._getPercentageAxis()); | |
1244 $.extend(pAxis._labelFormat, graph.yAxis._labelFormat || {}); | |
1245 graph._drawAxis(pAxis, 'xAxis', dims[graph.X], dims[graph.Y] + dims[graph.H], | |
1246 dims[graph.X] + dims[graph.W], dims[graph.Y] + dims[graph.H]); | |
1247 this._drawYAxis(graph, numVal, barWidth, barGap, dims, yScale); | |
1248 graph._drawLegend(); | |
1249 }, | |
1250 | |
1251 /* Plot all of the rows. */ | |
1252 _drawRows: function(graph, numSer, numVal, barWidth, barGap, dims, xScale, yScale) { | |
1253 var totals = graph._getTotals(); | |
1254 var accum = []; | |
1255 for (var i = 0; i < numVal; i++) { | |
1256 accum[i] = 0; | |
1257 } | |
1258 for (var s = 0; s < numSer; s++) { | |
1259 var series = graph._series[s]; | |
1260 var g = graph._wrapper.group(this._chart, | |
1261 $.extend({class_: 'series' + s, fill: series._fill, | |
1262 stroke: series._stroke, strokeWidth: series._strokeWidth}, | |
1263 series._settings || {})); | |
1264 for (var i = 0; i < series._values.length; i++) { | |
1265 var r = graph._wrapper.rect(g, | |
1266 dims[graph.X] + xScale * accum[i] / totals[i], | |
1267 dims[graph.Y] + yScale * (barGap + i * (barWidth + barGap)), | |
1268 xScale * series._values[i] / totals[i], yScale * barWidth); | |
1269 graph._showStatus(r, series._name, | |
1270 roundNumber(series._values[i] / totals[i] * 100, 2)); | |
1271 accum[i] += series._values[i]; | |
1272 } | |
1273 } | |
1274 }, | |
1275 | |
1276 /* Draw the y-axis and its ticks. */ | |
1277 _drawYAxis: function(graph, numVal, barWidth, barGap, dims, yScale) { | |
1278 var axis = graph.xAxis; | |
1279 if (axis._title) { | |
1280 graph._wrapper.text(graph._chartCont, 0, 0, axis._title, $.extend({textAnchor: 'middle', | |
1281 transform: 'translate(' + (dims[graph.X] - axis._titleOffset) + ',' + | |
1282 (dims[graph.Y] + dims[graph.H] / 2) + ') rotate(-90)'}, axis._titleFormat || {})); | |
1283 } | |
1284 var gl = graph._wrapper.group(graph._chartCont, | |
1285 $.extend({class_: 'yAxis'}, axis._lineFormat)); | |
1286 var gt = graph._wrapper.group(graph._chartCont, | |
1287 $.extend({class_: 'yAxisLabels', textAnchor: 'end'}, axis._labelFormat)); | |
1288 graph._wrapper.line(gl, dims[graph.X], dims[graph.Y], | |
1289 dims[graph.X], dims[graph.Y] + dims[graph.H]); | |
1290 if (axis._ticks.major) { | |
1291 var offsets = graph._getTickOffsets(axis, false); | |
1292 for (var i = 1; i < numVal; i++) { | |
1293 var y = dims[graph.Y] + yScale * (barGap / 2 + i * (barWidth + barGap)); | |
1294 graph._wrapper.line(gl, dims[graph.X] + offsets[0] * axis._ticks.size, y, | |
1295 dims[graph.X] + offsets[1] * axis._ticks.size, y); | |
1296 } | |
1297 for (var i = 0; i < numVal; i++) { | |
1298 var y = dims[graph.Y] + yScale * (barGap / 2 + (i + 0.5) * (barWidth + barGap)); | |
1299 graph._wrapper.text(gt, dims[graph.X] - axis._ticks.size, y, | |
1300 (axis._labels ? axis._labels[i] : '' + i)); | |
1301 } | |
1302 } | |
1303 } | |
1304 }); | |
1305 | |
1306 //------------------------------------------------------------------------------ | |
1307 | |
1308 /* Draw a standard line chart. */ | |
1309 function SVGLineChart() { | |
1310 } | |
1311 | |
1312 $.extend(SVGLineChart.prototype, { | |
1313 | |
1314 /* Retrieve the display title for this chart type. | |
1315 @return the title */ | |
1316 title: function() { | |
1317 return 'Basic line chart'; | |
1318 }, | |
1319 | |
1320 /* Retrieve a description of this chart type. | |
1321 @return its description */ | |
1322 description: function() { | |
1323 return 'Compare sets of values as continuous lines.'; | |
1324 }, | |
1325 | |
1326 /* Retrieve a list of the options that may be set for this chart type. | |
1327 @return options list */ | |
1328 options: function() { | |
1329 return []; | |
1330 }, | |
1331 | |
1332 /* Actually draw the graph in this type's style. | |
1333 @param graph (object) the SVGGraph object */ | |
1334 drawGraph: function(graph) { | |
1335 graph._drawChartBackground(); | |
1336 var dims = graph._getDims(); | |
1337 var xScale = dims[graph.W] / (graph.xAxis._scale.max - graph.xAxis._scale.min); | |
1338 var yScale = dims[graph.H] / (graph.yAxis._scale.max - graph.yAxis._scale.min); | |
1339 this._chart = graph._wrapper.group(graph._chartCont, {class_: 'chart'}); | |
1340 for (var i = 0; i < graph._series.length; i++) { | |
1341 this._drawSeries(graph, i, dims, xScale, yScale); | |
1342 } | |
1343 graph._drawTitle(); | |
1344 graph._drawAxes(); | |
1345 graph._drawLegend(); | |
1346 }, | |
1347 | |
1348 /* Plot an individual series. */ | |
1349 _drawSeries: function(graph, cur, dims, xScale, yScale) { | |
1350 var series = graph._series[cur]; | |
1351 var path = graph._wrapper.createPath(); | |
1352 for (var i = 0; i < series._values.length; i++) { | |
1353 var x = dims[graph.X] + i * xScale; | |
1354 var y = dims[graph.Y] + (graph.yAxis._scale.max - series._values[i]) * yScale; | |
1355 if (i == 0) { | |
1356 path.move(x, y); | |
1357 } | |
1358 else { | |
1359 path.line(x, y); | |
1360 } | |
1361 } | |
1362 var p = graph._wrapper.path(this._chart, path, | |
1363 $.extend({id: 'series' + cur, fill: 'none', stroke: series._stroke, | |
1364 strokeWidth: series._strokeWidth}, series._settings || {})); | |
1365 graph._showStatus(p, series._name, 0); | |
1366 } | |
1367 }); | |
1368 | |
1369 //------------------------------------------------------------------------------ | |
1370 | |
1371 /* Draw a standard pie chart. */ | |
1372 function SVGPieChart() { | |
1373 } | |
1374 | |
1375 $.extend(SVGPieChart.prototype, { | |
1376 | |
1377 _options: ['explode (number or number[]) - indexes of sections to explode out of the pie', | |
1378 'explodeDist (number) - the distance to move an exploded section', | |
1379 'pieGap (number) - the distance between pies for multiple values'], | |
1380 | |
1381 /* Retrieve the display title for this chart type. | |
1382 @return the title */ | |
1383 title: function() { | |
1384 return 'Pie chart'; | |
1385 }, | |
1386 | |
1387 /* Retrieve a description of this chart type. | |
1388 @return its description */ | |
1389 description: function() { | |
1390 return 'Compare relative sizes of values as contributions to the whole.'; | |
1391 }, | |
1392 | |
1393 /* Retrieve a list of the options that may be set for this chart type. | |
1394 @return options list */ | |
1395 options: function() { | |
1396 return this._options; | |
1397 }, | |
1398 | |
1399 /* Actually draw the graph in this type's style. | |
1400 @param graph (object) the SVGGraph object */ | |
1401 drawGraph: function(graph) { | |
1402 graph._drawChartBackground(true, true); | |
1403 this._chart = graph._wrapper.group(graph._chartCont, {class_: 'chart'}); | |
1404 var dims = graph._getDims(); | |
1405 this._drawSeries(graph, dims); | |
1406 graph._drawTitle(); | |
1407 graph._drawLegend(); | |
1408 }, | |
1409 | |
1410 /* Plot all the series. */ | |
1411 _drawSeries: function(graph, dims) { | |
1412 var totals = graph._getTotals(); | |
1413 var numSer = graph._series.length; | |
1414 var numVal = (numSer ? (graph._series[0])._values.length : 0); | |
1415 var path = graph._wrapper.createPath(); | |
1416 var explode = graph._chartOptions.explode || []; | |
1417 explode = (isArray(explode) ? explode : [explode]); | |
1418 var explodeDist = graph._chartOptions.explodeDist || 10; | |
1419 var pieGap = (numVal <= 1 ? 0 : graph._chartOptions.pieGap || 10); | |
1420 var xBase = (dims[graph.W] - (numVal * pieGap) - pieGap) / numVal / 2; | |
1421 var yBase = dims[graph.H] / 2; | |
1422 var radius = Math.min(xBase, yBase) - (explode.length > 0 ? explodeDist : 0); | |
1423 var gt = graph._wrapper.group(graph._chartCont, $.extend( | |
1424 {class_: 'xAxisLabels', textAnchor: 'middle'}, graph.xAxis._labelFormat)); | |
1425 var gl = []; | |
1426 for (var i = 0; i < numVal; i++) { | |
1427 var cx = dims[graph.X] + xBase + (i * (2 * Math.min(xBase, yBase) + pieGap)) + pieGap; | |
1428 var cy = dims[graph.Y] + yBase; | |
1429 var curTotal = 0; | |
1430 for (var j = 0; j < numSer; j++) { | |
1431 var series = graph._series[j]; | |
1432 if (i == 0) { | |
1433 gl[j] = graph._wrapper.group(this._chart, $.extend({class_: 'series' + j, | |
1434 fill: series._fill, stroke: series._stroke, | |
1435 strokeWidth: series._strokeWidth}, series._settings || {})); | |
1436 } | |
1437 if (series._values[i] == 0) { | |
1438 continue; | |
1439 } | |
1440 var start = (curTotal / totals[i]) * 2 * Math.PI; | |
1441 curTotal += series._values[i]; | |
1442 var end = (curTotal / totals[i]) * 2 * Math.PI; | |
1443 var exploding = false; | |
1444 for (var k = 0; k < explode.length; k++) { | |
1445 if (explode[k] == j) { | |
1446 exploding = true; | |
1447 break; | |
1448 } | |
1449 } | |
1450 var x = cx + (exploding ? explodeDist * Math.cos((start + end) / 2) : 0); | |
1451 var y = cy + (exploding ? explodeDist * Math.sin((start + end) / 2) : 0); | |
1452 var p = graph._wrapper.path(gl[j], path.reset().move(x, y). | |
1453 line(x + radius * Math.cos(start), y + radius * Math.sin(start)). | |
1454 arc(radius, radius, 0, (end - start < Math.PI ? 0 : 1), 1, | |
1455 x + radius * Math.cos(end), y + radius * Math.sin(end)).close()); | |
1456 graph._showStatus(p, series._name, | |
1457 roundNumber((end - start) / 2 / Math.PI * 100, 2)); | |
1458 } | |
1459 if (graph.xAxis) { | |
1460 graph._wrapper.text(gt, cx, dims[graph.Y] + dims[graph.H] + graph.xAxis._titleOffset, | |
1461 graph.xAxis._labels[i]) | |
1462 } | |
1463 } | |
1464 } | |
1465 }); | |
1466 | |
1467 //------------------------------------------------------------------------------ | |
1468 | |
1469 /* Determine whether an object is an array. */ | |
1470 function isArray(a) { | |
1471 return (a && a.constructor == Array); | |
1472 } | |
1473 | |
1474 // Basic chart types | |
1475 $.svg.graphing.addChartType('column', new SVGColumnChart()); | |
1476 $.svg.graphing.addChartType('stackedColumn', new SVGStackedColumnChart()); | |
1477 $.svg.graphing.addChartType('row', new SVGRowChart()); | |
1478 $.svg.graphing.addChartType('stackedRow', new SVGStackedRowChart()); | |
1479 $.svg.graphing.addChartType('line', new SVGLineChart()); | |
1480 $.svg.graphing.addChartType('pie', new SVGPieChart()); | |
1481 | |
1482 })(jQuery) |