comparison webapp/src/main/webapp/jquery/svg/jquery.svgplot.js @ 903:7779b37d1d05

refactored into maven modules per servlet type. can build servlet-api 2.3 and 3.0 via profile now!
author robcast
date Tue, 26 Apr 2011 20:24:31 +0200
parents client/src/main/webapp/jquery/svg/jquery.svgplot.js@ba1eb2d821a2
children 301ef9bf1965
comparison
equal deleted inserted replaced
902:89ba3ffcf552 903:7779b37d1d05
1 /* http://keith-wood.name/svg.html
2 SVG plotting extension for jQuery v1.4.3.
3 Written by Keith Wood (kbwood{at}iinet.com.au) December 2008.
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('plot', SVGPlot);
11
12 /* Extension point for SVG plotting.
13 Access through svg.plot. */
14 function SVGPlot(wrapper) {
15 this._wrapper = wrapper; // The attached SVG wrapper object
16 this._drawNow = false; // True for immediate update, false to wait for redraw call
17 // The plot title and settings
18 this._title = {value: '', offset: 25, settings: {textAnchor: 'middle'}};
19 this._area = [0.1, 0.1, 0.8, 0.9]; // The chart area: left, top, right, bottom,
20 // > 1 in pixels, <= 1 as proportion
21 this._areaFormat = {fill: 'none', stroke: 'black'}; // The formatting for the plot area
22 this._gridlines = []; // The formatting of the x- and y-gridlines
23 this._equalXY = true; // True for equal-sized x- and y-units, false to fill available space
24 this._functions = []; // The functions to be plotted, each is an object
25 this._onstatus = null; // The callback function for status updates
26 this._uuid = new Date().getTime();
27 this._plotCont = this._wrapper.svg(0, 0, 0, 0, {class_: 'svg-plot'}); // The main container for the plot
28
29 this.xAxis = new SVGPlotAxis(this); // The main x-axis
30 this.xAxis.title('X', 20);
31 this.yAxis = new SVGPlotAxis(this); // The main y-axis
32 this.yAxis.title('Y', 20);
33 this.legend = new SVGPlotLegend(this); // The plot legend
34 this._drawNow = true;
35 }
36
37 $.extend(SVGPlot.prototype, {
38
39 /* Useful indexes. */
40 X: 0,
41 Y: 1,
42 W: 2,
43 H: 3,
44 L: 0,
45 T: 1,
46 R: 2,
47 B: 3,
48
49 /* Set or retrieve the container for the plot.
50 @param cont (SVG element) the container for the plot
51 @return (SVGPlot) this plot object or
52 (SVG element) the current container (if no parameters) */
53 container: function(cont) {
54 if (arguments.length == 0) {
55 return this._plotCont;
56 }
57 this._plotCont = cont;
58 return this;
59 },
60
61 /* Set or retrieve the main plotting area.
62 @param left (number) > 1 is pixels, <= 1 is proportion of width or
63 (number[4]) for left, top, right, bottom
64 @param top (number) > 1 is pixels, <= 1 is proportion of height
65 @param right (number) > 1 is pixels, <= 1 is proportion of width
66 @param bottom (number) > 1 is pixels, <= 1 is proportion of height
67 @return (SVGPlot) this plot object or
68 (number[4]) the plotting area: left, top, right, bottom (if no parameters) */
69 area: function(left, top, right, bottom) {
70 if (arguments.length == 0) {
71 return this._area;
72 }
73 this._area = (isArray(left) ? left : [left, top, right, bottom]);
74 this._drawPlot();
75 return this;
76 },
77
78 /* Set or retrieve the background of the plot area.
79 @param fill (string) how to fill the area background
80 @param stroke (string) the colour of the outline (optional)
81 @param settings (object) additional formatting for the area background (optional)
82 @return (SVGPlot) this plot object or
83 (object) the area format (if no parameters) */
84 format: function(fill, stroke, settings) {
85 if (arguments.length == 0) {
86 return this._areaFormat;
87 }
88 if (typeof stroke == 'object') {
89 settings = stroke;
90 stroke = null;
91 }
92 this._areaFormat = $.extend({fill: fill},
93 (stroke ? {stroke: stroke} : {}), settings || {});
94 this._drawPlot();
95 return this;
96 },
97
98 /* Set or retrieve the gridlines formatting for the plot area.
99 @param xSettings (string) the colour of the gridlines along the x-axis, or
100 (object) formatting for the gridlines along the x-axis, or
101 null for none
102 @param ySettings (string) the colour of the gridlines along the y-axis, or
103 (object) formatting for the gridlines along the y-axis, or
104 null for none
105 @return (SVGPlot) this plot object or
106 (object[2]) the gridlines formatting (if no parameters) */
107 gridlines: function(xSettings, ySettings) {
108 if (arguments.length == 0) {
109 return this._gridlines;
110 }
111 this._gridlines = [(typeof xSettings == 'string' ? {stroke: xSettings} : xSettings),
112 (typeof ySettings == 'string' ? {stroke: ySettings} : ySettings)];
113 if (this._gridlines[0] == null && this._gridlines[1] == null) {
114 this._gridlines = [];
115 }
116 this._drawPlot();
117 return this;
118 },
119
120 /* Set or retrieve the equality of the x- and y-axes.
121 @param value (boolean) true for equal x- and y-units, false to fill the available space
122 @return (SVGPlot) this plot object or
123 (boolean) the current setting (if no parameters) */
124 equalXY: function(value) {
125 if (arguments.length == 0) {
126 return this._equalXY;
127 }
128 this._equalXY = value;
129 return this;
130 },
131
132 /* Set or retrieve the title of the plot and its formatting.
133 @param value (string) the title
134 @param offset (number) the vertical positioning of the title
135 > 1 is pixels, <= 1 is proportion of width (optional)
136 @param colour (string) the colour of the title (optional)
137 @param settings (object) formatting for the title (optional)
138 @return (SVGPlot) this plot object or
139 (object) value, offset, and settings for the title (if no parameters) */
140 title: function(value, offset, colour, settings) {
141 if (arguments.length == 0) {
142 return this._title;
143 }
144 if (typeof offset != 'number') {
145 settings = colour;
146 colour = offset;
147 offset = null;
148 }
149 if (typeof colour != 'string') {
150 settings = colour;
151 colour = null;
152 }
153 this._title = {value: value, offset: offset || this._title.offset,
154 settings: $.extend({textAnchor: 'middle'},
155 (colour ? {fill: colour} : {}), settings || {})};
156 this._drawPlot();
157 return this;
158 },
159
160 /* Add a function to be plotted on the plot.
161 @param name (string) the name of this series (optional)
162 @param fn (function) the function to be plotted
163 @param range (number[2]) the range of values to plot (optional)
164 @param points (number) the number of points to plot within this range (optional)
165 @param stroke (string) the colour of the plotted lines (optional)
166 @param strokeWidth (number) the width of the plotted lines (optional)
167 @param settings (object) additional settings for the plotted values (optional)
168 @return (SVGPlot) this plot object */
169 addFunction: function(name, fn, range, points, stroke, strokeWidth, settings) {
170 this._functions.push(new SVGPlotFunction(
171 this, name, fn, range, points, stroke, strokeWidth, settings));
172 this._drawPlot();
173 return this;
174 },
175
176 /* Retrieve the function wrappers.
177 @param i (number) the function index (optional)
178 @return (SVGPlotFunction) the specified function or
179 (SVGPlotFunction[]) the list of functions */
180 functions: function(i) {
181 return (arguments.length > 0 ? this._functions[i] : null) || this._functions;
182 },
183
184 /* Suppress drawing of the plot until redraw() is called.
185 @return (SVGPlot) this plot object */
186 noDraw: function() {
187 this._drawNow = false;
188 return this;
189 },
190
191 /* Redraw the entire plot with the current settings and values.
192 @return (SVGPlot) this plot object */
193 redraw: function() {
194 this._drawNow = true;
195 this._drawPlot();
196 return this;
197 },
198
199 /* Set the callback function for status updates.
200 @param onstatus (function) the callback function
201 @return (SVGPlot) this plot object */
202 status: function(onstatus) {
203 this._onstatus = onstatus;
204 return this;
205 },
206
207 /* Actually draw the plot (if allowed). */
208 _drawPlot: function() {
209 if (!this._drawNow) {
210 return;
211 }
212 while (this._plotCont.firstChild) {
213 this._plotCont.removeChild(this._plotCont.firstChild);
214 }
215 if (!this._plotCont.parent) {
216 this._wrapper._svg.appendChild(this._plotCont);
217 }
218 // Set sizes if not already there
219 if (!this._plotCont.width) {
220 this._plotCont.setAttribute('width',
221 parseInt(this._plotCont.getAttribute('width'), 10) || this._wrapper._width());
222 }
223 else if (this._plotCont.width.baseVal) {
224 this._plotCont.width.baseVal.value =
225 this._plotCont.width.baseVal.value || this._wrapper._width();
226 }
227 else {
228 this._plotCont.width = this._plotCont.width || this._wrapper._width();
229 }
230 if (!this._plotCont.height) {
231 this._plotCont.setAttribute('height',
232 parseInt(this._plotCont.getAttribute('height'), 10) || this._wrapper._height());
233 }
234 else if (this._plotCont.height.baseVal) {
235 this._plotCont.height.baseVal.value =
236 this._plotCont.height.baseVal.value || this._wrapper._height();
237 }
238 else {
239 this._plotCont.height = this._plotCont.height || this._wrapper._height();
240 }
241 this._drawChartBackground();
242 var dims = this._getDims();
243 var clip = this._wrapper.other(this._plotCont, 'clipPath', {id: 'clip' + this._uuid});
244 this._wrapper.rect(clip, dims[this.X], dims[this.Y], dims[this.W], dims[this.H]);
245 this._plot = this._wrapper.group(this._plotCont,
246 {class_: 'foreground', clipPath: 'url(#clip' + this._uuid + ')'});
247 this._drawAxis(true);
248 this._drawAxis(false);
249 for (var i = 0; i < this._functions.length; i++) {
250 this._plotFunction(this._functions[i], i);
251 }
252 this._drawTitle();
253 this._drawLegend();
254 },
255
256 /* Decode an attribute value.
257 @param node the node to examine
258 @param name the attribute name
259 @return the actual value */
260 _getValue: function(node, name) {
261 return (!node[name] ? parseInt(node.getAttribute(name), 10) :
262 (node[name].baseVal ? node[name].baseVal.value : node[name]));
263 },
264
265 /* Calculate the actual dimensions of the plot area.
266 @param area (number[4]) the area values to evaluate (optional)
267 @return (number[4]) an array of dimension values: left, top, width, height */
268 _getDims: function(area) {
269 var otherArea = (area != null);
270 area = area || this._area;
271 var availWidth = this._getValue(this._plotCont, 'width');
272 var availHeight = this._getValue(this._plotCont, 'height');
273 var left = (area[this.L] > 1 ? area[this.L] : availWidth * area[this.L]);
274 var top = (area[this.T] > 1 ? area[this.T] : availHeight * area[this.T]);
275 var width = (area[this.R] > 1 ? area[this.R] : availWidth * area[this.R]) - left;
276 var height = (area[this.B] > 1 ? area[this.B] : availHeight * area[this.B]) - top;
277 if (this._equalXY && !otherArea) {
278 var scale = Math.min(width / (this.xAxis._scale.max - this.xAxis._scale.min),
279 height / (this.yAxis._scale.max - this.yAxis._scale.min));
280 width = scale * (this.xAxis._scale.max - this.xAxis._scale.min);
281 height = scale * (this.yAxis._scale.max - this.yAxis._scale.min);
282 }
283 return [left, top, width, height];
284 },
285
286 /* Calculate the scaling factors for the plot area.
287 @return (number[2]) the x- and y-scaling factors */
288 _getScales: function() {
289 var dims = this._getDims();
290 return [dims[this.W] / (this.xAxis._scale.max - this.xAxis._scale.min),
291 dims[this.H] / (this.yAxis._scale.max - this.yAxis._scale.min)];
292 },
293
294 /* Draw the chart background, including gridlines.
295 @param noXGrid (boolean) true to suppress the x-gridlines, false to draw them (optional)
296 @param noYGrid (boolean) true to suppress the y-gridlines, false to draw them (optional)
297 @return (element) the background group element */
298 _drawChartBackground: function(noXGrid, noYGrid) {
299 var bg = this._wrapper.group(this._plotCont, {class_: 'background'});
300 var dims = this._getDims();
301 this._wrapper.rect(bg, dims[this.X], dims[this.Y], dims[this.W], dims[this.H], this._areaFormat);
302 if (this._gridlines[0] && this.yAxis._ticks.major && !noYGrid) {
303 this._drawGridlines(bg, true, this._gridlines[0], dims);
304 }
305 if (this._gridlines[1] && this.xAxis._ticks.major && !noXGrid) {
306 this._drawGridlines(bg, false, this._gridlines[1], dims);
307 }
308 return bg;
309 },
310
311 /* Draw one set of gridlines.
312 @param bg (element) the background group element
313 @param horiz (boolean) true if horizontal, false if vertical
314 @param format (object) additional settings for the gridlines */
315 _drawGridlines: function(bg, horiz, format, dims) {
316 var g = this._wrapper.group(bg, format);
317 var axis = (horiz ? this.yAxis : this.xAxis);
318 var scales = this._getScales();
319 var major = Math.floor(axis._scale.min / axis._ticks.major) * axis._ticks.major;
320 major += (major <= axis._scale.min ? axis._ticks.major : 0);
321 while (major < axis._scale.max) {
322 var v = (horiz ? axis._scale.max - major : major - axis._scale.min) *
323 scales[horiz ? 1 : 0] + (horiz ? dims[this.Y] : dims[this.X]);
324 this._wrapper.line(g, (horiz ? dims[this.X] : v), (horiz ? v : dims[this.Y]),
325 (horiz ? dims[this.X] + dims[this.W] : v), (horiz ? v : dims[this.Y] + dims[this.H]));
326 major += axis._ticks.major;
327 }
328 },
329
330 /* Draw an axis, its tick marks, and title.
331 @param horiz (boolean) true for x-axis, false for y-axis */
332 _drawAxis: function(horiz) {
333 var id = (horiz ? 'x' : 'y') + 'Axis';
334 var axis = (horiz ? this.xAxis : this.yAxis);
335 var axis2 = (horiz ? this.yAxis : this.xAxis);
336 var dims = this._getDims();
337 var scales = this._getScales();
338 var gl = this._wrapper.group(this._plot, $.extend({class_: id}, axis._lineFormat));
339 var gt = this._wrapper.group(this._plot, $.extend({class_: id + 'Labels',
340 textAnchor: (horiz ? 'middle' : 'end')}, axis._labelFormat));
341 var zero = (horiz ? axis2._scale.max : -axis2._scale.min) *
342 scales[horiz ? 1 : 0] + (horiz ? dims[this.Y] : dims[this.X]);
343 this._wrapper.line(gl, (horiz ? dims[this.X] : zero), (horiz ? zero : dims[this.Y]),
344 (horiz ? dims[this.X] + dims[this.W] : zero),
345 (horiz ? zero : dims[this.Y] + dims[this.H]));
346 if (axis._ticks.major) {
347 var size = axis._ticks.size;
348 var major = Math.floor(axis._scale.min / axis._ticks.major) * axis._ticks.major;
349 major = (major < axis._scale.min ? major + axis._ticks.major : major);
350 var minor = (!axis._ticks.minor ? axis._scale.max + 1 :
351 Math.floor(axis._scale.min / axis._ticks.minor) * axis._ticks.minor);
352 minor = (minor < axis._scale.min ? minor + axis._ticks.minor : minor);
353 var offsets = [(axis._ticks.position == 'nw' || axis._ticks.position == 'both' ? -1 : 0),
354 (axis._ticks.position == 'se' || axis._ticks.position == 'both' ? +1 : 0)];
355 while (major <= axis._scale.max || minor <= axis._scale.max) {
356 var cur = Math.min(major, minor);
357 var len = (cur == major ? size : size / 2);
358 var xy = (horiz ? cur - axis._scale.min : axis._scale.max - cur) *
359 scales[horiz ? 0 : 1] + (horiz ? dims[this.X] : dims[this.Y]);
360 this._wrapper.line(gl, (horiz ? xy : zero + len * offsets[0]),
361 (horiz ? zero + len * offsets[0] : xy),
362 (horiz ? xy : zero + len * offsets[1]),
363 (horiz ? zero + len * offsets[1] : xy));
364 if (cur == major && cur != 0) {
365 this._wrapper.text(gt, (horiz ? xy : zero - size),
366 (horiz ? zero - size : xy), '' + cur);
367 }
368 major += (cur == major ? axis._ticks.major : 0);
369 minor += (cur == minor ? axis._ticks.minor : 0);
370 }
371 }
372 if (axis._title) {
373 if (horiz) {
374 this._wrapper.text(this._plotCont, dims[this.X] - axis._titleOffset,
375 zero, axis._title, $.extend({textAnchor: 'end'}, axis._titleFormat || {}));
376 }
377 else {
378 this._wrapper.text(this._plotCont, zero,
379 dims[this.Y] + dims[this.H] + axis._titleOffset,
380 axis._title, $.extend({textAnchor : 'middle'}, axis._titleFormat || {}));
381 }
382 }
383 },
384
385 /* Plot an individual function. */
386 _plotFunction: function(fn, cur) {
387 var dims = this._getDims();
388 var scales = this._getScales();
389 var path = this._wrapper.createPath();
390 var range = fn._range || [this.xAxis._scale.min, this.xAxis._scale.max];
391 var xScale = (range[1] - range[0]) / fn._points;
392 var first = true;
393 for (var i = 0; i <= fn._points; i++) {
394 var x = range[0] + i * xScale;
395 if (x > this.xAxis._scale.max + xScale) {
396 break;
397 }
398 if (x < this.xAxis._scale.min - xScale) {
399 continue;
400 }
401 var px = (x - this.xAxis._scale.min) * scales[0] + dims[this.X];
402 var py = dims[this.H] - ((fn._fn(x) - this.yAxis._scale.min) * scales[1]) + dims[this.Y];
403 path[(first ? 'move' : 'line') + 'To'](px, py);
404 first = false;
405 }
406 var p = this._wrapper.path(this._plot, path,
407 $.extend({class_: 'fn' + cur, fill: 'none', stroke: fn._stroke,
408 strokeWidth: fn._strokeWidth}, fn._settings || {}));
409 this._showStatus(p, fn._name);
410 },
411
412 /* Draw the plot title - centred. */
413 _drawTitle: function() {
414 this._wrapper.text(this._plotCont, this._getValue(this._plotCont, 'width') / 2,
415 this._title.offset, this._title.value, this._title.settings);
416 },
417
418 /* Draw the chart legend. */
419 _drawLegend: function() {
420 if (!this.legend._show) {
421 return;
422 }
423 var g = this._wrapper.group(this._plotCont, {class_: 'legend'});
424 var dims = this._getDims(this.legend._area);
425 this._wrapper.rect(g, dims[this.X], dims[this.Y], dims[this.W], dims[this.H],
426 this.legend._bgSettings);
427 var horiz = dims[this.W] > dims[this.H];
428 var numFn = this._functions.length;
429 var offset = (horiz ? dims[this.W] : dims[this.H]) / numFn;
430 var xBase = dims[this.X] + 5;
431 var yBase = dims[this.Y] + ((horiz ? dims[this.H] : offset) + this.legend._sampleSize) / 2;
432 for (var i = 0; i < numFn; i++) {
433 var fn = this._functions[i];
434 this._wrapper.rect(g, xBase + (horiz ? i * offset : 0),
435 yBase + (horiz ? 0 : i * offset) - this.legend._sampleSize,
436 this.legend._sampleSize, this.legend._sampleSize, {fill: fn._stroke});
437 this._wrapper.text(g, xBase + (horiz ? i * offset : 0) + this.legend._sampleSize + 5,
438 yBase + (horiz ? 0 : i * offset), fn._name, this.legend._textSettings);
439 }
440 },
441
442 /* Show the current value status on hover. */
443 _showStatus: function(elem, label) {
444 var status = this._onstatus;
445 if (this._onstatus) {
446 $(elem).hover(function(evt) { status.apply(this, [label]); },
447 function() { status.apply(this, ['']); });
448 }
449 }
450 });
451
452 /* Details about each plot function.
453 @param plot (SVGPlot) the owning plot
454 @param name (string) the name of this function (optional)
455 @param fn (function) the function to be plotted
456 @param range (number[2]) the range of values to be plotted (optional)
457 @param points (number) the number of points to plot within this range (optional)
458 @param stroke (string) the colour of the (out)line for the plot (optional)
459 @param strokeWidth (number) the width of the (out)line for the plot (optional)
460 @param settings (object) additional formatting settings (optional)
461 @return (SVGPlotFunction) the new plot function object */
462 function SVGPlotFunction(plot, name, fn, range, points, stroke, strokeWidth, settings) {
463 if (typeof name != 'string') {
464 settings = strokeWidth;
465 strokeWidth = stroke;
466 stroke = points;
467 points = range;
468 range = fn;
469 fn = name;
470 name = null;
471 }
472 if (!isArray(range)) {
473 settings = strokeWidth;
474 strokeWidth = stroke;
475 stroke = points;
476 points = range;
477 range = null;
478 }
479 if (typeof points != 'number') {
480 settings = strokeWidth;
481 strokeWidth = stroke;
482 stroke = points;
483 points = null;
484 }
485 if (typeof stroke != 'string') {
486 settings = strokeWidth;
487 strokeWidth = stroke;
488 stroke = null;
489 }
490 if (typeof strokeWidth != 'number') {
491 settings = strokeWidth;
492 strokeWidth = null;
493 }
494 this._plot = plot; // The owning plot
495 this._name = name || ''; // Display name
496 this._fn = fn || identity; // The actual function: y = fn(x)
497 this._range = range; // The range of values plotted
498 this._points = points || 100; // The number of points plotted
499 this._stroke = stroke || 'black'; // The line colour
500 this._strokeWidth = strokeWidth || 1; // The line width
501 this._settings = settings || {}; // Any other settings
502 }
503
504 $.extend(SVGPlotFunction.prototype, {
505
506 /* Set or retrieve the name for this function.
507 @param name (string) the function's name
508 @return (SVGPlotFunction) this plot function object or
509 (string) the function name (if no parameters) */
510 name: function(name) {
511 if (arguments.length == 0) {
512 return this._name;
513 }
514 this._name = name;
515 this._plot._drawPlot();
516 return this;
517 },
518
519 /* Set or retrieve the function to be plotted.
520 @param name (string) the function's name (optional)
521 @param fn (function) the function to be ploted
522 @return (SVGPlotFunction) this plot function object or
523 (function) the actual function (if no parameters) */
524 fn: function(name, fn) {
525 if (arguments.length == 0) {
526 return this._fn;
527 }
528 if (typeof name == 'function') {
529 fn = name;
530 name = null;
531 }
532 this._name = name || this._name;
533 this._fn = fn;
534 this._plot._drawPlot();
535 return this;
536 },
537
538 /* Set or retrieve the range of values to be plotted.
539 @param min (number) the minimum value to be plotted
540 @param max (number) the maximum value to be plotted
541 @return (SVGPlotFunction) this plot function object or
542 (number[2]) the value range (if no parameters) */
543 range: function(min, max) {
544 if (arguments.length == 0) {
545 return this._range;
546 }
547 this._range = (min == null ? null : [min, max]);
548 this._plot._drawPlot();
549 return this;
550 },
551
552 /* Set or retrieve the number of points to be plotted.
553 @param value (number) the number of points to plot
554 @return (SVGPlotFunction) this plot function object or
555 (number) the number of points (if no parameters) */
556 points: function(value) {
557 if (arguments.length == 0) {
558 return this._points;
559 }
560 this._points = value;
561 this._plot._drawPlot();
562 return this;
563 },
564
565 /* Set or retrieve the formatting for this function.
566 @param stroke (string) the (out)line colour
567 @param strokeWidth (number) the line's width (optional)
568 @param settings (object) additional formatting settings for the function (optional)
569 @return (SVGPlotFunction) this plot function object or
570 (object) formatting settings (if no parameters) */
571 format: function(stroke, strokeWidth, settings) {
572 if (arguments.length == 0) {
573 return $.extend({stroke: this._stroke,
574 strokeWidth: this._strokeWidth}, this._settings);
575 }
576 if (typeof strokeWidth != 'number') {
577 settings = strokeWidth;
578 strokeWidth = null;
579 }
580 this._stroke = stroke || this._stroke;
581 this._strokeWidth = strokeWidth || this._strokeWidth;
582 $.extend(this._settings, settings || {});
583 this._plot._drawPlot();
584 return this;
585 },
586
587 /* Return to the parent plot. */
588 end: function() {
589 return this._plot;
590 }
591 });
592
593 /* Default function to plot.
594 @param x (number) the input value
595 @return (number) the same value */
596 function identity(x) {
597 return x;
598 }
599
600 /* Details about each plot axis.
601 @param plot (SVGPlot) the owning plot
602 @param title (string) the title of the axis
603 @param min (number) the minimum value displayed on this axis
604 @param max (number) the maximum value displayed on this axis
605 @param major (number) the distance between major ticks
606 @param minor (number) the distance between minor ticks (optional)
607 @return (SVGPlotAxis) the new axis object */
608 function SVGPlotAxis(plot, title, min, max, major, minor) {
609 this._plot = plot; // The owning plot
610 this._title = title || ''; // The plot's title
611 this._titleFormat = {}; // Formatting settings for the title
612 this._titleOffset = 0; // The offset for positioning the title
613 this._labelFormat = {}; // Formatting settings for the labels
614 this._lineFormat = {stroke: 'black', strokeWidth: 1}; // Formatting settings for the axis lines
615 this._ticks = {major: major || 10, minor: minor || 0, size: 10, position: 'both'}; // Tick mark options
616 this._scale = {min: min || 0, max: max || 100}; // Axis scale settings
617 this._crossAt = 0; // Where this axis crosses the other one. */
618 }
619
620 $.extend(SVGPlotAxis.prototype, {
621
622 /* Set or retrieve the scale for this axis.
623 @param min (number) the minimum value shown
624 @param max (number) the maximum value shown
625 @return (SVGPlotAxis) this axis object or
626 (object) min and max values (if no parameters) */
627 scale: function(min, max) {
628 if (arguments.length == 0) {
629 return this._scale;
630 }
631 this._scale.min = min;
632 this._scale.max = max;
633 this._plot._drawPlot();
634 return this;
635 },
636
637 /* Set or retrieve the ticks for this axis.
638 @param major (number) the distance between major ticks
639 @param minor (number) the distance between minor ticks
640 @param size (number) the length of the major ticks (minor are half) (optional)
641 @param position (string) the location of the ticks:
642 'nw', 'se', 'both' (optional)
643 @return (SVGPlotAxis) this axis object or
644 (object) major, minor, size, and position values (if no parameters) */
645 ticks: function(major, minor, size, position) {
646 if (arguments.length == 0) {
647 return this._ticks;
648 }
649 if (typeof size == 'string') {
650 position = size;
651 size = null;
652 }
653 this._ticks.major = major;
654 this._ticks.minor = minor;
655 this._ticks.size = size || this._ticks.size;
656 this._ticks.position = position || this._ticks.position;
657 this._plot._drawPlot();
658 return this;
659 },
660
661 /* Set or retrieve the title for this axis.
662 @param title (string) the title text
663 @param offset (number) the distance to offset the title position (optional)
664 @param colour (string) how to colour the title (optional)
665 @param format (object) formatting settings for the title (optional)
666 @return (SVGPlotAxis) this axis object or
667 (object) title, offset, and format values (if no parameters) */
668 title: function(title, offset, colour, format) {
669 if (arguments.length == 0) {
670 return {title: this._title, offset: this._titleOffset, format: this._titleFormat};
671 }
672 if (typeof offset != 'number') {
673 format = colour;
674 colour = offset;
675 offset = null;
676 }
677 if (typeof colour != 'string') {
678 format = colour;
679 colour = null;
680 }
681 this._title = title;
682 this._titleOffset = (offset != null ? offset : this._titleOffset);
683 if (colour || format) {
684 this._titleFormat = $.extend(format || {}, (colour ? {fill: colour} : {}));
685 }
686 this._plot._drawPlot();
687 return this;
688 },
689
690 /* Set or retrieve the label format for this axis.
691 @param colour (string) how to colour the labels (optional)
692 @param format (object) formatting settings for the labels (optional)
693 @return (SVGPlotAxis) this axis object or
694 (object) format values (if no parameters) */
695 format: function(colour, format) {
696 if (arguments.length == 0) {
697 return this._labelFormat;
698 }
699 if (typeof colour != 'string') {
700 format = colour;
701 colour = null;
702 }
703 this._labelFormat = $.extend(format || {}, (colour ? {fill: colour} : {}));
704 this._plot._drawPlot();
705 return this;
706 },
707
708 /* Set or retrieve the line formatting for this axis.
709 @param colour (string) the line's colour
710 @param width (number) the line's width (optional)
711 @param settings (object) additional formatting settings for the line (optional)
712 @return (SVGPlotAxis) this axis object or
713 (object) line formatting values (if no parameters) */
714 line: function(colour, width, settings) {
715 if (arguments.length == 0) {
716 return this._lineFormat;
717 }
718 if (typeof width != 'number') {
719 settings = width;
720 width = null;
721 }
722 $.extend(this._lineFormat, {stroke: colour, strokeWidth:
723 width || this._lineFormat.strokeWidth}, settings || {});
724 this._plot._drawPlot();
725 return this;
726 },
727
728 /* Return to the parent plot. */
729 end: function() {
730 return this._plot;
731 }
732 });
733
734 /* Details about the plot legend.
735 @param plot (SVGPlot) the owning plot
736 @param bgSettings (object) additional formatting settings for the legend background (optional)
737 @param textSettings (object) additional formatting settings for the legend text (optional)
738 @return (SVGPlotLegend) the new legend object */
739 function SVGPlotLegend(plot, bgSettings, textSettings) {
740 this._plot = plot; // The owning plot
741 this._show = true; // Show the legend?
742 this._area = [0.9, 0.1, 1.0, 0.9]; // The legend area: left, top, right, bottom,
743 // > 1 in pixels, <= 1 as proportion
744 this._sampleSize = 15; // Size of sample box
745 this._bgSettings = bgSettings || {stroke: 'gray'}; // Additional formatting settings for the legend background
746 this._textSettings = textSettings || {}; // Additional formatting settings for the text
747 }
748
749 $.extend(SVGPlotLegend.prototype, {
750
751 /* Set or retrieve whether the legend should be shown.
752 @param show (boolean) true to display it, false to hide it
753 @return (SVGPlotLegend) this legend object or
754 (boolean) show the legend? (if no parameters) */
755 show: function(show) {
756 if (arguments.length == 0) {
757 return this._show;
758 }
759 this._show = show;
760 this._plot._drawPlot();
761 return this;
762 },
763
764 /* Set or retrieve the legend area.
765 @param left (number) > 1 is pixels, <= 1 is proportion of width or
766 (number[4]) for left, top, right, bottom
767 @param top (number) > 1 is pixels, <= 1 is proportion of height
768 @param right (number) > 1 is pixels, <= 1 is proportion of width
769 @param bottom (number) > 1 is pixels, <= 1 is proportion of height
770 @return (SVGPlotLegend) this legend object or
771 (number[4]) the legend area: left, top, right, bottom (if no parameters) */
772 area: function(left, top, right, bottom) {
773 if (arguments.length == 0) {
774 return this._area;
775 }
776 this._area = (isArray(left) ? left : [left, top, right, bottom]);
777 this._plot._drawPlot();
778 return this;
779 },
780
781 /* Set or retrieve additional settings for the legend area.
782 @param sampleSize (number) the size of the sample box to display (optional)
783 @param bgSettings (object) additional formatting settings for the legend background
784 @param textSettings (object) additional formatting settings for the legend text (optional)
785 @return (SVGPlotLegend) this legend object or
786 (object) bgSettings and textSettings for the legend (if no parameters) */
787 settings: function(sampleSize, bgSettings, textSettings) {
788 if (arguments.length == 0) {
789 return {sampleSize: this._sampleSize, bgSettings: this._bgSettings,
790 textSettings: this._textSettings};
791 }
792 if (typeof sampleSize == 'object') {
793 textSettings = bgSettings;
794 bgSettings = sampleSize;
795 sampleSize = null;
796 }
797 this._sampleSize = sampleSize || this._sampleSize;
798 this._bgSettings = bgSettings;
799 this._textSettings = textSettings || this._textSettings;
800 this._plot._drawPlot();
801 return this;
802 },
803
804 /* Return to the parent plot. */
805 end: function() {
806 return this._plot;
807 }
808 });
809
810 //==============================================================================
811
812 /* Determine whether an object is an array. */
813 function isArray(a) {
814 return (a && a.constructor == Array);
815 }
816
817 })(jQuery)