jquery_cheats 3.0.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/app/assets/javascripts/{jqueryCheats.js → jquery_cheats/jquery_cheats.js} +0 -0
  2. data/jquery_cheats.gemspec +1 -1
  3. data/lib/jquery_cheats/engine.rb +6 -0
  4. data/lib/{railtie.rb → jquery_cheats/railtie.rb} +0 -0
  5. data/lib/jquery_cheats.rb +2 -2
  6. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/excanvas.js +1438 -1438
  7. data/vendor/assets/javascripts/jquery_cheats/jqplot/index.js +1 -0
  8. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/jquery.jqplot.js +10901 -10901
  9. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.BezierCurveRenderer.js +311 -311
  10. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.BezierCurveRenderer.min.js +56 -56
  11. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.barRenderer.js +746 -746
  12. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.barRenderer.min.js +56 -56
  13. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.blockRenderer.js +233 -233
  14. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.blockRenderer.min.js +56 -56
  15. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.bubbleRenderer.js +753 -753
  16. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.bubbleRenderer.min.js +56 -56
  17. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasAxisLabelRenderer.js +201 -201
  18. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasAxisLabelRenderer.min.js +56 -56
  19. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasAxisTickRenderer.js +241 -241
  20. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasAxisTickRenderer.min.js +56 -56
  21. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasOverlay.js +863 -863
  22. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasOverlay.min.js +56 -56
  23. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasTextRenderer.js +447 -447
  24. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.canvasTextRenderer.min.js +56 -56
  25. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.categoryAxisRenderer.js +635 -635
  26. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.categoryAxisRenderer.min.js +56 -56
  27. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.ciParser.js +114 -114
  28. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.ciParser.min.js +56 -56
  29. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.cursor.js +1092 -1092
  30. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.cursor.min.js +56 -56
  31. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.dateAxisRenderer.js +702 -702
  32. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.dateAxisRenderer.min.js +56 -56
  33. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.donutRenderer.js +799 -799
  34. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.donutRenderer.min.js +56 -56
  35. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.dragable.js +223 -223
  36. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.dragable.min.js +56 -56
  37. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.enhancedLegendRenderer.js +240 -240
  38. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.enhancedLegendRenderer.min.js +56 -56
  39. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.funnelRenderer.js +937 -937
  40. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.funnelRenderer.min.js +56 -56
  41. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.highlighter.js +453 -453
  42. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.highlighter.min.js +56 -56
  43. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.json2.js +475 -475
  44. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.json2.min.js +56 -56
  45. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.logAxisRenderer.js +527 -527
  46. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.logAxisRenderer.min.js +56 -56
  47. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.mekkoAxisRenderer.js +610 -610
  48. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.mekkoAxisRenderer.min.js +56 -56
  49. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.mekkoRenderer.js +435 -435
  50. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.mekkoRenderer.min.js +56 -56
  51. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.meterGaugeRenderer.js +1028 -1028
  52. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.meterGaugeRenderer.min.js +56 -56
  53. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.ohlcRenderer.js +371 -371
  54. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.ohlcRenderer.min.js +56 -56
  55. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pieRenderer.js +898 -898
  56. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pieRenderer.min.js +56 -56
  57. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pointLabels.js +361 -361
  58. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pointLabels.min.js +56 -56
  59. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pyramidAxisRenderer.js +729 -729
  60. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pyramidAxisRenderer.min.js +56 -56
  61. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pyramidGridRenderer.js +422 -422
  62. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pyramidGridRenderer.min.js +56 -56
  63. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pyramidRenderer.js +489 -489
  64. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.pyramidRenderer.min.js +56 -56
  65. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.trendline.js +221 -221
  66. data/vendor/assets/javascripts/{jqplot → jquery_cheats/jqplot}/plugins/jqplot.trendline.min.js +56 -56
  67. data/vendor/assets/stylesheets/{jquery.jqplot.css → jquery_cheats/jquery.jqplot.css} +259 -259
  68. metadata +68 -67
  69. data/lib/engine.rb +0 -7
@@ -1,938 +1,938 @@
1
- /**
2
- * jqPlot
3
- * Pure JavaScript plotting plugin using jQuery
4
- *
5
- * Version: 1.0.0b2_r1012
6
- *
7
- * Copyright (c) 2009-2011 Chris Leonello
8
- * jqPlot is currently available for use in all personal or commercial projects
9
- * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
10
- * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
11
- * choose the license that best suits your project and use it accordingly.
12
- *
13
- * Although not required, the author would appreciate an email letting him
14
- * know of any substantial use of jqPlot. You can reach the author at:
15
- * chris at jqplot dot com or see http://www.jqplot.com/info.php .
16
- *
17
- * If you are feeling kind and generous, consider supporting the project by
18
- * making a donation at: http://www.jqplot.com/donate.php .
19
- *
20
- * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
21
- *
22
- * version 2007.04.27
23
- * author Ash Searle
24
- * http://hexmen.com/blog/2007/03/printf-sprintf/
25
- * http://hexmen.com/js/sprintf.js
26
- * The author (Ash Searle) has placed this code in the public domain:
27
- * "This code is unrestricted: you are free to use it however you like."
28
- *
29
- */
30
- (function($) {
31
- /**
32
- * Class: $.jqplot.FunnelRenderer
33
- * Plugin renderer to draw a funnel chart.
34
- * x values, if present, will be used as labels.
35
- * y values give area size.
36
- *
37
- * Funnel charts will draw a single series
38
- * only.
39
- *
40
- * To use this renderer, you need to include the
41
- * funnel renderer plugin, for example:
42
- *
43
- * > <script type="text/javascript" src="plugins/jqplot.funnelRenderer.js"></script>
44
- *
45
- * Properties described here are passed into the $.jqplot function
46
- * as options on the series renderer. For example:
47
- *
48
- * > plot2 = $.jqplot('chart2', [s1, s2], {
49
- * > seriesDefaults: {
50
- * > renderer:$.jqplot.FunnelRenderer,
51
- * > rendererOptions:{
52
- * > sectionMargin: 12,
53
- * > widthRatio: 0.3
54
- * > }
55
- * > }
56
- * > });
57
- *
58
- * IMPORTANT
59
- *
60
- * *The funnel renderer will reorder data in descending order* so the largest value in
61
- * the data set is first and displayed on top of the funnel. Data will then
62
- * be displayed in descending order down the funnel. The area of each funnel
63
- * section will correspond to the value of each data point relative to the sum
64
- * of all values. That is section area is proportional to section value divided by
65
- * sum of all section values.
66
- *
67
- * If your data is not in descending order when passed into the plot, *it will be
68
- * reordered* when stored in the series.data property. A copy of the unordered
69
- * data is kept in the series._unorderedData property.
70
- *
71
- * A funnel plot will trigger events on the plot target
72
- * according to user interaction. All events return the event object,
73
- * the series index, the point (section) index, and the point data for
74
- * the appropriate section. *Note* the point index will referr to the ordered
75
- * data, not the original unordered data.
76
- *
77
- * 'jqplotDataMouseOver' - triggered when mousing over a section.
78
- * 'jqplotDataHighlight' - triggered the first time user mouses over a section,
79
- * if highlighting is enabled.
80
- * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
81
- * a highlighted section.
82
- * 'jqplotDataClick' - triggered when the user clicks on a section.
83
- * 'jqplotDataRightClick' - tiggered when the user right clicks on a section if
84
- * the "captureRightClick" option is set to true on the plot.
85
- */
86
- $.jqplot.FunnelRenderer = function(){
87
- $.jqplot.LineRenderer.call(this);
88
- };
89
-
90
- $.jqplot.FunnelRenderer.prototype = new $.jqplot.LineRenderer();
91
- $.jqplot.FunnelRenderer.prototype.constructor = $.jqplot.FunnelRenderer;
92
-
93
- // called with scope of a series
94
- $.jqplot.FunnelRenderer.prototype.init = function(options, plot) {
95
- // Group: Properties
96
- //
97
- // prop: padding
98
- // padding between the funnel and plot edges, legend, etc.
99
- this.padding = {top: 20, right: 20, bottom: 20, left: 20};
100
- // prop: sectionMargin
101
- // spacing between funnel sections in pixels.
102
- this.sectionMargin = 6;
103
- // prop: fill
104
- // true or false, wether to fill the areas.
105
- this.fill = true;
106
- // prop: shadowOffset
107
- // offset of the shadow from the area and offset of
108
- // each succesive stroke of the shadow from the last.
109
- this.shadowOffset = 2;
110
- // prop: shadowAlpha
111
- // transparency of the shadow (0 = transparent, 1 = opaque)
112
- this.shadowAlpha = 0.07;
113
- // prop: shadowDepth
114
- // number of strokes to apply to the shadow,
115
- // each stroke offset shadowOffset from the last.
116
- this.shadowDepth = 5;
117
- // prop: highlightMouseOver
118
- // True to highlight area when moused over.
119
- // This must be false to enable highlightMouseDown to highlight when clicking on a area.
120
- this.highlightMouseOver = true;
121
- // prop: highlightMouseDown
122
- // True to highlight when a mouse button is pressed over a area.
123
- // This will be disabled if highlightMouseOver is true.
124
- this.highlightMouseDown = false;
125
- // prop: highlightColors
126
- // array of colors to use when highlighting an area.
127
- this.highlightColors = [];
128
- // prop: widthRatio
129
- // The ratio of the width of the top of the funnel to the bottom.
130
- // a ratio of 0 will make an upside down pyramid.
131
- this.widthRatio = 0.2;
132
- // prop: lineWidth
133
- // width of line if areas are stroked and not filled.
134
- this.lineWidth = 2;
135
- // prop: dataLabels
136
- // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
137
- // Defaults to percentage of each pie slice.
138
- this.dataLabels = 'percent';
139
- // prop: showDataLabels
140
- // true to show data labels on slices.
141
- this.showDataLabels = false;
142
- // prop: dataLabelFormatString
143
- // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
144
- this.dataLabelFormatString = null;
145
- // prop: dataLabelThreshold
146
- // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed.
147
- // This applies to all label types, not just to percentage labels.
148
- this.dataLabelThreshold = 3;
149
- this._type = 'funnel';
150
-
151
- this.tickRenderer = $.jqplot.FunnelTickRenderer;
152
-
153
- // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
154
- if (options.highlightMouseDown && options.highlightMouseOver == null) {
155
- options.highlightMouseOver = false;
156
- }
157
-
158
- $.extend(true, this, options);
159
-
160
- // index of the currenty highlighted point, if any
161
- this._highlightedPoint = null;
162
-
163
- // lengths of bases, or horizontal sides of areas of trapezoid.
164
- this._bases = [];
165
- // total area
166
- this._atot;
167
- // areas of segments.
168
- this._areas = [];
169
- // vertical lengths of segments.
170
- this._lengths = [];
171
- // angle of the funnel to vertical.
172
- this._angle;
173
- this._dataIndices = [];
174
-
175
- // sort data
176
- this._unorderedData = $.extend(true, [], this.data);
177
- var idxs = $.extend(true, [], this.data);
178
- for (var i=0; i<idxs.length; i++) {
179
- idxs[i].push(i);
180
- }
181
- this.data.sort( function (a, b) { return b[1] - a[1]; } );
182
- idxs.sort( function (a, b) { return b[1] - a[1]; });
183
- for (var i=0; i<idxs.length; i++) {
184
- this._dataIndices.push(idxs[i][2]);
185
- }
186
-
187
- // set highlight colors if none provided
188
- if (this.highlightColors.length == 0) {
189
- for (var i=0; i<this.seriesColors.length; i++){
190
- var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
191
- var newrgb = [rgba[0], rgba[1], rgba[2]];
192
- var sum = newrgb[0] + newrgb[1] + newrgb[2];
193
- for (var j=0; j<3; j++) {
194
- // when darkening, lowest color component can be is 60.
195
- newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.4 * (255 - newrgb[j]);
196
- newrgb[j] = parseInt(newrgb[j], 10);
197
- }
198
- this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
199
- }
200
- }
201
-
202
- plot.postParseOptionsHooks.addOnce(postParseOptions);
203
- plot.postInitHooks.addOnce(postInit);
204
- plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
205
- plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
206
- plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
207
- plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
208
- plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
209
- plot.postDrawHooks.addOnce(postPlotDraw);
210
-
211
- };
212
-
213
- // gridData will be of form [label, percentage of total]
214
- $.jqplot.FunnelRenderer.prototype.setGridData = function(plot) {
215
- // set gridData property. This will hold angle in radians of each data point.
216
- var sum = 0;
217
- var td = [];
218
- for (var i=0; i<this.data.length; i++){
219
- sum += this.data[i][1];
220
- td.push([this.data[i][0], this.data[i][1]]);
221
- }
222
-
223
- // normalize y values, so areas are proportional.
224
- for (var i=0; i<td.length; i++) {
225
- td[i][1] = td[i][1]/sum;
226
- }
227
-
228
- this._bases = new Array(td.length + 1);
229
- this._lengths = new Array(td.length);
230
-
231
- this.gridData = td;
232
- };
233
-
234
- $.jqplot.FunnelRenderer.prototype.makeGridData = function(data, plot) {
235
- // set gridData property. This will hold angle in radians of each data point.
236
- var sum = 0;
237
- var td = [];
238
- for (var i=0; i<this.data.length; i++){
239
- sum += this.data[i][1];
240
- td.push([this.data[i][0], this.data[i][1]]);
241
- }
242
-
243
- // normalize y values, so areas are proportional.
244
- for (var i=0; i<td.length; i++) {
245
- td[i][1] = td[i][1]/sum;
246
- }
247
-
248
- this._bases = new Array(td.length + 1);
249
- this._lengths = new Array(td.length);
250
-
251
- return td;
252
- };
253
-
254
- $.jqplot.FunnelRenderer.prototype.drawSection = function (ctx, vertices, color, isShadow) {
255
- var fill = this.fill;
256
- var lineWidth = this.lineWidth;
257
- ctx.save();
258
-
259
- if (isShadow) {
260
- for (var i=0; i<this.shadowDepth; i++) {
261
- ctx.save();
262
- ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
263
- doDraw();
264
- }
265
- }
266
-
267
- else {
268
- doDraw();
269
- }
270
-
271
- function doDraw () {
272
- ctx.beginPath();
273
- ctx.fillStyle = color;
274
- ctx.strokeStyle = color;
275
- ctx.lineWidth = lineWidth;
276
- ctx.moveTo(vertices[0][0], vertices[0][1]);
277
- for (var i=1; i<4; i++) {
278
- ctx.lineTo(vertices[i][0], vertices[i][1]);
279
- }
280
- ctx.closePath();
281
- if (fill) {
282
- ctx.fill();
283
- }
284
- else {
285
- ctx.stroke();
286
- }
287
- }
288
-
289
- if (isShadow) {
290
- for (var i=0; i<this.shadowDepth; i++) {
291
- ctx.restore();
292
- }
293
- }
294
-
295
- ctx.restore();
296
- };
297
-
298
- // called with scope of series
299
- $.jqplot.FunnelRenderer.prototype.draw = function (ctx, gd, options, plot) {
300
- var i;
301
- var opts = (options != undefined) ? options : {};
302
- // offset and direction of offset due to legend placement
303
- var offx = 0;
304
- var offy = 0;
305
- var trans = 1;
306
- this._areas = [];
307
- // var colorGenerator = new this.colorGenerator(this.seriesColors);
308
- if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
309
- var li = options.legendInfo;
310
- switch (li.location) {
311
- case 'nw':
312
- offx = li.width + li.xoffset;
313
- break;
314
- case 'w':
315
- offx = li.width + li.xoffset;
316
- break;
317
- case 'sw':
318
- offx = li.width + li.xoffset;
319
- break;
320
- case 'ne':
321
- offx = li.width + li.xoffset;
322
- trans = -1;
323
- break;
324
- case 'e':
325
- offx = li.width + li.xoffset;
326
- trans = -1;
327
- break;
328
- case 'se':
329
- offx = li.width + li.xoffset;
330
- trans = -1;
331
- break;
332
- case 'n':
333
- offy = li.height + li.yoffset;
334
- break;
335
- case 's':
336
- offy = li.height + li.yoffset;
337
- trans = -1;
338
- break;
339
- default:
340
- break;
341
- }
342
- }
343
-
344
- var loff = (trans==1) ? this.padding.left + offx : this.padding.left;
345
- var toff = (trans==1) ? this.padding.top + offy : this.padding.top;
346
- var roff = (trans==-1) ? this.padding.right + offx : this.padding.right;
347
- var boff = (trans==-1) ? this.padding.bottom + offy : this.padding.bottom;
348
-
349
- var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
350
- var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
351
- var fill = (opts.fill != undefined) ? opts.fill : this.fill;
352
- var cw = ctx.canvas.width;
353
- var ch = ctx.canvas.height;
354
- this._bases[0] = cw - loff - roff;
355
- var ltot = this._length = ch - toff - boff;
356
-
357
- var hend = this._bases[0]*this.widthRatio;
358
- this._atot = ltot/2 * (this._bases[0] + this._bases[0]*this.widthRatio);
359
-
360
- this._angle = Math.atan((this._bases[0] - hend)/2/ltot);
361
-
362
- for (i=0; i<gd.length; i++) {
363
- this._areas.push(gd[i][1] * this._atot);
364
- }
365
-
366
-
367
- var guess, err, count, lsum=0;
368
- var tolerance = 0.0001;
369
-
370
- for (i=0; i<this._areas.length; i++) {
371
- guess = this._areas[i]/this._bases[i];
372
- err = 999999;
373
- this._lengths[i] = guess;
374
- count = 0;
375
- while (err > this._lengths[i]*tolerance && count < 100) {
376
- this._lengths[i] = this._areas[i]/(this._bases[i] - this._lengths[i] * Math.tan(this._angle));
377
- err = Math.abs(this._lengths[i] - guess);
378
- this._bases[i+1] = this._bases[i] - (2*this._lengths[i]*Math.tan(this._angle));
379
- guess = this._lengths[i];
380
- count++;
381
- }
382
- lsum += this._lengths[i];
383
- }
384
-
385
- // figure out vertices of each section
386
- this._vertices = new Array(gd.length);
387
-
388
- // these are 4 coners of entire trapezoid
389
- var p0 = [loff, toff],
390
- p1 = [loff+this._bases[0], toff],
391
- p2 = [loff + (this._bases[0] - this._bases[this._bases.length-1])/2, toff + this._length],
392
- p3 = [p2[0] + this._bases[this._bases.length-1], p2[1]];
393
-
394
- // equations of right and left sides, returns x, y values given height of section (y value)
395
- function findleft (l) {
396
- var m = (p0[1] - p2[1])/(p0[0] - p2[0]);
397
- var b = p0[1] - m*p0[0];
398
- var y = l + p0[1];
399
-
400
- return [(y - b)/m, y];
401
- }
402
-
403
- function findright (l) {
404
- var m = (p1[1] - p3[1])/(p1[0] - p3[0]);
405
- var b = p1[1] - m*p1[0];
406
- var y = l + p1[1];
407
-
408
- return [(y - b)/m, y];
409
- }
410
-
411
- var x = offx, y = offy;
412
- var h=0, adj=0;
413
-
414
- for (i=0; i<gd.length; i++) {
415
- this._vertices[i] = new Array();
416
- var v = this._vertices[i];
417
- var sm = this.sectionMargin;
418
- if (i == 0) {
419
- adj = 0;
420
- }
421
- if (i == 1) {
422
- adj = sm/3;
423
- }
424
- else if (i > 0 && i < gd.length-1) {
425
- adj = sm/2;
426
- }
427
- else if (i == gd.length -1) {
428
- adj = 2*sm/3;
429
- }
430
- v.push(findleft(h+adj));
431
- v.push(findright(h+adj));
432
- h += this._lengths[i];
433
- if (i == 0) {
434
- adj = -2*sm/3;
435
- }
436
- else if (i > 0 && i < gd.length-1) {
437
- adj = -sm/2;
438
- }
439
- else if (i == gd.length - 1) {
440
- adj = 0;
441
- }
442
- v.push(findright(h+adj));
443
- v.push(findleft(h+adj));
444
-
445
- }
446
-
447
- if (this.shadow) {
448
- var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
449
- for (var i=0; i<gd.length; i++) {
450
- this.renderer.drawSection.call (this, ctx, this._vertices[i], shadowColor, true);
451
- }
452
-
453
- }
454
- for (var i=0; i<gd.length; i++) {
455
- var v = this._vertices[i];
456
- this.renderer.drawSection.call (this, ctx, v, this.seriesColors[i]);
457
-
458
- if (this.showDataLabels && gd[i][1]*100 >= this.dataLabelThreshold) {
459
- var fstr, label;
460
-
461
- if (this.dataLabels == 'label') {
462
- fstr = this.dataLabelFormatString || '%s';
463
- label = $.jqplot.sprintf(fstr, gd[i][0]);
464
- }
465
- else if (this.dataLabels == 'value') {
466
- fstr = this.dataLabelFormatString || '%d';
467
- label = $.jqplot.sprintf(fstr, this.data[i][1]);
468
- }
469
- else if (this.dataLabels == 'percent') {
470
- fstr = this.dataLabelFormatString || '%d%%';
471
- label = $.jqplot.sprintf(fstr, gd[i][1]*100);
472
- }
473
- else if (this.dataLabels.constructor == Array) {
474
- fstr = this.dataLabelFormatString || '%s';
475
- label = $.jqplot.sprintf(fstr, this.dataLabels[this._dataIndices[i]]);
476
- }
477
-
478
- var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
479
-
480
- var x = (v[0][0] + v[1][0])/2 + this.canvas._offsets.left;
481
- var y = (v[1][1] + v[2][1])/2 + this.canvas._offsets.top;
482
-
483
- var labelelem = $('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem);
484
- x -= labelelem.width()/2;
485
- y -= labelelem.height()/2;
486
- x = Math.round(x);
487
- y = Math.round(y);
488
- labelelem.css({left: x, top: y});
489
- }
490
-
491
- }
492
-
493
- };
494
-
495
- $.jqplot.FunnelAxisRenderer = function() {
496
- $.jqplot.LinearAxisRenderer.call(this);
497
- };
498
-
499
- $.jqplot.FunnelAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
500
- $.jqplot.FunnelAxisRenderer.prototype.constructor = $.jqplot.FunnelAxisRenderer;
501
-
502
-
503
- // There are no traditional axes on a funnel chart. We just need to provide
504
- // dummy objects with properties so the plot will render.
505
- // called with scope of axis object.
506
- $.jqplot.FunnelAxisRenderer.prototype.init = function(options){
507
- //
508
- this.tickRenderer = $.jqplot.FunnelTickRenderer;
509
- $.extend(true, this, options);
510
- // I don't think I'm going to need _dataBounds here.
511
- // have to go Axis scaling in a way to fit chart onto plot area
512
- // and provide u2p and p2u functionality for mouse cursor, etc.
513
- // for convienence set _dataBounds to 0 and 100 and
514
- // set min/max to 0 and 100.
515
- this._dataBounds = {min:0, max:100};
516
- this.min = 0;
517
- this.max = 100;
518
- this.showTicks = false;
519
- this.ticks = [];
520
- this.showMark = false;
521
- this.show = false;
522
- };
523
-
524
-
525
-
526
- /**
527
- * Class: $.jqplot.FunnelLegendRenderer
528
- * Legend Renderer specific to funnel plots. Set by default
529
- * when the user creates a funnel plot.
530
- */
531
- $.jqplot.FunnelLegendRenderer = function(){
532
- $.jqplot.TableLegendRenderer.call(this);
533
- };
534
-
535
- $.jqplot.FunnelLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
536
- $.jqplot.FunnelLegendRenderer.prototype.constructor = $.jqplot.FunnelLegendRenderer;
537
-
538
- $.jqplot.FunnelLegendRenderer.prototype.init = function(options) {
539
- // Group: Properties
540
- //
541
- // prop: numberRows
542
- // Maximum number of rows in the legend. 0 or null for unlimited.
543
- this.numberRows = null;
544
- // prop: numberColumns
545
- // Maximum number of columns in the legend. 0 or null for unlimited.
546
- this.numberColumns = null;
547
- $.extend(true, this, options);
548
- };
549
-
550
- // called with context of legend
551
- $.jqplot.FunnelLegendRenderer.prototype.draw = function() {
552
- var legend = this;
553
- if (this.show) {
554
- var series = this._series;
555
- var ss = 'position:absolute;';
556
- ss += (this.background) ? 'background:'+this.background+';' : '';
557
- ss += (this.border) ? 'border:'+this.border+';' : '';
558
- ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
559
- ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
560
- ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
561
- ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
562
- ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
563
- ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
564
- ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
565
- this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
566
- // Funnel charts legends don't go by number of series, but by number of data points
567
- // in the series. Refactor things here for that.
568
-
569
- var pad = false,
570
- reverse = false,
571
- nr, nc;
572
- var s = series[0];
573
- var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
574
-
575
- if (s.show) {
576
- var pd = s.data;
577
- if (this.numberRows) {
578
- nr = this.numberRows;
579
- if (!this.numberColumns){
580
- nc = Math.ceil(pd.length/nr);
581
- }
582
- else{
583
- nc = this.numberColumns;
584
- }
585
- }
586
- else if (this.numberColumns) {
587
- nc = this.numberColumns;
588
- nr = Math.ceil(pd.length/this.numberColumns);
589
- }
590
- else {
591
- nr = pd.length;
592
- nc = 1;
593
- }
594
-
595
- var i, j, tr, td1, td2, lt, rs, color;
596
- var idx = 0;
597
-
598
- for (i=0; i<nr; i++) {
599
- if (reverse){
600
- tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
601
- }
602
- else{
603
- tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
604
- }
605
- for (j=0; j<nc; j++) {
606
- if (idx < pd.length){
607
- lt = this.labels[idx] || pd[idx][0].toString();
608
- color = colorGenerator.next();
609
- if (!reverse){
610
- if (i>0){
611
- pad = true;
612
- }
613
- else{
614
- pad = false;
615
- }
616
- }
617
- else{
618
- if (i == nr -1){
619
- pad = false;
620
- }
621
- else{
622
- pad = true;
623
- }
624
- }
625
- rs = (pad) ? this.rowSpacing : '0';
626
-
627
- td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
628
- '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
629
- '</div></td>');
630
- td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
631
- if (this.escapeHtml){
632
- td2.text(lt);
633
- }
634
- else {
635
- td2.html(lt);
636
- }
637
- if (reverse) {
638
- td2.prependTo(tr);
639
- td1.prependTo(tr);
640
- }
641
- else {
642
- td1.appendTo(tr);
643
- td2.appendTo(tr);
644
- }
645
- pad = true;
646
- }
647
- idx++;
648
- }
649
- }
650
- }
651
- }
652
- return this._elem;
653
- };
654
-
655
- // $.jqplot.FunnelLegendRenderer.prototype.pack = function(offsets) {
656
- // if (this.show) {
657
- // // fake a grid for positioning
658
- // var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};
659
- // if (this.placement == 'insideGrid') {
660
- // switch (this.location) {
661
- // case 'nw':
662
- // var a = grid._left + this.xoffset;
663
- // var b = grid._top + this.yoffset;
664
- // this._elem.css('left', a);
665
- // this._elem.css('top', b);
666
- // break;
667
- // case 'n':
668
- // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
669
- // var b = grid._top + this.yoffset;
670
- // this._elem.css('left', a);
671
- // this._elem.css('top', b);
672
- // break;
673
- // case 'ne':
674
- // var a = offsets.right + this.xoffset;
675
- // var b = grid._top + this.yoffset;
676
- // this._elem.css({right:a, top:b});
677
- // break;
678
- // case 'e':
679
- // var a = offsets.right + this.xoffset;
680
- // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
681
- // this._elem.css({right:a, top:b});
682
- // break;
683
- // case 'se':
684
- // var a = offsets.right + this.xoffset;
685
- // var b = offsets.bottom + this.yoffset;
686
- // this._elem.css({right:a, bottom:b});
687
- // break;
688
- // case 's':
689
- // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
690
- // var b = offsets.bottom + this.yoffset;
691
- // this._elem.css({left:a, bottom:b});
692
- // break;
693
- // case 'sw':
694
- // var a = grid._left + this.xoffset;
695
- // var b = offsets.bottom + this.yoffset;
696
- // this._elem.css({left:a, bottom:b});
697
- // break;
698
- // case 'w':
699
- // var a = grid._left + this.xoffset;
700
- // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
701
- // this._elem.css({left:a, top:b});
702
- // break;
703
- // default: // same as 'se'
704
- // var a = grid._right - this.xoffset;
705
- // var b = grid._bottom + this.yoffset;
706
- // this._elem.css({right:a, bottom:b});
707
- // break;
708
- // }
709
- //
710
- // }
711
- // else {
712
- // switch (this.location) {
713
- // case 'nw':
714
- // var a = this._plotDimensions.width - grid._left + this.xoffset;
715
- // var b = grid._top + this.yoffset;
716
- // this._elem.css('right', a);
717
- // this._elem.css('top', b);
718
- // break;
719
- // case 'n':
720
- // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
721
- // var b = this._plotDimensions.height - grid._top + this.yoffset;
722
- // this._elem.css('left', a);
723
- // this._elem.css('bottom', b);
724
- // break;
725
- // case 'ne':
726
- // var a = this._plotDimensions.width - offsets.right + this.xoffset;
727
- // var b = grid._top + this.yoffset;
728
- // this._elem.css({left:a, top:b});
729
- // break;
730
- // case 'e':
731
- // var a = this._plotDimensions.width - offsets.right + this.xoffset;
732
- // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
733
- // this._elem.css({left:a, top:b});
734
- // break;
735
- // case 'se':
736
- // var a = this._plotDimensions.width - offsets.right + this.xoffset;
737
- // var b = offsets.bottom + this.yoffset;
738
- // this._elem.css({left:a, bottom:b});
739
- // break;
740
- // case 's':
741
- // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
742
- // var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
743
- // this._elem.css({left:a, top:b});
744
- // break;
745
- // case 'sw':
746
- // var a = this._plotDimensions.width - grid._left + this.xoffset;
747
- // var b = offsets.bottom + this.yoffset;
748
- // this._elem.css({right:a, bottom:b});
749
- // break;
750
- // case 'w':
751
- // var a = this._plotDimensions.width - grid._left + this.xoffset;
752
- // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
753
- // this._elem.css({right:a, top:b});
754
- // break;
755
- // default: // same as 'se'
756
- // var a = grid._right - this.xoffset;
757
- // var b = grid._bottom + this.yoffset;
758
- // this._elem.css({right:a, bottom:b});
759
- // break;
760
- // }
761
- // }
762
- // }
763
- // };
764
-
765
- // setup default renderers for axes and legend so user doesn't have to
766
- // called with scope of plot
767
- function preInit(target, data, options) {
768
- options = options || {};
769
- options.axesDefaults = options.axesDefaults || {};
770
- options.legend = options.legend || {};
771
- options.seriesDefaults = options.seriesDefaults || {};
772
- // only set these if there is a funnel series
773
- var setopts = false;
774
- if (options.seriesDefaults.renderer == $.jqplot.FunnelRenderer) {
775
- setopts = true;
776
- }
777
- else if (options.series) {
778
- for (var i=0; i < options.series.length; i++) {
779
- if (options.series[i].renderer == $.jqplot.FunnelRenderer) {
780
- setopts = true;
781
- }
782
- }
783
- }
784
-
785
- if (setopts) {
786
- options.axesDefaults.renderer = $.jqplot.FunnelAxisRenderer;
787
- options.legend.renderer = $.jqplot.FunnelLegendRenderer;
788
- options.legend.preDraw = true;
789
- options.sortData = false;
790
- options.seriesDefaults.pointLabels = {show: false};
791
- }
792
- }
793
-
794
- function postInit(target, data, options) {
795
- // if multiple series, add a reference to the previous one so that
796
- // funnel rings can nest.
797
- for (var i=0; i<this.series.length; i++) {
798
- if (this.series[i].renderer.constructor == $.jqplot.FunnelRenderer) {
799
- // don't allow mouseover and mousedown at same time.
800
- if (this.series[i].highlightMouseOver) {
801
- this.series[i].highlightMouseDown = false;
802
- }
803
- }
804
- }
805
- }
806
-
807
- // called with scope of plot
808
- function postParseOptions(options) {
809
- for (var i=0; i<this.series.length; i++) {
810
- this.series[i].seriesColors = this.seriesColors;
811
- this.series[i].colorGenerator = $.jqplot.colorGenerator;
812
- }
813
- }
814
-
815
- function highlight (plot, sidx, pidx) {
816
- var s = plot.series[sidx];
817
- var canvas = plot.plugins.funnelRenderer.highlightCanvas;
818
- canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
819
- s._highlightedPoint = pidx;
820
- plot.plugins.funnelRenderer.highlightedSeriesIndex = sidx;
821
- s.renderer.drawSection.call(s, canvas._ctx, s._vertices[pidx], s.highlightColors[pidx], false);
822
- }
823
-
824
- function unhighlight (plot) {
825
- var canvas = plot.plugins.funnelRenderer.highlightCanvas;
826
- canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
827
- for (var i=0; i<plot.series.length; i++) {
828
- plot.series[i]._highlightedPoint = null;
829
- }
830
- plot.plugins.funnelRenderer.highlightedSeriesIndex = null;
831
- plot.target.trigger('jqplotDataUnhighlight');
832
- }
833
-
834
- function handleMove(ev, gridpos, datapos, neighbor, plot) {
835
- if (neighbor) {
836
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
837
- var evt1 = jQuery.Event('jqplotDataMouseOver');
838
- evt1.pageX = ev.pageX;
839
- evt1.pageY = ev.pageY;
840
- plot.target.trigger(evt1, ins);
841
- if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
842
- var evt = jQuery.Event('jqplotDataHighlight');
843
- evt.pageX = ev.pageX;
844
- evt.pageY = ev.pageY;
845
- plot.target.trigger(evt, ins);
846
- highlight (plot, ins[0], ins[1]);
847
- }
848
- }
849
- else if (neighbor == null) {
850
- unhighlight (plot);
851
- }
852
- }
853
-
854
- function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
855
- if (neighbor) {
856
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
857
- if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
858
- var evt = jQuery.Event('jqplotDataHighlight');
859
- evt.pageX = ev.pageX;
860
- evt.pageY = ev.pageY;
861
- plot.target.trigger(evt, ins);
862
- highlight (plot, ins[0], ins[1]);
863
- }
864
- }
865
- else if (neighbor == null) {
866
- unhighlight (plot);
867
- }
868
- }
869
-
870
- function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
871
- var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex;
872
- if (idx != null && plot.series[idx].highlightMouseDown) {
873
- unhighlight(plot);
874
- }
875
- }
876
-
877
- function handleClick(ev, gridpos, datapos, neighbor, plot) {
878
- if (neighbor) {
879
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
880
- var evt = jQuery.Event('jqplotDataClick');
881
- evt.pageX = ev.pageX;
882
- evt.pageY = ev.pageY;
883
- plot.target.trigger(evt, ins);
884
- }
885
- }
886
-
887
- function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
888
- if (neighbor) {
889
- var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
890
- var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex;
891
- if (idx != null && plot.series[idx].highlightMouseDown) {
892
- unhighlight(plot);
893
- }
894
- var evt = jQuery.Event('jqplotDataRightClick');
895
- evt.pageX = ev.pageX;
896
- evt.pageY = ev.pageY;
897
- plot.target.trigger(evt, ins);
898
- }
899
- }
900
-
901
- // called within context of plot
902
- // create a canvas which we can draw on.
903
- // insert it before the eventCanvas, so eventCanvas will still capture events.
904
- function postPlotDraw() {
905
- // Memory Leaks patch
906
- if (this.plugins.funnelRenderer && this.plugins.funnelRenderer.highlightCanvas) {
907
- this.plugins.funnelRenderer.highlightCanvas.resetCanvas();
908
- this.plugins.funnelRenderer.highlightCanvas = null;
909
- }
910
-
911
- this.plugins.funnelRenderer = {};
912
- this.plugins.funnelRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
913
-
914
- // do we have any data labels? if so, put highlight canvas before those
915
- var labels = $(this.targetId+' .jqplot-data-label');
916
- if (labels.length) {
917
- $(labels[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this));
918
- }
919
- // else put highlight canvas before event canvas.
920
- else {
921
- this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this));
922
- }
923
- var hctx = this.plugins.funnelRenderer.highlightCanvas.setContext();
924
- this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
925
- }
926
-
927
- $.jqplot.preInitHooks.push(preInit);
928
-
929
- $.jqplot.FunnelTickRenderer = function() {
930
- $.jqplot.AxisTickRenderer.call(this);
931
- };
932
-
933
- $.jqplot.FunnelTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
934
- $.jqplot.FunnelTickRenderer.prototype.constructor = $.jqplot.FunnelTickRenderer;
935
-
936
- })(jQuery);
937
-
1
+ /**
2
+ * jqPlot
3
+ * Pure JavaScript plotting plugin using jQuery
4
+ *
5
+ * Version: 1.0.0b2_r1012
6
+ *
7
+ * Copyright (c) 2009-2011 Chris Leonello
8
+ * jqPlot is currently available for use in all personal or commercial projects
9
+ * under both the MIT (http://www.opensource.org/licenses/mit-license.php) and GPL
10
+ * version 2.0 (http://www.gnu.org/licenses/gpl-2.0.html) licenses. This means that you can
11
+ * choose the license that best suits your project and use it accordingly.
12
+ *
13
+ * Although not required, the author would appreciate an email letting him
14
+ * know of any substantial use of jqPlot. You can reach the author at:
15
+ * chris at jqplot dot com or see http://www.jqplot.com/info.php .
16
+ *
17
+ * If you are feeling kind and generous, consider supporting the project by
18
+ * making a donation at: http://www.jqplot.com/donate.php .
19
+ *
20
+ * sprintf functions contained in jqplot.sprintf.js by Ash Searle:
21
+ *
22
+ * version 2007.04.27
23
+ * author Ash Searle
24
+ * http://hexmen.com/blog/2007/03/printf-sprintf/
25
+ * http://hexmen.com/js/sprintf.js
26
+ * The author (Ash Searle) has placed this code in the public domain:
27
+ * "This code is unrestricted: you are free to use it however you like."
28
+ *
29
+ */
30
+ (function($) {
31
+ /**
32
+ * Class: $.jqplot.FunnelRenderer
33
+ * Plugin renderer to draw a funnel chart.
34
+ * x values, if present, will be used as labels.
35
+ * y values give area size.
36
+ *
37
+ * Funnel charts will draw a single series
38
+ * only.
39
+ *
40
+ * To use this renderer, you need to include the
41
+ * funnel renderer plugin, for example:
42
+ *
43
+ * > <script type="text/javascript" src="plugins/jqplot.funnelRenderer.js"></script>
44
+ *
45
+ * Properties described here are passed into the $.jqplot function
46
+ * as options on the series renderer. For example:
47
+ *
48
+ * > plot2 = $.jqplot('chart2', [s1, s2], {
49
+ * > seriesDefaults: {
50
+ * > renderer:$.jqplot.FunnelRenderer,
51
+ * > rendererOptions:{
52
+ * > sectionMargin: 12,
53
+ * > widthRatio: 0.3
54
+ * > }
55
+ * > }
56
+ * > });
57
+ *
58
+ * IMPORTANT
59
+ *
60
+ * *The funnel renderer will reorder data in descending order* so the largest value in
61
+ * the data set is first and displayed on top of the funnel. Data will then
62
+ * be displayed in descending order down the funnel. The area of each funnel
63
+ * section will correspond to the value of each data point relative to the sum
64
+ * of all values. That is section area is proportional to section value divided by
65
+ * sum of all section values.
66
+ *
67
+ * If your data is not in descending order when passed into the plot, *it will be
68
+ * reordered* when stored in the series.data property. A copy of the unordered
69
+ * data is kept in the series._unorderedData property.
70
+ *
71
+ * A funnel plot will trigger events on the plot target
72
+ * according to user interaction. All events return the event object,
73
+ * the series index, the point (section) index, and the point data for
74
+ * the appropriate section. *Note* the point index will referr to the ordered
75
+ * data, not the original unordered data.
76
+ *
77
+ * 'jqplotDataMouseOver' - triggered when mousing over a section.
78
+ * 'jqplotDataHighlight' - triggered the first time user mouses over a section,
79
+ * if highlighting is enabled.
80
+ * 'jqplotDataUnhighlight' - triggered when a user moves the mouse out of
81
+ * a highlighted section.
82
+ * 'jqplotDataClick' - triggered when the user clicks on a section.
83
+ * 'jqplotDataRightClick' - tiggered when the user right clicks on a section if
84
+ * the "captureRightClick" option is set to true on the plot.
85
+ */
86
+ $.jqplot.FunnelRenderer = function(){
87
+ $.jqplot.LineRenderer.call(this);
88
+ };
89
+
90
+ $.jqplot.FunnelRenderer.prototype = new $.jqplot.LineRenderer();
91
+ $.jqplot.FunnelRenderer.prototype.constructor = $.jqplot.FunnelRenderer;
92
+
93
+ // called with scope of a series
94
+ $.jqplot.FunnelRenderer.prototype.init = function(options, plot) {
95
+ // Group: Properties
96
+ //
97
+ // prop: padding
98
+ // padding between the funnel and plot edges, legend, etc.
99
+ this.padding = {top: 20, right: 20, bottom: 20, left: 20};
100
+ // prop: sectionMargin
101
+ // spacing between funnel sections in pixels.
102
+ this.sectionMargin = 6;
103
+ // prop: fill
104
+ // true or false, wether to fill the areas.
105
+ this.fill = true;
106
+ // prop: shadowOffset
107
+ // offset of the shadow from the area and offset of
108
+ // each succesive stroke of the shadow from the last.
109
+ this.shadowOffset = 2;
110
+ // prop: shadowAlpha
111
+ // transparency of the shadow (0 = transparent, 1 = opaque)
112
+ this.shadowAlpha = 0.07;
113
+ // prop: shadowDepth
114
+ // number of strokes to apply to the shadow,
115
+ // each stroke offset shadowOffset from the last.
116
+ this.shadowDepth = 5;
117
+ // prop: highlightMouseOver
118
+ // True to highlight area when moused over.
119
+ // This must be false to enable highlightMouseDown to highlight when clicking on a area.
120
+ this.highlightMouseOver = true;
121
+ // prop: highlightMouseDown
122
+ // True to highlight when a mouse button is pressed over a area.
123
+ // This will be disabled if highlightMouseOver is true.
124
+ this.highlightMouseDown = false;
125
+ // prop: highlightColors
126
+ // array of colors to use when highlighting an area.
127
+ this.highlightColors = [];
128
+ // prop: widthRatio
129
+ // The ratio of the width of the top of the funnel to the bottom.
130
+ // a ratio of 0 will make an upside down pyramid.
131
+ this.widthRatio = 0.2;
132
+ // prop: lineWidth
133
+ // width of line if areas are stroked and not filled.
134
+ this.lineWidth = 2;
135
+ // prop: dataLabels
136
+ // Either 'label', 'value', 'percent' or an array of labels to place on the pie slices.
137
+ // Defaults to percentage of each pie slice.
138
+ this.dataLabels = 'percent';
139
+ // prop: showDataLabels
140
+ // true to show data labels on slices.
141
+ this.showDataLabels = false;
142
+ // prop: dataLabelFormatString
143
+ // Format string for data labels. If none, '%s' is used for "label" and for arrays, '%d' for value and '%d%%' for percentage.
144
+ this.dataLabelFormatString = null;
145
+ // prop: dataLabelThreshold
146
+ // Threshhold in percentage (0 - 100) of pie area, below which no label will be displayed.
147
+ // This applies to all label types, not just to percentage labels.
148
+ this.dataLabelThreshold = 3;
149
+ this._type = 'funnel';
150
+
151
+ this.tickRenderer = $.jqplot.FunnelTickRenderer;
152
+
153
+ // if user has passed in highlightMouseDown option and not set highlightMouseOver, disable highlightMouseOver
154
+ if (options.highlightMouseDown && options.highlightMouseOver == null) {
155
+ options.highlightMouseOver = false;
156
+ }
157
+
158
+ $.extend(true, this, options);
159
+
160
+ // index of the currenty highlighted point, if any
161
+ this._highlightedPoint = null;
162
+
163
+ // lengths of bases, or horizontal sides of areas of trapezoid.
164
+ this._bases = [];
165
+ // total area
166
+ this._atot;
167
+ // areas of segments.
168
+ this._areas = [];
169
+ // vertical lengths of segments.
170
+ this._lengths = [];
171
+ // angle of the funnel to vertical.
172
+ this._angle;
173
+ this._dataIndices = [];
174
+
175
+ // sort data
176
+ this._unorderedData = $.extend(true, [], this.data);
177
+ var idxs = $.extend(true, [], this.data);
178
+ for (var i=0; i<idxs.length; i++) {
179
+ idxs[i].push(i);
180
+ }
181
+ this.data.sort( function (a, b) { return b[1] - a[1]; } );
182
+ idxs.sort( function (a, b) { return b[1] - a[1]; });
183
+ for (var i=0; i<idxs.length; i++) {
184
+ this._dataIndices.push(idxs[i][2]);
185
+ }
186
+
187
+ // set highlight colors if none provided
188
+ if (this.highlightColors.length == 0) {
189
+ for (var i=0; i<this.seriesColors.length; i++){
190
+ var rgba = $.jqplot.getColorComponents(this.seriesColors[i]);
191
+ var newrgb = [rgba[0], rgba[1], rgba[2]];
192
+ var sum = newrgb[0] + newrgb[1] + newrgb[2];
193
+ for (var j=0; j<3; j++) {
194
+ // when darkening, lowest color component can be is 60.
195
+ newrgb[j] = (sum > 570) ? newrgb[j] * 0.8 : newrgb[j] + 0.4 * (255 - newrgb[j]);
196
+ newrgb[j] = parseInt(newrgb[j], 10);
197
+ }
198
+ this.highlightColors.push('rgb('+newrgb[0]+','+newrgb[1]+','+newrgb[2]+')');
199
+ }
200
+ }
201
+
202
+ plot.postParseOptionsHooks.addOnce(postParseOptions);
203
+ plot.postInitHooks.addOnce(postInit);
204
+ plot.eventListenerHooks.addOnce('jqplotMouseMove', handleMove);
205
+ plot.eventListenerHooks.addOnce('jqplotMouseDown', handleMouseDown);
206
+ plot.eventListenerHooks.addOnce('jqplotMouseUp', handleMouseUp);
207
+ plot.eventListenerHooks.addOnce('jqplotClick', handleClick);
208
+ plot.eventListenerHooks.addOnce('jqplotRightClick', handleRightClick);
209
+ plot.postDrawHooks.addOnce(postPlotDraw);
210
+
211
+ };
212
+
213
+ // gridData will be of form [label, percentage of total]
214
+ $.jqplot.FunnelRenderer.prototype.setGridData = function(plot) {
215
+ // set gridData property. This will hold angle in radians of each data point.
216
+ var sum = 0;
217
+ var td = [];
218
+ for (var i=0; i<this.data.length; i++){
219
+ sum += this.data[i][1];
220
+ td.push([this.data[i][0], this.data[i][1]]);
221
+ }
222
+
223
+ // normalize y values, so areas are proportional.
224
+ for (var i=0; i<td.length; i++) {
225
+ td[i][1] = td[i][1]/sum;
226
+ }
227
+
228
+ this._bases = new Array(td.length + 1);
229
+ this._lengths = new Array(td.length);
230
+
231
+ this.gridData = td;
232
+ };
233
+
234
+ $.jqplot.FunnelRenderer.prototype.makeGridData = function(data, plot) {
235
+ // set gridData property. This will hold angle in radians of each data point.
236
+ var sum = 0;
237
+ var td = [];
238
+ for (var i=0; i<this.data.length; i++){
239
+ sum += this.data[i][1];
240
+ td.push([this.data[i][0], this.data[i][1]]);
241
+ }
242
+
243
+ // normalize y values, so areas are proportional.
244
+ for (var i=0; i<td.length; i++) {
245
+ td[i][1] = td[i][1]/sum;
246
+ }
247
+
248
+ this._bases = new Array(td.length + 1);
249
+ this._lengths = new Array(td.length);
250
+
251
+ return td;
252
+ };
253
+
254
+ $.jqplot.FunnelRenderer.prototype.drawSection = function (ctx, vertices, color, isShadow) {
255
+ var fill = this.fill;
256
+ var lineWidth = this.lineWidth;
257
+ ctx.save();
258
+
259
+ if (isShadow) {
260
+ for (var i=0; i<this.shadowDepth; i++) {
261
+ ctx.save();
262
+ ctx.translate(this.shadowOffset*Math.cos(this.shadowAngle/180*Math.PI), this.shadowOffset*Math.sin(this.shadowAngle/180*Math.PI));
263
+ doDraw();
264
+ }
265
+ }
266
+
267
+ else {
268
+ doDraw();
269
+ }
270
+
271
+ function doDraw () {
272
+ ctx.beginPath();
273
+ ctx.fillStyle = color;
274
+ ctx.strokeStyle = color;
275
+ ctx.lineWidth = lineWidth;
276
+ ctx.moveTo(vertices[0][0], vertices[0][1]);
277
+ for (var i=1; i<4; i++) {
278
+ ctx.lineTo(vertices[i][0], vertices[i][1]);
279
+ }
280
+ ctx.closePath();
281
+ if (fill) {
282
+ ctx.fill();
283
+ }
284
+ else {
285
+ ctx.stroke();
286
+ }
287
+ }
288
+
289
+ if (isShadow) {
290
+ for (var i=0; i<this.shadowDepth; i++) {
291
+ ctx.restore();
292
+ }
293
+ }
294
+
295
+ ctx.restore();
296
+ };
297
+
298
+ // called with scope of series
299
+ $.jqplot.FunnelRenderer.prototype.draw = function (ctx, gd, options, plot) {
300
+ var i;
301
+ var opts = (options != undefined) ? options : {};
302
+ // offset and direction of offset due to legend placement
303
+ var offx = 0;
304
+ var offy = 0;
305
+ var trans = 1;
306
+ this._areas = [];
307
+ // var colorGenerator = new this.colorGenerator(this.seriesColors);
308
+ if (options.legendInfo && options.legendInfo.placement == 'insideGrid') {
309
+ var li = options.legendInfo;
310
+ switch (li.location) {
311
+ case 'nw':
312
+ offx = li.width + li.xoffset;
313
+ break;
314
+ case 'w':
315
+ offx = li.width + li.xoffset;
316
+ break;
317
+ case 'sw':
318
+ offx = li.width + li.xoffset;
319
+ break;
320
+ case 'ne':
321
+ offx = li.width + li.xoffset;
322
+ trans = -1;
323
+ break;
324
+ case 'e':
325
+ offx = li.width + li.xoffset;
326
+ trans = -1;
327
+ break;
328
+ case 'se':
329
+ offx = li.width + li.xoffset;
330
+ trans = -1;
331
+ break;
332
+ case 'n':
333
+ offy = li.height + li.yoffset;
334
+ break;
335
+ case 's':
336
+ offy = li.height + li.yoffset;
337
+ trans = -1;
338
+ break;
339
+ default:
340
+ break;
341
+ }
342
+ }
343
+
344
+ var loff = (trans==1) ? this.padding.left + offx : this.padding.left;
345
+ var toff = (trans==1) ? this.padding.top + offy : this.padding.top;
346
+ var roff = (trans==-1) ? this.padding.right + offx : this.padding.right;
347
+ var boff = (trans==-1) ? this.padding.bottom + offy : this.padding.bottom;
348
+
349
+ var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow;
350
+ var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine;
351
+ var fill = (opts.fill != undefined) ? opts.fill : this.fill;
352
+ var cw = ctx.canvas.width;
353
+ var ch = ctx.canvas.height;
354
+ this._bases[0] = cw - loff - roff;
355
+ var ltot = this._length = ch - toff - boff;
356
+
357
+ var hend = this._bases[0]*this.widthRatio;
358
+ this._atot = ltot/2 * (this._bases[0] + this._bases[0]*this.widthRatio);
359
+
360
+ this._angle = Math.atan((this._bases[0] - hend)/2/ltot);
361
+
362
+ for (i=0; i<gd.length; i++) {
363
+ this._areas.push(gd[i][1] * this._atot);
364
+ }
365
+
366
+
367
+ var guess, err, count, lsum=0;
368
+ var tolerance = 0.0001;
369
+
370
+ for (i=0; i<this._areas.length; i++) {
371
+ guess = this._areas[i]/this._bases[i];
372
+ err = 999999;
373
+ this._lengths[i] = guess;
374
+ count = 0;
375
+ while (err > this._lengths[i]*tolerance && count < 100) {
376
+ this._lengths[i] = this._areas[i]/(this._bases[i] - this._lengths[i] * Math.tan(this._angle));
377
+ err = Math.abs(this._lengths[i] - guess);
378
+ this._bases[i+1] = this._bases[i] - (2*this._lengths[i]*Math.tan(this._angle));
379
+ guess = this._lengths[i];
380
+ count++;
381
+ }
382
+ lsum += this._lengths[i];
383
+ }
384
+
385
+ // figure out vertices of each section
386
+ this._vertices = new Array(gd.length);
387
+
388
+ // these are 4 coners of entire trapezoid
389
+ var p0 = [loff, toff],
390
+ p1 = [loff+this._bases[0], toff],
391
+ p2 = [loff + (this._bases[0] - this._bases[this._bases.length-1])/2, toff + this._length],
392
+ p3 = [p2[0] + this._bases[this._bases.length-1], p2[1]];
393
+
394
+ // equations of right and left sides, returns x, y values given height of section (y value)
395
+ function findleft (l) {
396
+ var m = (p0[1] - p2[1])/(p0[0] - p2[0]);
397
+ var b = p0[1] - m*p0[0];
398
+ var y = l + p0[1];
399
+
400
+ return [(y - b)/m, y];
401
+ }
402
+
403
+ function findright (l) {
404
+ var m = (p1[1] - p3[1])/(p1[0] - p3[0]);
405
+ var b = p1[1] - m*p1[0];
406
+ var y = l + p1[1];
407
+
408
+ return [(y - b)/m, y];
409
+ }
410
+
411
+ var x = offx, y = offy;
412
+ var h=0, adj=0;
413
+
414
+ for (i=0; i<gd.length; i++) {
415
+ this._vertices[i] = new Array();
416
+ var v = this._vertices[i];
417
+ var sm = this.sectionMargin;
418
+ if (i == 0) {
419
+ adj = 0;
420
+ }
421
+ if (i == 1) {
422
+ adj = sm/3;
423
+ }
424
+ else if (i > 0 && i < gd.length-1) {
425
+ adj = sm/2;
426
+ }
427
+ else if (i == gd.length -1) {
428
+ adj = 2*sm/3;
429
+ }
430
+ v.push(findleft(h+adj));
431
+ v.push(findright(h+adj));
432
+ h += this._lengths[i];
433
+ if (i == 0) {
434
+ adj = -2*sm/3;
435
+ }
436
+ else if (i > 0 && i < gd.length-1) {
437
+ adj = -sm/2;
438
+ }
439
+ else if (i == gd.length - 1) {
440
+ adj = 0;
441
+ }
442
+ v.push(findright(h+adj));
443
+ v.push(findleft(h+adj));
444
+
445
+ }
446
+
447
+ if (this.shadow) {
448
+ var shadowColor = 'rgba(0,0,0,'+this.shadowAlpha+')';
449
+ for (var i=0; i<gd.length; i++) {
450
+ this.renderer.drawSection.call (this, ctx, this._vertices[i], shadowColor, true);
451
+ }
452
+
453
+ }
454
+ for (var i=0; i<gd.length; i++) {
455
+ var v = this._vertices[i];
456
+ this.renderer.drawSection.call (this, ctx, v, this.seriesColors[i]);
457
+
458
+ if (this.showDataLabels && gd[i][1]*100 >= this.dataLabelThreshold) {
459
+ var fstr, label;
460
+
461
+ if (this.dataLabels == 'label') {
462
+ fstr = this.dataLabelFormatString || '%s';
463
+ label = $.jqplot.sprintf(fstr, gd[i][0]);
464
+ }
465
+ else if (this.dataLabels == 'value') {
466
+ fstr = this.dataLabelFormatString || '%d';
467
+ label = $.jqplot.sprintf(fstr, this.data[i][1]);
468
+ }
469
+ else if (this.dataLabels == 'percent') {
470
+ fstr = this.dataLabelFormatString || '%d%%';
471
+ label = $.jqplot.sprintf(fstr, gd[i][1]*100);
472
+ }
473
+ else if (this.dataLabels.constructor == Array) {
474
+ fstr = this.dataLabelFormatString || '%s';
475
+ label = $.jqplot.sprintf(fstr, this.dataLabels[this._dataIndices[i]]);
476
+ }
477
+
478
+ var fact = (this._radius ) * this.dataLabelPositionFactor + this.sliceMargin + this.dataLabelNudge;
479
+
480
+ var x = (v[0][0] + v[1][0])/2 + this.canvas._offsets.left;
481
+ var y = (v[1][1] + v[2][1])/2 + this.canvas._offsets.top;
482
+
483
+ var labelelem = $('<span class="jqplot-funnel-series jqplot-data-label" style="position:absolute;">' + label + '</span>').insertBefore(plot.eventCanvas._elem);
484
+ x -= labelelem.width()/2;
485
+ y -= labelelem.height()/2;
486
+ x = Math.round(x);
487
+ y = Math.round(y);
488
+ labelelem.css({left: x, top: y});
489
+ }
490
+
491
+ }
492
+
493
+ };
494
+
495
+ $.jqplot.FunnelAxisRenderer = function() {
496
+ $.jqplot.LinearAxisRenderer.call(this);
497
+ };
498
+
499
+ $.jqplot.FunnelAxisRenderer.prototype = new $.jqplot.LinearAxisRenderer();
500
+ $.jqplot.FunnelAxisRenderer.prototype.constructor = $.jqplot.FunnelAxisRenderer;
501
+
502
+
503
+ // There are no traditional axes on a funnel chart. We just need to provide
504
+ // dummy objects with properties so the plot will render.
505
+ // called with scope of axis object.
506
+ $.jqplot.FunnelAxisRenderer.prototype.init = function(options){
507
+ //
508
+ this.tickRenderer = $.jqplot.FunnelTickRenderer;
509
+ $.extend(true, this, options);
510
+ // I don't think I'm going to need _dataBounds here.
511
+ // have to go Axis scaling in a way to fit chart onto plot area
512
+ // and provide u2p and p2u functionality for mouse cursor, etc.
513
+ // for convienence set _dataBounds to 0 and 100 and
514
+ // set min/max to 0 and 100.
515
+ this._dataBounds = {min:0, max:100};
516
+ this.min = 0;
517
+ this.max = 100;
518
+ this.showTicks = false;
519
+ this.ticks = [];
520
+ this.showMark = false;
521
+ this.show = false;
522
+ };
523
+
524
+
525
+
526
+ /**
527
+ * Class: $.jqplot.FunnelLegendRenderer
528
+ * Legend Renderer specific to funnel plots. Set by default
529
+ * when the user creates a funnel plot.
530
+ */
531
+ $.jqplot.FunnelLegendRenderer = function(){
532
+ $.jqplot.TableLegendRenderer.call(this);
533
+ };
534
+
535
+ $.jqplot.FunnelLegendRenderer.prototype = new $.jqplot.TableLegendRenderer();
536
+ $.jqplot.FunnelLegendRenderer.prototype.constructor = $.jqplot.FunnelLegendRenderer;
537
+
538
+ $.jqplot.FunnelLegendRenderer.prototype.init = function(options) {
539
+ // Group: Properties
540
+ //
541
+ // prop: numberRows
542
+ // Maximum number of rows in the legend. 0 or null for unlimited.
543
+ this.numberRows = null;
544
+ // prop: numberColumns
545
+ // Maximum number of columns in the legend. 0 or null for unlimited.
546
+ this.numberColumns = null;
547
+ $.extend(true, this, options);
548
+ };
549
+
550
+ // called with context of legend
551
+ $.jqplot.FunnelLegendRenderer.prototype.draw = function() {
552
+ var legend = this;
553
+ if (this.show) {
554
+ var series = this._series;
555
+ var ss = 'position:absolute;';
556
+ ss += (this.background) ? 'background:'+this.background+';' : '';
557
+ ss += (this.border) ? 'border:'+this.border+';' : '';
558
+ ss += (this.fontSize) ? 'font-size:'+this.fontSize+';' : '';
559
+ ss += (this.fontFamily) ? 'font-family:'+this.fontFamily+';' : '';
560
+ ss += (this.textColor) ? 'color:'+this.textColor+';' : '';
561
+ ss += (this.marginTop != null) ? 'margin-top:'+this.marginTop+';' : '';
562
+ ss += (this.marginBottom != null) ? 'margin-bottom:'+this.marginBottom+';' : '';
563
+ ss += (this.marginLeft != null) ? 'margin-left:'+this.marginLeft+';' : '';
564
+ ss += (this.marginRight != null) ? 'margin-right:'+this.marginRight+';' : '';
565
+ this._elem = $('<table class="jqplot-table-legend" style="'+ss+'"></table>');
566
+ // Funnel charts legends don't go by number of series, but by number of data points
567
+ // in the series. Refactor things here for that.
568
+
569
+ var pad = false,
570
+ reverse = false,
571
+ nr, nc;
572
+ var s = series[0];
573
+ var colorGenerator = new $.jqplot.ColorGenerator(s.seriesColors);
574
+
575
+ if (s.show) {
576
+ var pd = s.data;
577
+ if (this.numberRows) {
578
+ nr = this.numberRows;
579
+ if (!this.numberColumns){
580
+ nc = Math.ceil(pd.length/nr);
581
+ }
582
+ else{
583
+ nc = this.numberColumns;
584
+ }
585
+ }
586
+ else if (this.numberColumns) {
587
+ nc = this.numberColumns;
588
+ nr = Math.ceil(pd.length/this.numberColumns);
589
+ }
590
+ else {
591
+ nr = pd.length;
592
+ nc = 1;
593
+ }
594
+
595
+ var i, j, tr, td1, td2, lt, rs, color;
596
+ var idx = 0;
597
+
598
+ for (i=0; i<nr; i++) {
599
+ if (reverse){
600
+ tr = $('<tr class="jqplot-table-legend"></tr>').prependTo(this._elem);
601
+ }
602
+ else{
603
+ tr = $('<tr class="jqplot-table-legend"></tr>').appendTo(this._elem);
604
+ }
605
+ for (j=0; j<nc; j++) {
606
+ if (idx < pd.length){
607
+ lt = this.labels[idx] || pd[idx][0].toString();
608
+ color = colorGenerator.next();
609
+ if (!reverse){
610
+ if (i>0){
611
+ pad = true;
612
+ }
613
+ else{
614
+ pad = false;
615
+ }
616
+ }
617
+ else{
618
+ if (i == nr -1){
619
+ pad = false;
620
+ }
621
+ else{
622
+ pad = true;
623
+ }
624
+ }
625
+ rs = (pad) ? this.rowSpacing : '0';
626
+
627
+ td1 = $('<td class="jqplot-table-legend" style="text-align:center;padding-top:'+rs+';">'+
628
+ '<div><div class="jqplot-table-legend-swatch" style="border-color:'+color+';"></div>'+
629
+ '</div></td>');
630
+ td2 = $('<td class="jqplot-table-legend" style="padding-top:'+rs+';"></td>');
631
+ if (this.escapeHtml){
632
+ td2.text(lt);
633
+ }
634
+ else {
635
+ td2.html(lt);
636
+ }
637
+ if (reverse) {
638
+ td2.prependTo(tr);
639
+ td1.prependTo(tr);
640
+ }
641
+ else {
642
+ td1.appendTo(tr);
643
+ td2.appendTo(tr);
644
+ }
645
+ pad = true;
646
+ }
647
+ idx++;
648
+ }
649
+ }
650
+ }
651
+ }
652
+ return this._elem;
653
+ };
654
+
655
+ // $.jqplot.FunnelLegendRenderer.prototype.pack = function(offsets) {
656
+ // if (this.show) {
657
+ // // fake a grid for positioning
658
+ // var grid = {_top:offsets.top, _left:offsets.left, _right:offsets.right, _bottom:this._plotDimensions.height - offsets.bottom};
659
+ // if (this.placement == 'insideGrid') {
660
+ // switch (this.location) {
661
+ // case 'nw':
662
+ // var a = grid._left + this.xoffset;
663
+ // var b = grid._top + this.yoffset;
664
+ // this._elem.css('left', a);
665
+ // this._elem.css('top', b);
666
+ // break;
667
+ // case 'n':
668
+ // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
669
+ // var b = grid._top + this.yoffset;
670
+ // this._elem.css('left', a);
671
+ // this._elem.css('top', b);
672
+ // break;
673
+ // case 'ne':
674
+ // var a = offsets.right + this.xoffset;
675
+ // var b = grid._top + this.yoffset;
676
+ // this._elem.css({right:a, top:b});
677
+ // break;
678
+ // case 'e':
679
+ // var a = offsets.right + this.xoffset;
680
+ // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
681
+ // this._elem.css({right:a, top:b});
682
+ // break;
683
+ // case 'se':
684
+ // var a = offsets.right + this.xoffset;
685
+ // var b = offsets.bottom + this.yoffset;
686
+ // this._elem.css({right:a, bottom:b});
687
+ // break;
688
+ // case 's':
689
+ // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
690
+ // var b = offsets.bottom + this.yoffset;
691
+ // this._elem.css({left:a, bottom:b});
692
+ // break;
693
+ // case 'sw':
694
+ // var a = grid._left + this.xoffset;
695
+ // var b = offsets.bottom + this.yoffset;
696
+ // this._elem.css({left:a, bottom:b});
697
+ // break;
698
+ // case 'w':
699
+ // var a = grid._left + this.xoffset;
700
+ // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
701
+ // this._elem.css({left:a, top:b});
702
+ // break;
703
+ // default: // same as 'se'
704
+ // var a = grid._right - this.xoffset;
705
+ // var b = grid._bottom + this.yoffset;
706
+ // this._elem.css({right:a, bottom:b});
707
+ // break;
708
+ // }
709
+ //
710
+ // }
711
+ // else {
712
+ // switch (this.location) {
713
+ // case 'nw':
714
+ // var a = this._plotDimensions.width - grid._left + this.xoffset;
715
+ // var b = grid._top + this.yoffset;
716
+ // this._elem.css('right', a);
717
+ // this._elem.css('top', b);
718
+ // break;
719
+ // case 'n':
720
+ // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
721
+ // var b = this._plotDimensions.height - grid._top + this.yoffset;
722
+ // this._elem.css('left', a);
723
+ // this._elem.css('bottom', b);
724
+ // break;
725
+ // case 'ne':
726
+ // var a = this._plotDimensions.width - offsets.right + this.xoffset;
727
+ // var b = grid._top + this.yoffset;
728
+ // this._elem.css({left:a, top:b});
729
+ // break;
730
+ // case 'e':
731
+ // var a = this._plotDimensions.width - offsets.right + this.xoffset;
732
+ // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
733
+ // this._elem.css({left:a, top:b});
734
+ // break;
735
+ // case 'se':
736
+ // var a = this._plotDimensions.width - offsets.right + this.xoffset;
737
+ // var b = offsets.bottom + this.yoffset;
738
+ // this._elem.css({left:a, bottom:b});
739
+ // break;
740
+ // case 's':
741
+ // var a = (offsets.left + (this._plotDimensions.width - offsets.right))/2 - this.getWidth()/2;
742
+ // var b = this._plotDimensions.height - offsets.bottom + this.yoffset;
743
+ // this._elem.css({left:a, top:b});
744
+ // break;
745
+ // case 'sw':
746
+ // var a = this._plotDimensions.width - grid._left + this.xoffset;
747
+ // var b = offsets.bottom + this.yoffset;
748
+ // this._elem.css({right:a, bottom:b});
749
+ // break;
750
+ // case 'w':
751
+ // var a = this._plotDimensions.width - grid._left + this.xoffset;
752
+ // var b = (offsets.top + (this._plotDimensions.height - offsets.bottom))/2 - this.getHeight()/2;
753
+ // this._elem.css({right:a, top:b});
754
+ // break;
755
+ // default: // same as 'se'
756
+ // var a = grid._right - this.xoffset;
757
+ // var b = grid._bottom + this.yoffset;
758
+ // this._elem.css({right:a, bottom:b});
759
+ // break;
760
+ // }
761
+ // }
762
+ // }
763
+ // };
764
+
765
+ // setup default renderers for axes and legend so user doesn't have to
766
+ // called with scope of plot
767
+ function preInit(target, data, options) {
768
+ options = options || {};
769
+ options.axesDefaults = options.axesDefaults || {};
770
+ options.legend = options.legend || {};
771
+ options.seriesDefaults = options.seriesDefaults || {};
772
+ // only set these if there is a funnel series
773
+ var setopts = false;
774
+ if (options.seriesDefaults.renderer == $.jqplot.FunnelRenderer) {
775
+ setopts = true;
776
+ }
777
+ else if (options.series) {
778
+ for (var i=0; i < options.series.length; i++) {
779
+ if (options.series[i].renderer == $.jqplot.FunnelRenderer) {
780
+ setopts = true;
781
+ }
782
+ }
783
+ }
784
+
785
+ if (setopts) {
786
+ options.axesDefaults.renderer = $.jqplot.FunnelAxisRenderer;
787
+ options.legend.renderer = $.jqplot.FunnelLegendRenderer;
788
+ options.legend.preDraw = true;
789
+ options.sortData = false;
790
+ options.seriesDefaults.pointLabels = {show: false};
791
+ }
792
+ }
793
+
794
+ function postInit(target, data, options) {
795
+ // if multiple series, add a reference to the previous one so that
796
+ // funnel rings can nest.
797
+ for (var i=0; i<this.series.length; i++) {
798
+ if (this.series[i].renderer.constructor == $.jqplot.FunnelRenderer) {
799
+ // don't allow mouseover and mousedown at same time.
800
+ if (this.series[i].highlightMouseOver) {
801
+ this.series[i].highlightMouseDown = false;
802
+ }
803
+ }
804
+ }
805
+ }
806
+
807
+ // called with scope of plot
808
+ function postParseOptions(options) {
809
+ for (var i=0; i<this.series.length; i++) {
810
+ this.series[i].seriesColors = this.seriesColors;
811
+ this.series[i].colorGenerator = $.jqplot.colorGenerator;
812
+ }
813
+ }
814
+
815
+ function highlight (plot, sidx, pidx) {
816
+ var s = plot.series[sidx];
817
+ var canvas = plot.plugins.funnelRenderer.highlightCanvas;
818
+ canvas._ctx.clearRect(0,0,canvas._ctx.canvas.width, canvas._ctx.canvas.height);
819
+ s._highlightedPoint = pidx;
820
+ plot.plugins.funnelRenderer.highlightedSeriesIndex = sidx;
821
+ s.renderer.drawSection.call(s, canvas._ctx, s._vertices[pidx], s.highlightColors[pidx], false);
822
+ }
823
+
824
+ function unhighlight (plot) {
825
+ var canvas = plot.plugins.funnelRenderer.highlightCanvas;
826
+ canvas._ctx.clearRect(0,0, canvas._ctx.canvas.width, canvas._ctx.canvas.height);
827
+ for (var i=0; i<plot.series.length; i++) {
828
+ plot.series[i]._highlightedPoint = null;
829
+ }
830
+ plot.plugins.funnelRenderer.highlightedSeriesIndex = null;
831
+ plot.target.trigger('jqplotDataUnhighlight');
832
+ }
833
+
834
+ function handleMove(ev, gridpos, datapos, neighbor, plot) {
835
+ if (neighbor) {
836
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
837
+ var evt1 = jQuery.Event('jqplotDataMouseOver');
838
+ evt1.pageX = ev.pageX;
839
+ evt1.pageY = ev.pageY;
840
+ plot.target.trigger(evt1, ins);
841
+ if (plot.series[ins[0]].highlightMouseOver && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
842
+ var evt = jQuery.Event('jqplotDataHighlight');
843
+ evt.pageX = ev.pageX;
844
+ evt.pageY = ev.pageY;
845
+ plot.target.trigger(evt, ins);
846
+ highlight (plot, ins[0], ins[1]);
847
+ }
848
+ }
849
+ else if (neighbor == null) {
850
+ unhighlight (plot);
851
+ }
852
+ }
853
+
854
+ function handleMouseDown(ev, gridpos, datapos, neighbor, plot) {
855
+ if (neighbor) {
856
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
857
+ if (plot.series[ins[0]].highlightMouseDown && !(ins[0] == plot.plugins.funnelRenderer.highlightedSeriesIndex && ins[1] == plot.series[ins[0]]._highlightedPoint)) {
858
+ var evt = jQuery.Event('jqplotDataHighlight');
859
+ evt.pageX = ev.pageX;
860
+ evt.pageY = ev.pageY;
861
+ plot.target.trigger(evt, ins);
862
+ highlight (plot, ins[0], ins[1]);
863
+ }
864
+ }
865
+ else if (neighbor == null) {
866
+ unhighlight (plot);
867
+ }
868
+ }
869
+
870
+ function handleMouseUp(ev, gridpos, datapos, neighbor, plot) {
871
+ var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex;
872
+ if (idx != null && plot.series[idx].highlightMouseDown) {
873
+ unhighlight(plot);
874
+ }
875
+ }
876
+
877
+ function handleClick(ev, gridpos, datapos, neighbor, plot) {
878
+ if (neighbor) {
879
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
880
+ var evt = jQuery.Event('jqplotDataClick');
881
+ evt.pageX = ev.pageX;
882
+ evt.pageY = ev.pageY;
883
+ plot.target.trigger(evt, ins);
884
+ }
885
+ }
886
+
887
+ function handleRightClick(ev, gridpos, datapos, neighbor, plot) {
888
+ if (neighbor) {
889
+ var ins = [neighbor.seriesIndex, neighbor.pointIndex, neighbor.data];
890
+ var idx = plot.plugins.funnelRenderer.highlightedSeriesIndex;
891
+ if (idx != null && plot.series[idx].highlightMouseDown) {
892
+ unhighlight(plot);
893
+ }
894
+ var evt = jQuery.Event('jqplotDataRightClick');
895
+ evt.pageX = ev.pageX;
896
+ evt.pageY = ev.pageY;
897
+ plot.target.trigger(evt, ins);
898
+ }
899
+ }
900
+
901
+ // called within context of plot
902
+ // create a canvas which we can draw on.
903
+ // insert it before the eventCanvas, so eventCanvas will still capture events.
904
+ function postPlotDraw() {
905
+ // Memory Leaks patch
906
+ if (this.plugins.funnelRenderer && this.plugins.funnelRenderer.highlightCanvas) {
907
+ this.plugins.funnelRenderer.highlightCanvas.resetCanvas();
908
+ this.plugins.funnelRenderer.highlightCanvas = null;
909
+ }
910
+
911
+ this.plugins.funnelRenderer = {};
912
+ this.plugins.funnelRenderer.highlightCanvas = new $.jqplot.GenericCanvas();
913
+
914
+ // do we have any data labels? if so, put highlight canvas before those
915
+ var labels = $(this.targetId+' .jqplot-data-label');
916
+ if (labels.length) {
917
+ $(labels[0]).before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this));
918
+ }
919
+ // else put highlight canvas before event canvas.
920
+ else {
921
+ this.eventCanvas._elem.before(this.plugins.funnelRenderer.highlightCanvas.createElement(this._gridPadding, 'jqplot-funnelRenderer-highlight-canvas', this._plotDimensions, this));
922
+ }
923
+ var hctx = this.plugins.funnelRenderer.highlightCanvas.setContext();
924
+ this.eventCanvas._elem.bind('mouseleave', {plot:this}, function (ev) { unhighlight(ev.data.plot); });
925
+ }
926
+
927
+ $.jqplot.preInitHooks.push(preInit);
928
+
929
+ $.jqplot.FunnelTickRenderer = function() {
930
+ $.jqplot.AxisTickRenderer.call(this);
931
+ };
932
+
933
+ $.jqplot.FunnelTickRenderer.prototype = new $.jqplot.AxisTickRenderer();
934
+ $.jqplot.FunnelTickRenderer.prototype.constructor = $.jqplot.FunnelTickRenderer;
935
+
936
+ })(jQuery);
937
+
938
938