jquerysvg 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +52 -0
- data/Rakefile +1 -0
- data/jquerysvg.gemspec +24 -0
- data/lib/jquerysvg.rb +6 -0
- data/lib/jquerysvg/engine.rb +9 -0
- data/lib/jquerysvg/version.rb +3 -0
- data/vendor/assets/javascripts/jquery.svg.js +1394 -0
- data/vendor/assets/javascripts/jquery.svganim.js +473 -0
- data/vendor/assets/javascripts/jquery.svgdom.js +406 -0
- data/vendor/assets/javascripts/jquery.svgfilter.js +402 -0
- data/vendor/assets/javascripts/jquery.svggraph.js +1482 -0
- data/vendor/assets/javascripts/jquery.svgplot.js +817 -0
- data/vendor/assets/javascripts/jquerysvg.js +6 -0
- data/vendor/assets/stylesheets/jquerysvg.scss +16 -0
- metadata +109 -0
|
@@ -0,0 +1,1482 @@
|
|
|
1
|
+
/* http://keith-wood.name/svg.html
|
|
2
|
+
SVG graphing extension for jQuery v1.4.5.
|
|
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)
|