jqplot-rails 0.1 → 0.3

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.
Files changed (38) hide show
  1. data/lib/jqplot-rails.rb +6 -4
  2. data/lib/jqplot-rails/railtie.rb +9 -0
  3. data/lib/jqplot-rails/view_helpers.rb +11 -0
  4. data/vendor/assets/javascripts/jqplot/excanvas.js +1438 -0
  5. data/vendor/assets/javascripts/jqplot/index.js +66 -0
  6. data/vendor/assets/javascripts/{jqplot.1.0.0b2_r792.js → jqplot/jquery.jqplot.js} +3458 -793
  7. data/vendor/assets/javascripts/jqplot/plugins/BezierCurveRenderer.js +313 -0
  8. data/vendor/assets/javascripts/jqplot/plugins/barRenderer.js +797 -0
  9. data/vendor/assets/javascripts/jqplot/plugins/blockRenderer.js +235 -0
  10. data/vendor/assets/javascripts/jqplot/plugins/bubbleRenderer.js +759 -0
  11. data/vendor/assets/javascripts/jqplot/plugins/canvasAxisLabelRenderer.js +203 -0
  12. data/vendor/assets/javascripts/jqplot/plugins/canvasAxisTickRenderer.js +243 -0
  13. data/vendor/assets/javascripts/jqplot/plugins/canvasOverlay.js +865 -0
  14. data/vendor/assets/javascripts/jqplot/plugins/canvasTextRenderer.js +449 -0
  15. data/vendor/assets/javascripts/jqplot/plugins/categoryAxisRenderer.js +673 -0
  16. data/vendor/assets/javascripts/jqplot/plugins/ciParser.js +116 -0
  17. data/vendor/assets/javascripts/jqplot/plugins/cursor.js +1108 -0
  18. data/vendor/assets/javascripts/jqplot/plugins/dateAxisRenderer.js +737 -0
  19. data/vendor/assets/javascripts/jqplot/plugins/donutRenderer.js +805 -0
  20. data/vendor/assets/javascripts/jqplot/plugins/dragable.js +225 -0
  21. data/vendor/assets/javascripts/jqplot/plugins/enhancedLegendRenderer.js +305 -0
  22. data/vendor/assets/javascripts/jqplot/plugins/funnelRenderer.js +943 -0
  23. data/vendor/assets/javascripts/jqplot/plugins/highlighter.js +465 -0
  24. data/vendor/assets/javascripts/jqplot/plugins/json2.js +475 -0
  25. data/vendor/assets/javascripts/jqplot/plugins/logAxisRenderer.js +529 -0
  26. data/vendor/assets/javascripts/jqplot/plugins/mekkoAxisRenderer.js +611 -0
  27. data/vendor/assets/javascripts/jqplot/plugins/mekkoRenderer.js +437 -0
  28. data/vendor/assets/javascripts/jqplot/plugins/meterGaugeRenderer.js +1030 -0
  29. data/vendor/assets/javascripts/jqplot/plugins/mobile.js +45 -0
  30. data/vendor/assets/javascripts/jqplot/plugins/ohlcRenderer.js +373 -0
  31. data/vendor/assets/javascripts/jqplot/plugins/pieRenderer.js +904 -0
  32. data/vendor/assets/javascripts/jqplot/plugins/pointLabels.js +379 -0
  33. data/vendor/assets/javascripts/jqplot/plugins/pyramidAxisRenderer.js +728 -0
  34. data/vendor/assets/javascripts/jqplot/plugins/pyramidGridRenderer.js +429 -0
  35. data/vendor/assets/javascripts/jqplot/plugins/pyramidRenderer.js +514 -0
  36. data/vendor/assets/javascripts/jqplot/plugins/trendline.js +223 -0
  37. data/vendor/assets/stylesheets/{jqplot.1.0.0b2_r792.css → jqplot.css} +48 -15
  38. metadata +64 -25
@@ -0,0 +1,437 @@
1
+ /**
2
+ * jqPlot
3
+ * Pure JavaScript plotting plugin using jQuery
4
+ *
5
+ * Version: 1.0.5
6
+ * Revision: 1122+
7
+ *
8
+ * Copyright (c) 2009-2013 Chris Leonello
9
+ * jqPlot is currently available for use in all personal or commercial projects
10
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12
+ * choose the license that best suits your project and use it accordingly.
13
+ *
14
+ * Although not required, the author would appreciate an email letting him
15
+ * know of any substantial use of jqPlot. You can reach the author at:
16
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
17
+ *
18
+ * If you are feeling kind and generous, consider supporting the project by
19
+ * making a donation at: http://www.jqplot.com/donate.php .
20
+ *
21
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
22
+ *
23
+ * version 2007.04.27
24
+ * author Ash Searle
25
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
26
+ * http://hexmen.com/js/sprintf.js
27
+ * The author (Ash Searle) has placed this code in the public domain:
28
+ * "This code is unrestricted: you are free to use it however you like."
29
+ *
30
+ */
31
+ (function($) {
32
+ /**
33
+ * Class: $.jqplot.MekkoRenderer
34
+ * Draws a Mekko style chart which shows 3 dimensional data on a 2 dimensional graph.
35
+ * the <$.jqplot.MekkoAxisRenderer> should be used with mekko charts. The mekko renderer
36
+ * overrides the default legend renderer with it's own $.jqplot.MekkoLegendRenderer
37
+ * which allows more flexibility to specify number of rows and columns in the legend.
38
+ *
39
+ * Data is specified per bar in the chart. You can specify data as an array of y values, or as
40
+ * an array of [label, value] pairs. Note that labels are used only on the first series.
41
+ * Labels on subsequent series are ignored:
42
+ *
43
+ * > bar1 = [['shirts', 8],['hats', 14],['shoes', 6],['gloves', 16],['dolls', 12]];
44
+ * > bar2 = [15,6,9,13,6];
45
+ * > bar3 = [['grumpy',4],['sneezy',2],['happy',7],['sleepy',9],['doc',7]];
46
+ *
47
+ * If you want to place labels for each bar under the axis, you use the barLabels option on
48
+ * the axes. The bar labels can be styled with the ".jqplot-mekko-barLabel" css class.
49
+ *
50
+ * > barLabels = ['Mickey Mouse', 'Donald Duck', 'Goofy'];
51
+ * > axes:{xaxis:{barLabels:barLabels}}
52
+ *
53
+ */
54
+
55
+
56
+ $.jqplot.MekkoRenderer = function(){
57
+ this.shapeRenderer = new $.jqplot.ShapeRenderer();
58
+ // prop: borderColor
59
+ // color of the borders between areas on the chart
60
+ this.borderColor = null;
61
+ // prop: showBorders
62
+ // True to draw borders lines between areas on the chart.
63
+ // False will draw borders lines with the same color as the area.
64
+ this.showBorders = true;
65
+ };
66
+
67
+ // called with scope of series.
68
+ $.jqplot.MekkoRenderer.prototype.init = function(options, plot) {
69
+ this.fill = false;
70
+ this.fillRect = true;
71
+ this.strokeRect = true;
72
+ this.shadow = false;
73
+ // width of bar on x axis.
74
+ this._xwidth = 0;
75
+ this._xstart = 0;
76
+ $.extend(true, this.renderer, options);
77
+ // set the shape renderer options
78
+ var opts = {lineJoin:'miter', lineCap:'butt', isarc:false, fillRect:this.fillRect, strokeRect:this.strokeRect};
79
+ this.renderer.shapeRenderer.init(opts);
80
+ plot.axes.x2axis._series.push(this);
81
+ this._type = 'mekko';
82
+ };
83
+
84
+ // Method: setGridData
85
+ // converts the user data values to grid coordinates and stores them
86
+ // in the gridData array. Will convert user data into appropriate
87
+ // rectangles.
88
+ // Called with scope of a series.
89
+ $.jqplot.MekkoRenderer.prototype.setGridData = function(plot) {
90
+ // recalculate the grid data
91
+ var xp = this._xaxis.series_u2p;
92
+ var yp = this._yaxis.series_u2p;
93
+ var data = this._plotData;
94
+ this.gridData = [];
95
+ // figure out width on x axis.
96
+ // this._xwidth = this._sumy / plot._sumy * this.canvas.getWidth();
97
+ this._xwidth = xp(this._sumy) - xp(0);
98
+ if (this.index>0) {
99
+ this._xstart = plot.series[this.index-1]._xstart + plot.series[this.index-1]._xwidth;
100
+ }
101
+ var totheight = this.canvas.getHeight();
102
+ var sumy = 0;
103
+ var cury;
104
+ var curheight;
105
+ for (var i=0; i<data.length; i++) {
106
+ if (data[i] != null) {
107
+ sumy += data[i][1];
108
+ cury = totheight - (sumy / this._sumy * totheight);
109
+ curheight = data[i][1] / this._sumy * totheight;
110
+ this.gridData.push([this._xstart, cury, this._xwidth, curheight]);
111
+ }
112
+ }
113
+ };
114
+
115
+ // Method: makeGridData
116
+ // converts any arbitrary data values to grid coordinates and
117
+ // returns them. This method exists so that plugins can use a series'
118
+ // linerenderer to generate grid data points without overwriting the
119
+ // grid data associated with that series.
120
+ // Called with scope of a series.
121
+ $.jqplot.MekkoRenderer.prototype.makeGridData = function(data, plot) {
122
+ // recalculate the grid data
123
+ // figure out width on x axis.
124
+ var xp = this._xaxis.series_u2p;
125
+ var totheight = this.canvas.getHeight();
126
+ var sumy = 0;
127
+ var cury;
128
+ var curheight;
129
+ var gd = [];
130
+ for (var i=0; i<data.length; i++) {
131
+ if (data[i] != null) {
132
+ sumy += data[i][1];
133
+ cury = totheight - (sumy / this._sumy * totheight);
134
+ curheight = data[i][1] / this._sumy * totheight;
135
+ gd.push([this._xstart, cury, this._xwidth, curheight]);
136
+ }
137
+ }
138
+ return gd;
139
+ };
140
+
141
+
142
+ // called within scope of series.
143
+ $.jqplot.MekkoRenderer.prototype.draw = function(ctx, gd, options) {
144
+ var i;
145
+ var opts = (options != undefined) ? options : {};
146
+ var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
147
+ var colorGenerator = new $.jqplot.ColorGenerator(this.seriesColors);
148
+ ctx.save();
149
+ if (gd.length) {
150
+ if (showLine) {
151
+ for (i=0; i<gd.length; i++){
152
+ opts.fillStyle = colorGenerator.next();
153
+ if (this.renderer.showBorders) {
154
+ opts.strokeStyle = this.renderer.borderColor;
155
+ }
156
+ else {
157
+ opts.strokeStyle = opts.fillStyle;
158
+ }
159
+ this.renderer.shapeRenderer.draw(ctx, gd[i], opts);
160
+ }
161
+ }
162
+ }
163
+
164
+ ctx.restore();
165
+ };
166
+
167
+ $.jqplot.MekkoRenderer.prototype.drawShadow = function(ctx, gd, options) {
168
+ // This is a no-op, no shadows on mekko charts.
169
+ };
170
+
171
+ /**
172
+ * Class: $.jqplot.MekkoLegendRenderer
173
+ * Legend renderer used by mekko charts with options for
174
+ * controlling number or rows and columns as well as placement
175
+ * outside of plot area.
176
+ *
177
+ */
178
+ $.jqplot.MekkoLegendRenderer = function(){
179
+ //
180
+ };
181
+
182
+ $.jqplot.MekkoLegendRenderer.prototype.init = function(options) {
183
+ // prop: numberRows
184
+ // Maximum number of rows in the legend. 0 or null for unlimited.
185
+ this.numberRows = null;
186
+ // prop: numberColumns
187
+ // Maximum number of columns in the legend. 0 or null for unlimited.
188
+ this.numberColumns = null;
189
+ // this will override the placement option on the Legend object
190
+ this.placement = "outside";
191
+ $.extend(true, this, options);
192
+ };
193
+
194
+ // called with scope of legend
195
+ $.jqplot.MekkoLegendRenderer.prototype.draw = function() {
196
+ var legend = this;
197
+ if (this.show) {
198
+ var series = this._series;
199
+ var ss = 'position:absolute;';
200
+ ss += (this.background) ? 'background:'+this.background+';' : '';
201
+ ss += (this.border) ? 'border:'+this.border+';' : '';
202
+ ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
203
+ ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
204
+ ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
205
+ this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
206
+ // Mekko charts legends don't go by number of series, but by number of data points
207
+ // in the series. Refactor things here for that.
208
+
209
+ var pad = false,
210
+ reverse = true, // mekko charts are always stacked, so reverse
211
+ nr, nc;
212
+ var s = series[0];
213
+ var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
214
+
215
+ if (s.show) {
216
+ var pd = s.data;
217
+ if (this.numberRows) {
218
+ nr = this.numberRows;
219
+ if (!this.numberColumns){
220
+ nc = Math.ceil(pd.length/nr);
221
+ }
222
+ else{
223
+ nc = this.numberColumns;
224
+ }
225
+ }
226
+ else if (this.numberColumns) {
227
+ nc = this.numberColumns;
228
+ nr = Math.ceil(pd.length/this.numberColumns);
229
+ }
230
+ else {
231
+ nr = pd.length;
232
+ nc = 1;
233
+ }
234
+
235
+ var i, j, tr, td1, td2, lt, rs, color;
236
+ var idx = 0;
237
+
238
+ for (i=0; i<nr; i++) {
239
+ if (reverse){
240
+ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
241
+ }
242
+ else{
243
+ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
244
+ }
245
+ for (j=0; j<nc; j++) {
246
+ if (idx < pd.length) {
247
+ lt = this.labels[idx] || pd[idx][0].toString();
248
+ color = colorGenerator.next();
249
+ if (!reverse){
250
+ if (i>0){
251
+ pad = true;
252
+ }
253
+ else{
254
+ pad = false;
255
+ }
256
+ }
257
+ else{
258
+ if (i == nr -1){
259
+ pad = false;
260
+ }
261
+ else{
262
+ pad = true;
263
+ }
264
+ }
265
+ rs = (pad) ? this.rowSpacing : '0';
266
+
267
+ td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
268
+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
269
+ '</div></td>');
270
+ td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
271
+ if (this.escapeHtml){
272
+ td2.text(lt);
273
+ }
274
+ else {
275
+ td2.html(lt);
276
+ }
277
+ if (reverse) {
278
+ td2.prependTo(tr);
279
+ td1.prependTo(tr);
280
+ }
281
+ else {
282
+ td1.appendTo(tr);
283
+ td2.appendTo(tr);
284
+ }
285
+ pad = true;
286
+ }
287
+ idx++;
288
+ }
289
+ }
290
+
291
+ tr = null;
292
+ td1 = null;
293
+ td2 = null;
294
+ }
295
+ }
296
+ return this._elem;
297
+ };
298
+
299
+ $.jqplot.MekkoLegendRenderer.prototype.pack = function(offsets) {
300
+ if (this.show) {
301
+ // fake a grid for positioning
302
+ var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};
303
+ if (this.placement == 'insideGrid') {
304
+ switch (this.location) {
305
+ case 'nw':
306
+ var a = grid._left + this.xoffset;
307
+ var b = grid._top + this.yoffset;
308
+ this._elem.css('left', a);
309
+ this._elem.css('top', b);
310
+ break;
311
+ case 'n':
312
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
313
+ var b = grid._top + this.yoffset;
314
+ this._elem.css('left', a);
315
+ this._elem.css('top', b);
316
+ break;
317
+ case 'ne':
318
+ var a = offsets.right + this.xoffset;
319
+ var b = grid._top + this.yoffset;
320
+ this._elem.css({right:a, top:b});
321
+ break;
322
+ case 'e':
323
+ var a = offsets.right + this.xoffset;
324
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
325
+ this._elem.css({right:a, top:b});
326
+ break;
327
+ case 'se':
328
+ var a = offsets.right + this.xoffset;
329
+ var b = offsets.bottom + this.yoffset;
330
+ this._elem.css({right:a, bottom:b});
331
+ break;
332
+ case 's':
333
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
334
+ var b = offsets.bottom + this.yoffset;
335
+ this._elem.css({left:a, bottom:b});
336
+ break;
337
+ case 'sw':
338
+ var a = grid._left + this.xoffset;
339
+ var b = offsets.bottom + this.yoffset;
340
+ this._elem.css({left:a, bottom:b});
341
+ break;
342
+ case 'w':
343
+ var a = grid._left + this.xoffset;
344
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
345
+ this._elem.css({left:a, top:b});
346
+ break;
347
+ default: // same as 'se'
348
+ var a = grid._right - this.xoffset;
349
+ var b = grid._bottom + this.yoffset;
350
+ this._elem.css({right:a, bottom:b});
351
+ break;
352
+ }
353
+
354
+ }
355
+ else {
356
+ switch (this.location) {
357
+ case 'nw':
358
+ var a = this._plotDimensions.width - grid._left + this.xoffset;
359
+ var b = grid._top + this.yoffset;
360
+ this._elem.css('right', a);
361
+ this._elem.css('top', b);
362
+ break;
363
+ case 'n':
364
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
365
+ var b = this._plotDimensions.height - grid._top + this.yoffset;
366
+ this._elem.css('left', a);
367
+ this._elem.css('bottom', b);
368
+ break;
369
+ case 'ne':
370
+ var a = this._plotDimensions.width - offsets.right + this.xoffset;
371
+ var b = grid._top + this.yoffset;
372
+ this._elem.css({left:a, top:b});
373
+ break;
374
+ case 'e':
375
+ var a = this._plotDimensions.width - offsets.right + this.xoffset;
376
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
377
+ this._elem.css({left:a, top:b});
378
+ break;
379
+ case 'se':
380
+ var a = this._plotDimensions.width - offsets.right + this.xoffset;
381
+ var b = offsets.bottom + this.yoffset;
382
+ this._elem.css({left:a, bottom:b});
383
+ break;
384
+ case 's':
385
+ var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
386
+ var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
387
+ this._elem.css({left:a, top:b});
388
+ break;
389
+ case 'sw':
390
+ var a = this._plotDimensions.width - grid._left + this.xoffset;
391
+ var b = offsets.bottom + this.yoffset;
392
+ this._elem.css({right:a, bottom:b});
393
+ break;
394
+ case 'w':
395
+ var a = this._plotDimensions.width - grid._left + this.xoffset;
396
+ var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
397
+ this._elem.css({right:a, top:b});
398
+ break;
399
+ default: // same as 'se'
400
+ var a = grid._right - this.xoffset;
401
+ var b = grid._bottom + this.yoffset;
402
+ this._elem.css({right:a, bottom:b});
403
+ break;
404
+ }
405
+ }
406
+ }
407
+ };
408
+
409
+ // setup default renderers for axes and legend so user doesn't have to
410
+ // called with scope of plot
411
+ function preInit(target, data, options) {
412
+ options = options || {};
413
+ options.axesDefaults = options.axesDefaults || {};
414
+ options.legend = options.legend || {};
415
+ options.seriesDefaults = options.seriesDefaults || {};
416
+ var setopts = false;
417
+ if (options.seriesDefaults.renderer == $.jqplot.MekkoRenderer) {
418
+ setopts = true;
419
+ }
420
+ else if (options.series) {
421
+ for (var i=0; i < options.series.length; i++) {
422
+ if (options.series[i].renderer == $.jqplot.MekkoRenderer) {
423
+ setopts = true;
424
+ }
425
+ }
426
+ }
427
+
428
+ if (setopts) {
429
+ options.axesDefaults.renderer = $.jqplot.MekkoAxisRenderer;
430
+ options.legend.renderer = $.jqplot.MekkoLegendRenderer;
431
+ options.legend.preDraw = true;
432
+ }
433
+ }
434
+
435
+ $.jqplot.preInitHooks.push(preInit);
436
+
437
+ })(jQuery);
@@ -0,0 +1,1030 @@
1
+ /**
2
+ * jqPlot
3
+ * Pure JavaScript plotting plugin using jQuery
4
+ *
5
+ * Version: 1.0.5
6
+ * Revision: 1122+
7
+ *
8
+ * Copyright (c) 2009-2013 Chris Leonello
9
+ * jqPlot is currently available for use in all personal or commercial projects
10
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
11
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
12
+ * choose the license that best suits your project and use it accordingly.
13
+ *
14
+ * Although not required, the author would appreciate an email letting him
15
+ * know of any substantial use of jqPlot. You can reach the author at:
16
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
17
+ *
18
+ * If you are feeling kind and generous, consider supporting the project by
19
+ * making a donation at: http://www.jqplot.com/donate.php .
20
+ *
21
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
22
+ *
23
+ * version 2007.04.27
24
+ * author Ash Searle
25
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
26
+ * http://hexmen.com/js/sprintf.js
27
+ * The author (Ash Searle) has placed this code in the public domain:
28
+ * "This code is unrestricted: you are free to use it however you like."
29
+ *
30
+ */
31
+ (function($) {
32
+ /**
33
+ * Class: $.jqplot.MeterGaugeRenderer
34
+ * Plugin renderer to draw a meter gauge chart.
35
+ *
36
+ * Data consists of a single series with 1 data point to position the gauge needle.
37
+ *
38
+ * To use this renderer, you need to include the
39
+ * meter gauge renderer plugin, for example:
40
+ *
41
+ * > <script type="text/javascript" src="plugins/jqplot.meterGaugeRenderer.js"></script>
42
+ *
43
+ * Properties described here are passed into the $.jqplot function
44
+ * as options on the series renderer. For example:
45
+ *
46
+ * > plot0 = $.jqplot('chart0',[[18]],{
47
+ * > title: 'Network Speed',
48
+ * > seriesDefaults: {
49
+ * > renderer: $.jqplot.MeterGaugeRenderer,
50
+ * > rendererOptions: {
51
+ * > label: 'MB/s'
52
+ * > }
53
+ * > }
54
+ * > });
55
+ *
56
+ * A meterGauge plot does not support events.
57
+ */
58
+ $.jqplot.MeterGaugeRenderer = function(){
59
+ $.jqplot.LineRenderer.call(this);
60
+ };
61
+
62
+ $.jqplot.MeterGaugeRenderer.prototype = new $.jqplot.LineRenderer();
63
+ $.jqplot.MeterGaugeRenderer.prototype.constructor = $.jqplot.MeterGaugeRenderer;
64
+
65
+ // called with scope of a series
66
+ $.jqplot.MeterGaugeRenderer.prototype.init = function(options) {
67
+ // Group: Properties
68
+ //
69
+ // prop: diameter
70
+ // Outer diameter of the meterGauge, auto computed by default
71
+ this.diameter = null;
72
+ // prop: padding
73
+ // padding between the meterGauge and plot edges, auto
74
+ // calculated by default.
75
+ this.padding = null;
76
+ // prop: shadowOffset
77
+ // offset of the shadow from the gauge ring and offset of
78
+ // each succesive stroke of the shadow from the last.
79
+ this.shadowOffset = 2;
80
+ // prop: shadowAlpha
81
+ // transparency of the shadow (0 = transparent, 1 = opaque)
82
+ this.shadowAlpha = 0.07;
83
+ // prop: shadowDepth
84
+ // number of strokes to apply to the shadow,
85
+ // each stroke offset shadowOffset from the last.
86
+ this.shadowDepth = 4;
87
+ // prop: background
88
+ // background color of the inside of the gauge.
89
+ this.background = "#efefef";
90
+ // prop: ringColor
91
+ // color of the outer ring, hub, and needle of the gauge.
92
+ this.ringColor = "#BBC6D0";
93
+ // needle color not implemented yet.
94
+ this.needleColor = "#C3D3E5";
95
+ // prop: tickColor
96
+ // color of the tick marks around the gauge.
97
+ this.tickColor = "989898";
98
+ // prop: ringWidth
99
+ // width of the ring around the gauge. Auto computed by default.
100
+ this.ringWidth = null;
101
+ // prop: min
102
+ // Minimum value on the gauge. Auto computed by default
103
+ this.min;
104
+ // prop: max
105
+ // Maximum value on the gauge. Auto computed by default
106
+ this.max;
107
+ // prop: ticks
108
+ // Array of tick values. Auto computed by default.
109
+ this.ticks = [];
110
+ // prop: showTicks
111
+ // true to show ticks around gauge.
112
+ this.showTicks = true;
113
+ // prop: showTickLabels
114
+ // true to show tick labels next to ticks.
115
+ this.showTickLabels = true;
116
+ // prop: label
117
+ // A gauge label like 'kph' or 'Volts'
118
+ this.label = null;
119
+ // prop: labelHeightAdjust
120
+ // Number of Pixels to offset the label up (-) or down (+) from its default position.
121
+ this.labelHeightAdjust = 0;
122
+ // prop: labelPosition
123
+ // Where to position the label, either 'inside' or 'bottom'.
124
+ this.labelPosition = 'inside';
125
+ // prop: intervals
126
+ // Array of ranges to be drawn around the gauge.
127
+ // Array of form:
128
+ // > [value1, value2, ...]
129
+ // indicating the values for the first, second, ... intervals.
130
+ this.intervals = [];
131
+ // prop: intervalColors
132
+ // Array of colors to use for the intervals.
133
+ this.intervalColors = [ "#4bb2c5", "#EAA228", "#c5b47f", "#579575", "#839557", "#958c12", "#953579", "#4b5de4", "#d8b83f", "#ff5800", "#0085cc", "#c747a3", "#cddf54", "#FBD178", "#26B4E3", "#bd70c7"];
134
+ // prop: intervalInnerRadius
135
+ // Radius of the inner circle of the interval ring.
136
+ this.intervalInnerRadius = null;
137
+ // prop: intervalOuterRadius
138
+ // Radius of the outer circle of the interval ring.
139
+ this.intervalOuterRadius = null;
140
+ this.tickRenderer = $.jqplot.MeterGaugeTickRenderer;
141
+ // ticks spaced every 1, 2, 2.5, 5, 10, 20, .1, .2, .25, .5, etc.
142
+ this.tickPositions = [1, 2, 2.5, 5, 10];
143
+ // prop: tickSpacing
144
+ // Degrees between ticks. This is a target number, if
145
+ // incompatible span and ticks are supplied, a suitable
146
+ // spacing close to this value will be computed.
147
+ this.tickSpacing = 30;
148
+ this.numberMinorTicks = null;
149
+ // prop: hubRadius
150
+ // Radius of the hub at the bottom center of gauge which the needle attaches to.
151
+ // Auto computed by default
152
+ this.hubRadius = null;
153
+ // prop: tickPadding
154
+ // padding of the tick marks to the outer ring and the tick labels to marks.
155
+ // Auto computed by default.
156
+ this.tickPadding = null;
157
+ // prop: needleThickness
158
+ // Maximum thickness the needle. Auto computed by default.
159
+ this.needleThickness = null;
160
+ // prop: needlePad
161
+ // Padding between needle and inner edge of the ring when the needle is at the min or max gauge value.
162
+ this.needlePad = 6;
163
+ // prop: pegNeedle
164
+ // True will stop needle just below/above the min/max values if data is below/above min/max,
165
+ // as if the meter is "pegged".
166
+ this.pegNeedle = true;
167
+ this._type = 'meterGauge';
168
+
169
+ $.extend(true, this, options);
170
+ this.type = null;
171
+ this.numberTicks = null;
172
+ this.tickInterval = null;
173
+ // span, the sweep (in degrees) from min to max. This gauge is
174
+ // a semi-circle.
175
+ this.span = 180;
176
+ // get rid of this nonsense
177
+ // this.innerSpan = this.span;
178
+ if (this.type == 'circular') {
179
+ this.semiCircular = false;
180
+ }
181
+ else if (this.type != 'circular') {
182
+ this.semiCircular = true;
183
+ }
184
+ else {
185
+ this.semiCircular = (this.span <= 180) ? true : false;
186
+ }
187
+ this._tickPoints = [];
188
+ // reference to label element.
189
+ this._labelElem = null;
190
+
191
+ // start the gauge at the beginning of the span
192
+ this.startAngle = (90 + (360 - this.span)/2) * Math.PI/180;
193
+ this.endAngle = (90 - (360 - this.span)/2) * Math.PI/180;
194
+
195
+ this.setmin = !!(this.min == null);
196
+ this.setmax = !!(this.max == null);
197
+
198
+ // if given intervals and is an array of values, create labels and colors.
199
+ if (this.intervals.length) {
200
+ if (this.intervals[0].length == null || this.intervals.length == 1) {
201
+ for (var i=0; i<this.intervals.length; i++) {
202
+ this.intervals[i] = [this.intervals[i], this.intervals[i], this.intervalColors[i]];
203
+ }
204
+ }
205
+ else if (this.intervals[0].length == 2) {
206
+ for (i=0; i<this.intervals.length; i++) {
207
+ this.intervals[i] = [this.intervals[i][0], this.intervals[i][1], this.intervalColors[i]];
208
+ }
209
+ }
210
+ }
211
+
212
+ // compute min, max and ticks if not supplied:
213
+ if (this.ticks.length) {
214
+ if (this.ticks[0].length == null || this.ticks[0].length == 1) {
215
+ for (var i=0; i<this.ticks.length; i++) {
216
+ this.ticks[i] = [this.ticks[i], this.ticks[i]];
217
+ }
218
+ }
219
+ this.min = (this.min == null) ? this.ticks[0][0] : this.min;
220
+ this.max = (this.max == null) ? this.ticks[this.ticks.length-1][0] : this.max;
221
+ this.setmin = false;
222
+ this.setmax = false;
223
+ this.numberTicks = this.ticks.length;
224
+ this.tickInterval = this.ticks[1][0] - this.ticks[0][0];
225
+ this.tickFactor = Math.floor(parseFloat((Math.log(this.tickInterval)/Math.log(10)).toFixed(11)));
226
+ // use the first interal to calculate minor ticks;
227
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);
228
+ if (!this.numberMinorTicks) {
229
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
230
+ }
231
+ if (!this.numberMinorTicks) {
232
+ this.numberMinorTicks = 1;
233
+ }
234
+ }
235
+
236
+ else if (this.intervals.length) {
237
+ this.min = (this.min == null) ? 0 : this.min;
238
+ this.setmin = false;
239
+ if (this.max == null) {
240
+ if (this.intervals[this.intervals.length-1][0] >= this.data[0][1]) {
241
+ this.max = this.intervals[this.intervals.length-1][0];
242
+ this.setmax = false;
243
+ }
244
+ }
245
+ else {
246
+ this.setmax = false;
247
+ }
248
+ }
249
+
250
+ else {
251
+ // no ticks and no intervals supplied, put needle in middle
252
+ this.min = (this.min == null) ? 0 : this.min;
253
+ this.setmin = false;
254
+ if (this.max == null) {
255
+ this.max = this.data[0][1] * 1.25;
256
+ this.setmax = true;
257
+ }
258
+ else {
259
+ this.setmax = false;
260
+ }
261
+ }
262
+ };
263
+
264
+ $.jqplot.MeterGaugeRenderer.prototype.setGridData = function(plot) {
265
+ // set gridData property. This will hold angle in radians of each data point.
266
+ var stack = [];
267
+ var td = [];
268
+ var sa = this.startAngle;
269
+ for (var i=0; i<this.data.length; i++){
270
+ stack.push(this.data[i][1]);
271
+ td.push([this.data[i][0]]);
272
+ if (i>0) {
273
+ stack[i] += stack[i-1];
274
+ }
275
+ }
276
+ var fact = Math.PI*2/stack[stack.length - 1];
277
+
278
+ for (var i=0; i<stack.length; i++) {
279
+ td[i][1] = stack[i] * fact;
280
+ }
281
+ this.gridData = td;
282
+ };
283
+
284
+ $.jqplot.MeterGaugeRenderer.prototype.makeGridData = function(data, plot) {
285
+ var stack = [];
286
+ var td = [];
287
+ var sa = this.startAngle;
288
+ for (var i=0; i<data.length; i++){
289
+ stack.push(data[i][1]);
290
+ td.push([data[i][0]]);
291
+ if (i>0) {
292
+ stack[i] += stack[i-1];
293
+ }
294
+ }
295
+ var fact = Math.PI*2/stack[stack.length - 1];
296
+
297
+ for (var i=0; i<stack.length; i++) {
298
+ td[i][1] = stack[i] * fact;
299
+ }
300
+ return td;
301
+ };
302
+
303
+
304
+ function getnmt(pos, interval, fact) {
305
+ var temp;
306
+ for (var i=pos.length-1; i>=0; i--) {
307
+ temp = interval/(pos[i] * Math.pow(10, fact));
308
+ if (temp == 4 || temp == 5) {
309
+ return temp - 1;
310
+ }
311
+ }
312
+ return null;
313
+ }
314
+
315
+ // called with scope of series
316
+ $.jqplot.MeterGaugeRenderer.prototype.draw = function (ctx, gd, options) {
317
+ var i;
318
+ var opts = (options != undefined) ? options : {};
319
+ // offset and direction of offset due to legend placement
320
+ var offx = 0;
321
+ var offy = 0;
322
+ var trans = 1;
323
+ if (options.legendInfo && options.legendInfo.placement == 'inside') {
324
+ var li = options.legendInfo;
325
+ switch (li.location) {
326
+ case 'nw':
327
+ offx = li.width + li.xoffset;
328
+ break;
329
+ case 'w':
330
+ offx = li.width + li.xoffset;
331
+ break;
332
+ case 'sw':
333
+ offx = li.width + li.xoffset;
334
+ break;
335
+ case 'ne':
336
+ offx = li.width + li.xoffset;
337
+ trans = -1;
338
+ break;
339
+ case 'e':
340
+ offx = li.width + li.xoffset;
341
+ trans = -1;
342
+ break;
343
+ case 'se':
344
+ offx = li.width + li.xoffset;
345
+ trans = -1;
346
+ break;
347
+ case 'n':
348
+ offy = li.height + li.yoffset;
349
+ break;
350
+ case 's':
351
+ offy = li.height + li.yoffset;
352
+ trans = -1;
353
+ break;
354
+ default:
355
+ break;
356
+ }
357
+ }
358
+
359
+
360
+
361
+ // pre-draw so can get it's dimensions.
362
+ if (this.label) {
363
+ this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>');
364
+ this.canvas._elem.after(this._labelElem);
365
+ }
366
+
367
+ var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
368
+ var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
369
+ var fill = (opts.fill != undefined) ? opts.fill : this.fill;
370
+ var cw = ctx.canvas.width;
371
+ var ch = ctx.canvas.height;
372
+ if (this.padding == null) {
373
+ this.padding = Math.round(Math.min(cw, ch)/30);
374
+ }
375
+ var w = cw - offx - 2 * this.padding;
376
+ var h = ch - offy - 2 * this.padding;
377
+ if (this.labelPosition == 'bottom' && this.label) {
378
+ h -= this._labelElem.outerHeight(true);
379
+ }
380
+ var mindim = Math.min(w,h);
381
+ var d = mindim;
382
+
383
+ if (!this.diameter) {
384
+ if (this.semiCircular) {
385
+ if ( w >= 2*h) {
386
+ if (!this.ringWidth) {
387
+ this.ringWidth = 2*h/35;
388
+ }
389
+ this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
390
+ this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad;
391
+ this.diameter = 2 * (h - 2*this.innerPad);
392
+ }
393
+ else {
394
+ if (!this.ringWidth) {
395
+ this.ringWidth = w/35;
396
+ }
397
+ this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
398
+ this.innerPad = this.ringWidth/2 + this.needleThickness/2 + this.needlePad;
399
+ this.diameter = w - 2*this.innerPad - this.ringWidth - this.padding;
400
+ }
401
+ // center taking into account legend and over draw for gauge bottom below hub.
402
+ // this will be center of hub.
403
+ this._center = [(cw - trans * offx)/2 + trans * offx, (ch + trans*offy - this.padding - this.ringWidth - this.innerPad)];
404
+ }
405
+ else {
406
+ if (!this.ringWidth) {
407
+ this.ringWidth = d/35;
408
+ }
409
+ this.needleThickness = this.needleThickness || 2+Math.pow(this.ringWidth, 0.8);
410
+ this.innerPad = 0;
411
+ this.diameter = d - this.ringWidth;
412
+ // center in middle of canvas taking into account legend.
413
+ // will be center of hub.
414
+ this._center = [(cw-trans*offx)/2 + trans * offx, (ch-trans*offy)/2 + trans * offy];
415
+ }
416
+ }
417
+
418
+
419
+ if (this._labelElem && this.labelPosition == 'bottom') {
420
+ this._center[1] -= this._labelElem.outerHeight(true);
421
+ }
422
+
423
+ this._radius = this.diameter/2;
424
+
425
+ this.tickSpacing = 6000/this.diameter;
426
+
427
+ if (!this.hubRadius) {
428
+ this.hubRadius = this.diameter/18;
429
+ }
430
+
431
+ this.shadowOffset = 0.5 + this.ringWidth/9;
432
+ this.shadowWidth = this.ringWidth*1;
433
+
434
+ this.tickPadding = 3 + Math.pow(this.diameter/20, 0.7);
435
+ this.tickOuterRadius = this._radius - this.ringWidth/2 - this.tickPadding;
436
+ this.tickLength = (this.showTicks) ? this._radius/13 : 0;
437
+
438
+ if (this.ticks.length == 0) {
439
+ // no ticks, lets make some.
440
+ var max = this.max,
441
+ min = this.min,
442
+ setmax = this.setmax,
443
+ setmin = this.setmin,
444
+ ti = (max - min) * this.tickSpacing / this.span;
445
+ var tf = Math.floor(parseFloat((Math.log(ti)/Math.log(10)).toFixed(11)));
446
+ var tp = (ti/Math.pow(10, tf));
447
+ (tp > 2 && tp <= 2.5) ? tp = 2.5 : tp = Math.ceil(tp);
448
+ var t = this.tickPositions;
449
+ var tpindex, nt;
450
+
451
+ for (i=0; i<t.length; i++) {
452
+ if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) {
453
+ ti = t[i]*Math.pow(10, tf);
454
+ tpindex = i;
455
+ }
456
+ }
457
+
458
+ for (i=0; i<t.length; i++) {
459
+ if (tp == t[i] || i && t[i-1] < tp && tp < t[i]) {
460
+ ti = t[i]*Math.pow(10, tf);
461
+ nt = Math.ceil((max - min) / ti);
462
+ }
463
+ }
464
+
465
+ // both max and min are free
466
+ if (setmax && setmin) {
467
+ var tmin = (min > 0) ? min - min % ti : min - min % ti - ti;
468
+ if (!this.forceZero) {
469
+ var diff = Math.min(min - tmin, 0.8*ti);
470
+ var ntp = Math.floor(diff/t[tpindex]);
471
+ if (ntp > 1) {
472
+ tmin = tmin + t[tpindex] * (ntp-1);
473
+ if (parseInt(tmin, 10) != tmin && parseInt(tmin-t[tpindex], 10) == tmin-t[tpindex]) {
474
+ tmin = tmin - t[tpindex];
475
+ }
476
+ }
477
+ }
478
+ if (min == tmin) {
479
+ min -= ti;
480
+ }
481
+ else {
482
+ // tmin should always be lower than dataMin
483
+ if (min - tmin > 0.23*ti) {
484
+ min = tmin;
485
+ }
486
+ else {
487
+ min = tmin -ti;
488
+ nt += 1;
489
+ }
490
+ }
491
+ nt += 1;
492
+ var tmax = min + (nt - 1) * ti;
493
+ if (max >= tmax) {
494
+ tmax += ti;
495
+ nt += 1;
496
+ }
497
+ // now tmax should always be mroe than dataMax
498
+ if (tmax - max < 0.23*ti) {
499
+ tmax += ti;
500
+ nt += 1;
501
+ }
502
+ this.max = max = tmax;
503
+ this.min = min;
504
+
505
+ this.tickInterval = ti;
506
+ this.numberTicks = nt;
507
+ var it;
508
+ for (i=0; i<nt; i++) {
509
+ it = parseFloat((min+i*ti).toFixed(11));
510
+ this.ticks.push([it, it]);
511
+ }
512
+ this.max = this.ticks[nt-1][1];
513
+
514
+ this.tickFactor = tf;
515
+ // determine number of minor ticks
516
+
517
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);
518
+
519
+ if (!this.numberMinorTicks) {
520
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
521
+ }
522
+ }
523
+ // max is free, min is fixed
524
+ else if (setmax) {
525
+ var tmax = min + (nt - 1) * ti;
526
+ if (max >= tmax) {
527
+ max = tmax + ti;
528
+ nt += 1;
529
+ }
530
+ else {
531
+ max = tmax;
532
+ }
533
+
534
+ this.tickInterval = this.tickInterval || ti;
535
+ this.numberTicks = this.numberTicks || nt;
536
+ var it;
537
+ for (i=0; i<this.numberTicks; i++) {
538
+ it = parseFloat((min+i*this.tickInterval).toFixed(11));
539
+ this.ticks.push([it, it]);
540
+ }
541
+ this.max = this.ticks[this.numberTicks-1][1];
542
+
543
+ this.tickFactor = tf;
544
+ // determine number of minor ticks
545
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);
546
+
547
+ if (!this.numberMinorTicks) {
548
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
549
+ }
550
+ }
551
+
552
+ // not setting max or min
553
+ if (!setmax && !setmin) {
554
+ var range = this.max - this.min;
555
+ tf = Math.floor(parseFloat((Math.log(range)/Math.log(10)).toFixed(11))) - 1;
556
+ var nticks = [5,6,4,7,3,8,9,10,2], res, numticks, nonSigDigits=0, sigRange;
557
+ // check to see how many zeros are at the end of the range
558
+ if (range > 1) {
559
+ var rstr = String(range);
560
+ if (rstr.search(/\./) == -1) {
561
+ var pos = rstr.search(/0+$/);
562
+ nonSigDigits = (pos > 0) ? rstr.length - pos - 1 : 0;
563
+ }
564
+ }
565
+ sigRange = range/Math.pow(10, nonSigDigits);
566
+ for (i=0; i<nticks.length; i++) {
567
+ res = sigRange/(nticks[i]-1);
568
+ if (res == parseInt(res, 10)) {
569
+ this.numberTicks = nticks[i];
570
+ this.tickInterval = range/(this.numberTicks-1);
571
+ this.tickFactor = tf+1;
572
+ break;
573
+ }
574
+ }
575
+ var it;
576
+ for (i=0; i<this.numberTicks; i++) {
577
+ it = parseFloat((this.min+i*this.tickInterval).toFixed(11));
578
+ this.ticks.push([it, it]);
579
+ }
580
+ // determine number of minor ticks
581
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor);
582
+
583
+ if (!this.numberMinorTicks) {
584
+ this.numberMinorTicks = getnmt(this.tickPositions, this.tickInterval, this.tickFactor-1);
585
+ }
586
+
587
+ if (!this.numberMinorTicks) {
588
+ this.numberMinorTicks = 1;
589
+ var nums = [4, 5, 3, 6, 2];
590
+ for (i=0; i<5; i++) {
591
+ var temp = this.tickInterval/nums[i];
592
+ if (temp == parseInt(temp, 10)) {
593
+ this.numberMinorTicks = nums[i]-1;
594
+ break;
595
+ }
596
+ }
597
+ }
598
+ }
599
+ }
600
+
601
+
602
+ var r = this._radius,
603
+ sa = this.startAngle,
604
+ ea = this.endAngle,
605
+ pi = Math.PI,
606
+ hpi = Math.PI/2;
607
+
608
+ if (this.semiCircular) {
609
+ var overAngle = Math.atan(this.innerPad/r),
610
+ outersa = this.outerStartAngle = sa - overAngle,
611
+ outerea = this.outerEndAngle = ea + overAngle,
612
+ hubsa = this.hubStartAngle = sa - Math.atan(this.innerPad/this.hubRadius*2),
613
+ hubea = this.hubEndAngle = ea + Math.atan(this.innerPad/this.hubRadius*2);
614
+
615
+ ctx.save();
616
+
617
+ ctx.translate(this._center[0], this._center[1]);
618
+ ctx.lineJoin = "round";
619
+ ctx.lineCap = "round";
620
+
621
+ // draw the innerbackground
622
+ ctx.save();
623
+ ctx.beginPath();
624
+ ctx.fillStyle = this.background;
625
+ ctx.arc(0, 0, r, outersa, outerea, false);
626
+ ctx.closePath();
627
+ ctx.fill();
628
+ ctx.restore();
629
+
630
+ // draw the shadow
631
+ // the outer ring.
632
+ var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
633
+ ctx.save();
634
+ for (var i=0; i<this.shadowDepth; i++) {
635
+ ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
636
+ ctx.beginPath();
637
+ ctx.strokeStyle = shadowColor;
638
+ ctx.lineWidth = this.shadowWidth;
639
+ ctx.arc(0 ,0, r, outersa, outerea, false);
640
+ ctx.closePath();
641
+ ctx.stroke();
642
+ }
643
+ ctx.restore();
644
+
645
+ // the inner hub.
646
+ ctx.save();
647
+ var tempd = parseInt((this.shadowDepth+1)/2, 10);
648
+ for (var i=0; i<tempd; i++) {
649
+ ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
650
+ ctx.beginPath();
651
+ ctx.fillStyle = shadowColor;
652
+ ctx.arc(0 ,0, this.hubRadius, hubsa, hubea, false);
653
+ ctx.closePath();
654
+ ctx.fill();
655
+ }
656
+ ctx.restore();
657
+
658
+ // draw the outer ring.
659
+ ctx.save();
660
+ ctx.beginPath();
661
+ ctx.strokeStyle = this.ringColor;
662
+ ctx.lineWidth = this.ringWidth;
663
+ ctx.arc(0 ,0, r, outersa, outerea, false);
664
+ ctx.closePath();
665
+ ctx.stroke();
666
+ ctx.restore();
667
+
668
+ // draw the hub
669
+
670
+ ctx.save();
671
+ ctx.beginPath();
672
+ ctx.fillStyle = this.ringColor;
673
+ ctx.arc(0 ,0, this.hubRadius,hubsa, hubea, false);
674
+ ctx.closePath();
675
+ ctx.fill();
676
+ ctx.restore();
677
+
678
+ // draw the ticks
679
+ if (this.showTicks) {
680
+ ctx.save();
681
+ var orad = this.tickOuterRadius,
682
+ tl = this.tickLength,
683
+ mtl = tl/2,
684
+ nmt = this.numberMinorTicks,
685
+ ts = this.span * Math.PI / 180 / (this.ticks.length-1),
686
+ mts = ts/(nmt + 1);
687
+
688
+ for (i = 0; i<this.ticks.length; i++) {
689
+ ctx.beginPath();
690
+ ctx.lineWidth = 1.5 + this.diameter/360;
691
+ ctx.strokeStyle = this.ringColor;
692
+ var wps = ts*i+sa;
693
+ ctx.moveTo(-orad * Math.cos(ts*i+sa), orad * Math.sin(ts*i+sa));
694
+ ctx.lineTo(-(orad-tl) * Math.cos(ts*i+sa), (orad - tl) * Math.sin(ts*i+sa));
695
+ this._tickPoints.push([(orad-tl) * Math.cos(ts*i+sa) + this._center[0] + this.canvas._offsets.left, (orad - tl) * Math.sin(ts*i+sa) + this._center[1] + this.canvas._offsets.top, ts*i+sa]);
696
+ ctx.stroke();
697
+ ctx.lineWidth = 1.0 + this.diameter/440;
698
+ if (i<this.ticks.length-1) {
699
+ for (var j=1; j<=nmt; j++) {
700
+ ctx.beginPath();
701
+ ctx.moveTo(-orad * Math.cos(ts*i+mts*j+sa), orad * Math.sin(ts*i+mts*j+sa));
702
+ ctx.lineTo(-(orad-mtl) * Math.cos(ts*i+mts*j+sa), (orad-mtl) * Math.sin(ts*i+mts*j+sa));
703
+ ctx.stroke();
704
+ }
705
+ }
706
+ }
707
+ ctx.restore();
708
+ }
709
+
710
+ // draw the tick labels
711
+ if (this.showTickLabels) {
712
+ var elem, l, t, ew, eh, dim, maxdim=0;
713
+ var tp = this.tickPadding * (1 - 1/(this.diameter/80+1));
714
+ for (i=0; i<this.ticks.length; i++) {
715
+ elem = $('<div class="jqplot-meterGauge-tick" style="position:absolute;">'+this.ticks[i][1]+'</div>');
716
+ this.canvas._elem.after(elem);
717
+ ew = elem.outerWidth(true);
718
+ eh = elem.outerHeight(true);
719
+ l = this._tickPoints[i][0] - ew * (this._tickPoints[i][2]-Math.PI)/Math.PI - tp * Math.cos(this._tickPoints[i][2]);
720
+ t = this._tickPoints[i][1] - eh/2 + eh/2 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) + tp/3 * Math.pow(Math.abs((Math.sin(this._tickPoints[i][2]))), 0.5) ;
721
+ // t = this._tickPoints[i][1] - eh/2 - eh/2 * Math.sin(this._tickPoints[i][2]) - tp/2 * Math.sin(this._tickPoints[i][2]);
722
+ elem.css({left:l, top:t});
723
+ dim = ew*Math.cos(this._tickPoints[i][2]) + eh*Math.sin(Math.PI/2+this._tickPoints[i][2]/2);
724
+ maxdim = (dim > maxdim) ? dim : maxdim;
725
+ }
726
+ }
727
+
728
+ // draw the gauge label
729
+ if (this.label && this.labelPosition == 'inside') {
730
+ var l = this._center[0] + this.canvas._offsets.left;
731
+ var tp = this.tickPadding * (1 - 1/(this.diameter/80+1));
732
+ var t = 0.5*(this._center[1] + this.canvas._offsets.top - this.hubRadius) + 0.5*(this._center[1] + this.canvas._offsets.top - this.tickOuterRadius + this.tickLength + tp) + this.labelHeightAdjust;
733
+ // this._labelElem = $('<div class="jqplot-meterGauge-label" style="position:absolute;">'+this.label+'</div>');
734
+ // this.canvas._elem.after(this._labelElem);
735
+ l -= this._labelElem.outerWidth(true)/2;
736
+ t -= this._labelElem.outerHeight(true)/2;
737
+ this._labelElem.css({left:l, top:t});
738
+ }
739
+
740
+ else if (this.label && this.labelPosition == 'bottom') {
741
+ var l = this._center[0] + this.canvas._offsets.left - this._labelElem.outerWidth(true)/2;
742
+ var t = this._center[1] + this.canvas._offsets.top + this.innerPad + + this.ringWidth + this.padding + this.labelHeightAdjust;
743
+ this._labelElem.css({left:l, top:t});
744
+
745
+ }
746
+
747
+ // draw the intervals
748
+
749
+ ctx.save();
750
+ var inner = this.intervalInnerRadius || this.hubRadius * 1.5;
751
+ if (this.intervalOuterRadius == null) {
752
+ if (this.showTickLabels) {
753
+ var outer = (this.tickOuterRadius - this.tickLength - this.tickPadding - this.diameter/8);
754
+ }
755
+ else {
756
+ var outer = (this.tickOuterRadius - this.tickLength - this.diameter/16);
757
+ }
758
+ }
759
+ else {
760
+ var outer = this.intervalOuterRadius;
761
+ }
762
+ var range = this.max - this.min;
763
+ var intrange = this.intervals[this.intervals.length-1] - this.min;
764
+ var start, end, span = this.span*Math.PI/180;
765
+ for (i=0; i<this.intervals.length; i++) {
766
+ start = (i == 0) ? sa : sa + (this.intervals[i-1][0] - this.min)*span/range;
767
+ if (start < 0) {
768
+ start = 0;
769
+ }
770
+ end = sa + (this.intervals[i][0] - this.min)*span/range;
771
+ if (end < 0) {
772
+ end = 0;
773
+ }
774
+ ctx.beginPath();
775
+ ctx.fillStyle = this.intervals[i][2];
776
+ ctx.arc(0, 0, inner, start, end, false);
777
+ ctx.lineTo(outer*Math.cos(end), outer*Math.sin(end));
778
+ ctx.arc(0, 0, outer, end, start, true);
779
+ ctx.lineTo(inner*Math.cos(start), inner*Math.sin(start));
780
+ ctx.closePath();
781
+ ctx.fill();
782
+ }
783
+ ctx.restore();
784
+
785
+ // draw the needle
786
+ var datapoint = this.data[0][1];
787
+ var dataspan = this.max - this.min;
788
+ if (this.pegNeedle) {
789
+ if (this.data[0][1] > this.max + dataspan*3/this.span) {
790
+ datapoint = this.max + dataspan*3/this.span;
791
+ }
792
+ if (this.data[0][1] < this.min - dataspan*3/this.span) {
793
+ datapoint = this.min - dataspan*3/this.span;
794
+ }
795
+ }
796
+ var dataang = (datapoint - this.min)/dataspan * this.span * Math.PI/180 + this.startAngle;
797
+
798
+
799
+ ctx.save();
800
+ ctx.beginPath();
801
+ ctx.fillStyle = this.ringColor;
802
+ ctx.strokeStyle = this.ringColor;
803
+ this.needleLength = (this.tickOuterRadius - this.tickLength) * 0.85;
804
+ this.needleThickness = (this.needleThickness < 2) ? 2 : this.needleThickness;
805
+ var endwidth = this.needleThickness * 0.4;
806
+
807
+
808
+ var dl = this.needleLength/10;
809
+ var dt = (this.needleThickness - endwidth)/10;
810
+ var templ;
811
+ for (var i=0; i<10; i++) {
812
+ templ = this.needleThickness - i*dt;
813
+ ctx.moveTo(dl*i*Math.cos(dataang), dl*i*Math.sin(dataang));
814
+ ctx.lineWidth = templ;
815
+ ctx.lineTo(dl*(i+1)*Math.cos(dataang), dl*(i+1)*Math.sin(dataang));
816
+ ctx.stroke();
817
+ }
818
+
819
+ ctx.restore();
820
+ }
821
+ else {
822
+ this._center = [(cw - trans * offx)/2 + trans * offx, (ch - trans*offy)/2 + trans * offy];
823
+ }
824
+ };
825
+
826
+ $.jqplot.MeterGaugeAxisRenderer = function() {
827
+ $.jqplot.LinearAxisRenderer.call(this);
828
+ };
829
+
830
+ $.jqplot.MeterGaugeAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
831
+ $.jqplot.MeterGaugeAxisRenderer.prototype.constructor = $.jqplot.MeterGaugeAxisRenderer;
832
+
833
+
834
+ // There are no traditional axes on a gauge chart. We just need to provide
835
+ // dummy objects with properties so the plot will render.
836
+ // called with scope of axis object.
837
+ $.jqplot.MeterGaugeAxisRenderer.prototype.init = function(options){
838
+ //
839
+ this.tickRenderer = $.jqplot.MeterGaugeTickRenderer;
840
+ $.extend(true, this, options);
841
+ // I don't think I'm going to need _dataBounds here.
842
+ // have to go Axis scaling in a way to fit chart onto plot area
843
+ // and provide u2p and p2u functionality for mouse cursor, etc.
844
+ // for convienence set _dataBounds to 0 and 100 and
845
+ // set min/max to 0 and 100.
846
+ this._dataBounds = {min:0, max:100};
847
+ this.min = 0;
848
+ this.max = 100;
849
+ this.showTicks = false;
850
+ this.ticks = [];
851
+ this.showMark = false;
852
+ this.show = false;
853
+ };
854
+
855
+ $.jqplot.MeterGaugeLegendRenderer = function(){
856
+ $.jqplot.TableLegendRenderer.call(this);
857
+ };
858
+
859
+ $.jqplot.MeterGaugeLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
860
+ $.jqplot.MeterGaugeLegendRenderer.prototype.constructor = $.jqplot.MeterGaugeLegendRenderer;
861
+
862
+ /**
863
+ * Class: $.jqplot.MeterGaugeLegendRenderer
864
+ *Meter gauges don't typically have a legend, this overrides the default legend renderer.
865
+ */
866
+ $.jqplot.MeterGaugeLegendRenderer.prototype.init = function(options) {
867
+ // Maximum number of rows in the legend. 0 or null for unlimited.
868
+ this.numberRows = null;
869
+ // Maximum number of columns in the legend. 0 or null for unlimited.
870
+ this.numberColumns = null;
871
+ $.extend(true, this, options);
872
+ };
873
+
874
+ // called with context of legend
875
+ $.jqplot.MeterGaugeLegendRenderer.prototype.draw = function() {
876
+ if (this.show) {
877
+ var series = this._series;
878
+ var ss = 'position:absolute;';
879
+ ss += (this.background) ? 'background:'+this.background+';' : '';
880
+ ss += (this.border) ? 'border:'+this.border+';' : '';
881
+ ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
882
+ ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
883
+ ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
884
+ ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
885
+ ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
886
+ ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
887
+ ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
888
+ this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
889
+ // MeterGauge charts legends don't go by number of series, but by number of data points
890
+ // in the series. Refactor things here for that.
891
+
892
+ var pad = false,
893
+ reverse = false,
894
+ nr, nc;
895
+ var s = series[0];
896
+
897
+ if (s.show) {
898
+ var pd = s.data;
899
+ if (this.numberRows) {
900
+ nr = this.numberRows;
901
+ if (!this.numberColumns){
902
+ nc = Math.ceil(pd.length/nr);
903
+ }
904
+ else{
905
+ nc = this.numberColumns;
906
+ }
907
+ }
908
+ else if (this.numberColumns) {
909
+ nc = this.numberColumns;
910
+ nr = Math.ceil(pd.length/this.numberColumns);
911
+ }
912
+ else {
913
+ nr = pd.length;
914
+ nc = 1;
915
+ }
916
+
917
+ var i, j, tr, td1, td2, lt, rs, color;
918
+ var idx = 0;
919
+
920
+ for (i=0; i<nr; i++) {
921
+ if (reverse){
922
+ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
923
+ }
924
+ else{
925
+ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
926
+ }
927
+ for (j=0; j<nc; j++) {
928
+ if (idx < pd.length){
929
+ // debugger
930
+ lt = this.labels[idx] || pd[idx][0].toString();
931
+ color = s.color;
932
+ if (!reverse){
933
+ if (i>0){
934
+ pad = true;
935
+ }
936
+ else{
937
+ pad = false;
938
+ }
939
+ }
940
+ else{
941
+ if (i == nr -1){
942
+ pad = false;
943
+ }
944
+ else{
945
+ pad = true;
946
+ }
947
+ }
948
+ rs = (pad) ? this.rowSpacing : '0';
949
+
950
+ td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
951
+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
952
+ '</div></td>');
953
+ td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
954
+ if (this.escapeHtml){
955
+ td2.text(lt);
956
+ }
957
+ else {
958
+ td2.html(lt);
959
+ }
960
+ if (reverse) {
961
+ td2.prependTo(tr);
962
+ td1.prependTo(tr);
963
+ }
964
+ else {
965
+ td1.appendTo(tr);
966
+ td2.appendTo(tr);
967
+ }
968
+ pad = true;
969
+ }
970
+ idx++;
971
+ }
972
+ }
973
+ }
974
+ }
975
+ return this._elem;
976
+ };
977
+
978
+
979
+ // setup default renderers for axes and legend so user doesn't have to
980
+ // called with scope of plot
981
+ function preInit(target, data, options) {
982
+ // debugger
983
+ options = options || {};
984
+ options.axesDefaults = options.axesDefaults || {};
985
+ options.legend = options.legend || {};
986
+ options.seriesDefaults = options.seriesDefaults || {};
987
+ options.grid = options.grid || {};
988
+
989
+ // only set these if there is a gauge series
990
+ var setopts = false;
991
+ if (options.seriesDefaults.renderer == $.jqplot.MeterGaugeRenderer) {
992
+ setopts = true;
993
+ }
994
+ else if (options.series) {
995
+ for (var i=0; i < options.series.length; i++) {
996
+ if (options.series[i].renderer == $.jqplot.MeterGaugeRenderer) {
997
+ setopts = true;
998
+ }
999
+ }
1000
+ }
1001
+
1002
+ if (setopts) {
1003
+ options.axesDefaults.renderer = $.jqplot.MeterGaugeAxisRenderer;
1004
+ options.legend.renderer = $.jqplot.MeterGaugeLegendRenderer;
1005
+ options.legend.preDraw = true;
1006
+ options.grid.background = options.grid.background || 'white';
1007
+ options.grid.drawGridlines = false;
1008
+ options.grid.borderWidth = (options.grid.borderWidth != null) ? options.grid.borderWidth : 0;
1009
+ options.grid.shadow = (options.grid.shadow != null) ? options.grid.shadow : false;
1010
+ }
1011
+ }
1012
+
1013
+ // called with scope of plot
1014
+ function postParseOptions(options) {
1015
+ //
1016
+ }
1017
+
1018
+ $.jqplot.preInitHooks.push(preInit);
1019
+ $.jqplot.postParseOptionsHooks.push(postParseOptions);
1020
+
1021
+ $.jqplot.MeterGaugeTickRenderer = function() {
1022
+ $.jqplot.AxisTickRenderer.call(this);
1023
+ };
1024
+
1025
+ $.jqplot.MeterGaugeTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
1026
+ $.jqplot.MeterGaugeTickRenderer.prototype.constructor = $.jqplot.MeterGaugeTickRenderer;
1027
+
1028
+ })(jQuery);
1029
+
1030
+