756
|
1 /* http://keith-wood.name/svg.html
|
1151
|
2 SVG graphing extension for jQuery v1.4.5.
|
756
|
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)
|