radiant-race_results-extension 1.4.3 → 1.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/app/controllers/admin/race_instances_controller.rb +1 -1
  2. data/app/controllers/race_instances_controller.rb +7 -1
  3. data/app/controllers/race_performances_controller.rb +8 -0
  4. data/app/models/race.rb +7 -0
  5. data/app/models/race_checkpoint.rb +27 -1
  6. data/app/models/race_checkpoint_time.rb +33 -5
  7. data/app/models/race_instance.rb +38 -9
  8. data/app/models/race_performance.rb +81 -3
  9. data/app/views/admin/races/_form.html.haml +1 -2
  10. data/app/views/race_clubs/show.html.haml +3 -3
  11. data/app/views/race_instances/_results_header.html.haml +10 -0
  12. data/app/views/race_instances/_splits_header.html.haml +9 -0
  13. data/app/views/race_instances/show.html.haml +17 -8
  14. data/app/views/race_instances/splits.html.haml +19 -14
  15. data/app/views/race_performances/_performance.html.haml +12 -2
  16. data/app/views/race_performances/_splits.html.haml +30 -8
  17. data/app/views/race_performances/show.html.haml +18 -76
  18. data/app/views/races/show.html.haml +2 -1
  19. data/config/locales/en.yml +20 -0
  20. data/config/routes.rb +1 -1
  21. data/db/migrate/20111103150827_mapping_routes.rb +22 -0
  22. data/db/migrate/20111115150827_finish_checkpoint.rb +8 -0
  23. data/lib/race_tags.rb +3 -3
  24. data/lib/radiant-race_results-extension.rb +1 -1
  25. data/public/images/race_results/sorts.png +0 -0
  26. data/public/javascripts/flot/API.txt +1201 -0
  27. data/public/javascripts/flot/FAQ.txt +76 -0
  28. data/public/javascripts/flot/LICENSE.txt +22 -0
  29. data/public/javascripts/flot/Makefile +9 -0
  30. data/public/javascripts/flot/NEWS.txt +508 -0
  31. data/public/javascripts/flot/PLUGINS.txt +137 -0
  32. data/public/javascripts/flot/README.txt +90 -0
  33. data/public/javascripts/flot/examples/ajax.html +143 -0
  34. data/public/javascripts/flot/examples/annotating.html +75 -0
  35. data/public/javascripts/flot/examples/arrow-down.gif +0 -0
  36. data/public/javascripts/flot/examples/arrow-left.gif +0 -0
  37. data/public/javascripts/flot/examples/arrow-right.gif +0 -0
  38. data/public/javascripts/flot/examples/arrow-up.gif +0 -0
  39. data/public/javascripts/flot/examples/basic.html +38 -0
  40. data/public/javascripts/flot/examples/data-eu-gdp-growth-1.json +4 -0
  41. data/public/javascripts/flot/examples/data-eu-gdp-growth-2.json +4 -0
  42. data/public/javascripts/flot/examples/data-eu-gdp-growth-3.json +4 -0
  43. data/public/javascripts/flot/examples/data-eu-gdp-growth-4.json +4 -0
  44. data/public/javascripts/flot/examples/data-eu-gdp-growth-5.json +4 -0
  45. data/public/javascripts/flot/examples/data-eu-gdp-growth.json +4 -0
  46. data/public/javascripts/flot/examples/data-japan-gdp-growth.json +4 -0
  47. data/public/javascripts/flot/examples/data-usa-gdp-growth.json +4 -0
  48. data/public/javascripts/flot/examples/graph-types.html +75 -0
  49. data/public/javascripts/flot/examples/hs-2004-27-a-large_web.jpg +0 -0
  50. data/public/javascripts/flot/examples/image.html +45 -0
  51. data/public/javascripts/flot/examples/index.html +44 -0
  52. data/public/javascripts/flot/examples/interacting-axes.html +97 -0
  53. data/public/javascripts/flot/examples/interacting.html +93 -0
  54. data/public/javascripts/flot/examples/layout.css +6 -0
  55. data/public/javascripts/flot/examples/multiple-axes.html +60 -0
  56. data/public/javascripts/flot/examples/navigate.html +118 -0
  57. data/public/javascripts/flot/examples/percentiles.html +57 -0
  58. data/public/javascripts/flot/examples/pie.html +756 -0
  59. data/public/javascripts/flot/examples/realtime.html +83 -0
  60. data/public/javascripts/flot/examples/resize.html +61 -0
  61. data/public/javascripts/flot/examples/selection.html +114 -0
  62. data/public/javascripts/flot/examples/setting-options.html +61 -0
  63. data/public/javascripts/flot/examples/stacking.html +77 -0
  64. data/public/javascripts/flot/examples/symbols.html +49 -0
  65. data/public/javascripts/flot/examples/thresholding.html +54 -0
  66. data/public/javascripts/flot/examples/time.html +71 -0
  67. data/public/javascripts/flot/examples/tracking.html +95 -0
  68. data/public/javascripts/flot/examples/turning-series.html +98 -0
  69. data/public/javascripts/flot/examples/visitors.html +90 -0
  70. data/public/javascripts/flot/examples/zooming.html +98 -0
  71. data/public/javascripts/flot/excanvas.js +1427 -0
  72. data/public/javascripts/flot/excanvas.min.js +1 -0
  73. data/public/javascripts/flot/jquery.colorhelpers.js +179 -0
  74. data/public/javascripts/flot/jquery.colorhelpers.min.js +1 -0
  75. data/public/javascripts/flot/jquery.flot.crosshair.js +167 -0
  76. data/public/javascripts/flot/jquery.flot.crosshair.min.js +1 -0
  77. data/public/javascripts/flot/jquery.flot.fillbetween.js +183 -0
  78. data/public/javascripts/flot/jquery.flot.fillbetween.min.js +1 -0
  79. data/public/javascripts/flot/jquery.flot.image.js +238 -0
  80. data/public/javascripts/flot/jquery.flot.image.min.js +1 -0
  81. data/public/javascripts/flot/jquery.flot.js +2599 -0
  82. data/public/javascripts/flot/jquery.flot.min.js +6 -0
  83. data/public/javascripts/flot/jquery.flot.navigate.js +336 -0
  84. data/public/javascripts/flot/jquery.flot.navigate.min.js +1 -0
  85. data/public/javascripts/flot/jquery.flot.pie.js +750 -0
  86. data/public/javascripts/flot/jquery.flot.pie.min.js +1 -0
  87. data/public/javascripts/flot/jquery.flot.resize.js +60 -0
  88. data/public/javascripts/flot/jquery.flot.resize.min.js +1 -0
  89. data/public/javascripts/flot/jquery.flot.selection.js +344 -0
  90. data/public/javascripts/flot/jquery.flot.selection.min.js +1 -0
  91. data/public/javascripts/flot/jquery.flot.stack.js +184 -0
  92. data/public/javascripts/flot/jquery.flot.stack.min.js +1 -0
  93. data/public/javascripts/flot/jquery.flot.symbol.js +70 -0
  94. data/public/javascripts/flot/jquery.flot.symbol.min.js +1 -0
  95. data/public/javascripts/flot/jquery.flot.threshold.js +103 -0
  96. data/public/javascripts/flot/jquery.flot.threshold.min.js +1 -0
  97. data/public/javascripts/flot/jquery.js +8316 -0
  98. data/public/javascripts/flot/jquery.min.js +23 -0
  99. data/public/javascripts/jquery.qtip.js +2675 -0
  100. data/public/javascripts/jquery.sparkline.js +1271 -0
  101. data/public/javascripts/races.js +245 -0
  102. data/public/stylesheets/sass/admin/races.sass +65 -70
  103. data/public/stylesheets/sass/jquery.flot.sass +416 -0
  104. data/public/stylesheets/sass/race_results.sass +38 -2
  105. data/radiant-race_results-extension.gemspec +1 -1
  106. metadata +95 -11
  107. data/public/javascripts/tablesorter.js +0 -3
@@ -0,0 +1,1271 @@
1
+ /**
2
+ *
3
+ * jquery.sparkline.js
4
+ *
5
+ * v1.6
6
+ * (c) Splunk, Inc
7
+ * Contact: Gareth Watts (gareth@splunk.com)
8
+ * http://omnipotent.net/jquery.sparkline/
9
+ *
10
+ * Generates inline sparkline charts from data supplied either to the method
11
+ * or inline in HTML
12
+ *
13
+ * Compatible with Internet Explorer 6.0+ and modern browsers equipped with the canvas tag
14
+ * (Firefox 2.0+, Safari, Opera, etc)
15
+ *
16
+ * License: New BSD License
17
+ *
18
+ * Copyright (c) 2010, Splunk Inc.
19
+ * All rights reserved.
20
+ *
21
+ * Redistribution and use in source and binary forms, with or without modification,
22
+ * are permitted provided that the following conditions are met:
23
+ *
24
+ * * Redistributions of source code must retain the above copyright notice,
25
+ * this list of conditions and the following disclaimer.
26
+ * * Redistributions in binary form must reproduce the above copyright notice,
27
+ * this list of conditions and the following disclaimer in the documentation
28
+ * and/or other materials provided with the distribution.
29
+ * * Neither the name of Splunk Inc nor the names of its contributors may
30
+ * be used to endorse or promote products derived from this software without
31
+ * specific prior written permission.
32
+ *
33
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
34
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
36
+ * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
38
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
40
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42
+ *
43
+ *
44
+ * Usage:
45
+ * $(selector).sparkline(values, options)
46
+ *
47
+ * If values is undefined or set to 'html' then the data values are read from the specified tag:
48
+ * <p>Sparkline: <span class="sparkline">1,4,6,6,8,5,3,5</span></p>
49
+ * $('.sparkline').sparkline();
50
+ * There must be no spaces in the enclosed data set
51
+ *
52
+ * Otherwise values must be an array of numbers or null values
53
+ * <p>Sparkline: <span id="sparkline1">This text replaced if the browser is compatible</span></p>
54
+ * $('#sparkline1').sparkline([1,4,6,6,8,5,3,5])
55
+ * $('#sparkline2').sparkline([1,4,6,null,null,5,3,5])
56
+ *
57
+ * Values can also be specified in an HTML comment, or as a values attribute:
58
+ * <p>Sparkline: <span class="sparkline"><!--1,4,6,6,8,5,3,5 --></span></p>
59
+ * <p>Sparkline: <span class="sparkline" values="1,4,6,6,8,5,3,5"></span></p>
60
+ * $('.sparkline').sparkline();
61
+ *
62
+ * For line charts, x values can also be specified:
63
+ * <p>Sparkline: <span class="sparkline">1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5</span></p>
64
+ * $('#sparkline1').sparkline([ [1,1], [2.7,4], [3.4,6], [5,6], [6,8], [8.7,5], [9,3], [10,5] ])
65
+ *
66
+ * By default, options should be passed in as teh second argument to the sparkline function:
67
+ * $('.sparkline').sparkline([1,2,3,4], {type: 'bar'})
68
+ *
69
+ * Options can also be set by passing them on the tag itself. This feature is disabled by default though
70
+ * as there's a slight performance overhead:
71
+ * $('.sparkline').sparkline([1,2,3,4], {enableTagOptions: true})
72
+ * <p>Sparkline: <span class="sparkline" sparkType="bar" sparkBarColor="red">loading</span></p>
73
+ * Prefix all options supplied as tag attribute with "spark" (configurable by setting tagOptionPrefix)
74
+ *
75
+ * Supported options:
76
+ * lineColor - Color of the line used for the chart
77
+ * fillColor - Color used to fill in the chart - Set to '' or false for a transparent chart
78
+ * width - Width of the chart - Defaults to 3 times the number of values in pixels
79
+ * height - Height of the chart - Defaults to the height of the containing element
80
+ * chartRangeMin - Specify the minimum value to use for the Y range of the chart - Defaults to the minimum value supplied
81
+ * chartRangeMax - Specify the maximum value to use for the Y range of the chart - Defaults to the maximum value supplied
82
+ * chartRangeClip - Clip out of range values to the max/min specified by chartRangeMin and chartRangeMax
83
+ * chartRangeMinX - Specify the minimum value to use for the X range of the chart - Defaults to the minimum value supplied
84
+ * chartRangeMaxX - Specify the maximum value to use for the X range of the chart - Defaults to the maximum value supplied
85
+ * composite - If true then don't erase any existing chart attached to the tag, but draw
86
+ * another chart over the top - Note that width and height are ignored if an
87
+ * existing chart is detected.
88
+ * tagValuesAttribute - Name of tag attribute to check for data values - Defaults to 'values'
89
+ * enableTagOptions - Whether to check tags for sparkline options
90
+ * tagOptionPrefix - Prefix used for options supplied as tag attributes - Defaults to 'spark'
91
+ *
92
+ * There are 7 types of sparkline, selected by supplying a "type" option of 'line' (default),
93
+ * 'bar', 'tristate', 'bullet', 'discrete', 'pie' or 'box'
94
+ * line - Line chart. Options:
95
+ * spotColor - Set to '' to not end each line in a circular spot
96
+ * minSpotColor - If set, color of spot at minimum value
97
+ * maxSpotColor - If set, color of spot at maximum value
98
+ * spotRadius - Radius in pixels
99
+ * lineWidth - Width of line in pixels
100
+ * normalRangeMin
101
+ * normalRangeMax - If set draws a filled horizontal bar between these two values marking the "normal"
102
+ * or expected range of values
103
+ * normalRangeColor - Color to use for the above bar
104
+ * drawNormalOnTop - Draw the normal range above the chart fill color if true
105
+ * defaultPixelsPerValue - Defaults to 3 pixels of width for each value in the chart
106
+ *
107
+ * bar - Bar chart. Options:
108
+ * barColor - Color of bars for postive values
109
+ * negBarColor - Color of bars for negative values
110
+ * zeroColor - Color of bars with zero values
111
+ * nullColor - Color of bars with null values - Defaults to omitting the bar entirely
112
+ * barWidth - Width of bars in pixels
113
+ * colorMap - Optional mappnig of values to colors to override the *BarColor values above
114
+ * can be an Array of values to control the color of individual bars
115
+ * barSpacing - Gap between bars in pixels
116
+ * zeroAxis - Centers the y-axis around zero if true
117
+ *
118
+ * tristate - Charts values of win (>0), lose (<0) or draw (=0)
119
+ * posBarColor - Color of win values
120
+ * negBarColor - Color of lose values
121
+ * zeroBarColor - Color of draw values
122
+ * barWidth - Width of bars in pixels
123
+ * barSpacing - Gap between bars in pixels
124
+ * colorMap - Optional mappnig of values to colors to override the *BarColor values above
125
+ * can be an Array of values to control the color of individual bars
126
+ *
127
+ * discrete - Options:
128
+ * lineHeight - Height of each line in pixels - Defaults to 30% of the graph height
129
+ * thesholdValue - Values less than this value will be drawn using thresholdColor instead of lineColor
130
+ * thresholdColor
131
+ *
132
+ * bullet - Values for bullet graphs msut be in the order: target, performance, range1, range2, range3, ...
133
+ * options:
134
+ * targetColor - The color of the vertical target marker
135
+ * targetWidth - The width of the target marker in pixels
136
+ * performanceColor - The color of the performance measure horizontal bar
137
+ * rangeColors - Colors to use for each qualitative range background color
138
+ *
139
+ * pie - Pie chart. Options:
140
+ * sliceColors - An array of colors to use for pie slices
141
+ * offset - Angle in degrees to offset the first slice - Try -90 or +90
142
+ *
143
+ * box - Box plot. Options:
144
+ * raw - Set to true to supply pre-computed plot points as values
145
+ * values should be: low_outlier, low_whisker, q1, median, q3, high_whisker, high_outlier
146
+ * When set to false you can supply any number of values and the box plot will
147
+ * be computed for you. Default is false.
148
+ * showOutliers - Set to true (default) to display outliers as circles
149
+ * outlierIRQ - Interquartile range used to determine outliers. Default 1.5
150
+ * boxLineColor - Outline color of the box
151
+ * boxFillColor - Fill color for the box
152
+ * whiskerColor - Line color used for whiskers
153
+ * outlierLineColor - Outline color of outlier circles
154
+ * outlierFillColor - Fill color of the outlier circles
155
+ * spotRadius - Radius of outlier circles
156
+ * medianColor - Line color of the median line
157
+ * target - Draw a target cross hair at the supplied value (default undefined)
158
+ *
159
+ *
160
+ *
161
+ * Examples:
162
+ * $('#sparkline1').sparkline(myvalues, { lineColor: '#f00', fillColor: false });
163
+ * $('.barsparks').sparkline('html', { type:'bar', height:'40px', barWidth:5 });
164
+ * $('#tristate').sparkline([1,1,-1,1,0,0,-1], { type:'tristate' }):
165
+ * $('#discrete').sparkline([1,3,4,5,5,3,4,5], { type:'discrete' });
166
+ * $('#bullet').sparkline([10,12,12,9,7], { type:'bullet' });
167
+ * $('#pie').sparkline([1,1,2], { type:'pie' });
168
+ */
169
+
170
+
171
+ (function($) {
172
+
173
+ /*
174
+ * Default configuration settings
175
+ */
176
+ var defaults = {
177
+ // Settings common to most/all chart types
178
+ common: {
179
+ type : 'line',
180
+ lineColor : '#8c8d8e',
181
+ fillColor : '#F2F0ED',
182
+ defaultPixelsPerValue : 5,
183
+ width : 'auto',
184
+ height : 'auto',
185
+ composite : false,
186
+ tagValuesAttribute: 'values',
187
+ tagOptionsPrefix: 'spark',
188
+ enableTagOptions: false
189
+ },
190
+ // Defaults for line charts
191
+ line: {
192
+ spotColor : '#006072',
193
+ spotRadius : 1.5,
194
+ minSpotColor : null,
195
+ maxSpotColor : null,
196
+ lineWidth: 1,
197
+ normalRangeMin : undefined,
198
+ normalRangeMax : undefined,
199
+ normalRangeColor : '#ccc',
200
+ drawNormalOnTop: false,
201
+ chartRangeMin : undefined,
202
+ chartRangeMax : undefined,
203
+ chartRangeMinX : undefined,
204
+ chartRangeMaxX : undefined
205
+ },
206
+ // Defaults for bar charts
207
+ bar: {
208
+ barColor : '#00f',
209
+ negBarColor : '#f44',
210
+ zeroColor: undefined,
211
+ nullColor: undefined,
212
+ zeroAxis : undefined,
213
+ barWidth : 4,
214
+ barSpacing : 1,
215
+ chartRangeMax: undefined,
216
+ chartRangeMin: undefined,
217
+ chartRangeClip: false,
218
+ colorMap : undefined
219
+ },
220
+ // Defaults for tristate charts
221
+ tristate: {
222
+ barWidth : 4,
223
+ barSpacing : 1,
224
+ posBarColor: '#6f6',
225
+ negBarColor : '#f44',
226
+ zeroBarColor : '#999',
227
+ colorMap : {}
228
+ },
229
+ // Defaults for discrete charts
230
+ discrete: {
231
+ lineHeight: 'auto',
232
+ thresholdColor: undefined,
233
+ thresholdValue : 0,
234
+ chartRangeMax: undefined,
235
+ chartRangeMin: undefined,
236
+ chartRangeClip: false
237
+ },
238
+ // Defaults for bullet charts
239
+ bullet: {
240
+ targetColor : 'red',
241
+ targetWidth : 3, // width of the target bar in pixels
242
+ performanceColor : 'blue',
243
+ rangeColors : ['#D3DAFE', '#A8B6FF', '#7F94FF' ],
244
+ base : undefined // set this to a number to change the base start number
245
+ },
246
+ // Defaults for pie charts
247
+ pie: {
248
+ sliceColors : ['#f00', '#0f0', '#00f']
249
+ },
250
+ // Defaults for box plots
251
+ box: {
252
+ raw: false,
253
+ boxLineColor: 'black',
254
+ boxFillColor: '#cdf',
255
+ whiskerColor: 'black',
256
+ outlierLineColor: '#333',
257
+ outlierFillColor: 'white',
258
+ medianColor: 'red',
259
+ showOutliers: true,
260
+ outlierIQR: 1.5,
261
+ spotRadius: 1.5,
262
+ target: undefined,
263
+ targetColor: '#4a2',
264
+ chartRangeMax: undefined,
265
+ chartRangeMin: undefined
266
+ }
267
+ };
268
+
269
+ // Provide a cross-browser interface to a few simple drawing primitives
270
+ var VCanvas_base, VCanvas_canvas, VCanvas_vml;
271
+ $.fn.simpledraw = function(width, height, use_existing) {
272
+ if (use_existing && this[0].VCanvas) {
273
+ return this[0].VCanvas;
274
+ }
275
+ if (width === undefined) {
276
+ width=$(this).innerWidth();
277
+ }
278
+ if (height === undefined) {
279
+ height=$(this).innerHeight();
280
+ }
281
+ if ($.browser.hasCanvas) {
282
+ return new VCanvas_canvas(width, height, this);
283
+ } else if ($.browser.msie) {
284
+ return new VCanvas_vml(width, height, this);
285
+ } else {
286
+ return false;
287
+ }
288
+ };
289
+
290
+ var pending = [];
291
+
292
+
293
+ $.fn.sparkline = function(uservalues, userOptions) {
294
+ return this.each(function() {
295
+ var options = new $.fn.sparkline.options(this, userOptions);
296
+ var render = function() {
297
+ var values, width, height;
298
+ if (uservalues==='html' || uservalues===undefined) {
299
+ var vals = this.getAttribute(options.get('tagValuesAttribute'));
300
+ if (vals===undefined || vals===null) {
301
+ vals = $(this).html();
302
+ }
303
+ values = vals.replace(/(^\s*<!--)|(-->\s*$)|\s+/g, '').split(',');
304
+ } else {
305
+ values = uservalues;
306
+ }
307
+
308
+ width = options.get('width')=='auto' ? values.length*options.get('defaultPixelsPerValue') : options.get('width');
309
+ if (options.get('height') == 'auto') {
310
+ if (!options.get('composite') || !this.VCanvas) {
311
+ // must be a better way to get the line height
312
+ var tmp = document.createElement('span');
313
+ tmp.innerHTML = 'a';
314
+ $(this).html(tmp);
315
+ height = $(tmp).innerHeight();
316
+ $(tmp).remove();
317
+ }
318
+ } else {
319
+ height = options.get('height');
320
+ }
321
+
322
+ $.fn.sparkline[options.get('type')].call(this, values, options, width, height);
323
+ };
324
+ // jQuery 1.3.0 completely changed the meaning of :hidden :-/
325
+ if (($(this).html() && $(this).is(':hidden')) || ($.fn.jquery < "1.3.0" && $(this).parents().is(':hidden')) || !$(this).parents('body').length) {
326
+ pending.push([this, render]);
327
+ } else {
328
+ render.call(this);
329
+ }
330
+ });
331
+ };
332
+
333
+ $.fn.sparkline.defaults = defaults;
334
+
335
+
336
+ $.sparkline_display_visible = function() {
337
+ for (var i=pending.length-1; i>=0; i--) {
338
+ var el = pending[i][0];
339
+ if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {
340
+ pending[i][1].call(el);
341
+ pending.splice(i, 1);
342
+ }
343
+ }
344
+ };
345
+
346
+
347
+ /**
348
+ * User option handler
349
+ */
350
+ var UNSET_OPTION = {};
351
+ var normalizeValue = function(val) {
352
+ switch(val) {
353
+ case 'undefined':
354
+ val = undefined;
355
+ break;
356
+ case 'null':
357
+ val = null;
358
+ break;
359
+ case 'true':
360
+ val = true;
361
+ break;
362
+ case 'false':
363
+ val = false;
364
+ break;
365
+ default:
366
+ var nf = parseFloat(val);
367
+ if (val == nf) {
368
+ val = nf;
369
+ }
370
+ }
371
+ return val;
372
+ };
373
+ $.fn.sparkline.options = function(tag, userOptions) {
374
+ var extendedOptions;
375
+ this.userOptions = userOptions = userOptions || {};
376
+ this.tag = tag;
377
+ this.tagValCache = {};
378
+ var defaults = $.fn.sparkline.defaults;
379
+ var base = defaults.common;
380
+ this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);
381
+
382
+ var tagOptionType = this.getTagSetting('type');
383
+ if (tagOptionType === UNSET_OPTION) {
384
+ extendedOptions = defaults[userOptions.type || base.type];
385
+ } else {
386
+ extendedOptions = defaults[tagOptionType];
387
+ }
388
+ this.mergedOptions = $.extend({}, base, extendedOptions, userOptions);
389
+ };
390
+
391
+
392
+ $.fn.sparkline.options.prototype.getTagSetting = function(key) {
393
+ var val, i, prefix = this.tagOptionsPrefix;
394
+ if (prefix === false || prefix === undefined) {
395
+ return UNSET_OPTION;
396
+ }
397
+ if (this.tagValCache.hasOwnProperty(key)) {
398
+ val = this.tagValCache.key;
399
+ } else {
400
+ val = this.tag.getAttribute(prefix + key);
401
+ if (val === undefined || val === null) {
402
+ val = UNSET_OPTION;
403
+ } else if (val.substr(0, 1) == '[') {
404
+ val = val.substr(1, val.length-2).split(',');
405
+ for(i=val.length; i--;) {
406
+ val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, ''));
407
+ }
408
+ } else if (val.substr(0, 1) == '{') {
409
+ var pairs= val.substr(1, val.length-2).split(',');
410
+ val = {};
411
+ for(i=pairs.length; i--;) {
412
+ var keyval = pairs[i].split(':', 2);
413
+ val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, ''));
414
+ }
415
+ } else {
416
+ val = normalizeValue(val);
417
+ }
418
+ this.tagValCache.key = val;
419
+ }
420
+ return val;
421
+ };
422
+
423
+ $.fn.sparkline.options.prototype.get = function(key) {
424
+ var tagOption = this.getTagSetting(key);
425
+ if (tagOption !== UNSET_OPTION) {
426
+ return tagOption;
427
+ }
428
+ return this.mergedOptions[key];
429
+ };
430
+
431
+
432
+ /**
433
+ * Line charts
434
+ */
435
+ $.fn.sparkline.line = function(values, options, width, height) {
436
+ var xvalues = [], yvalues = [], yminmax = [];
437
+ for (var i=0; i<values.length; i++) {
438
+ var val = values[i];
439
+ var isstr = typeof(values[i])=='string';
440
+ var isarray = typeof(values[i])=='object' && values[i] instanceof Array;
441
+ var sp = isstr && values[i].split(':');
442
+ if (isstr && sp.length == 2) { // x:y
443
+ xvalues.push(Number(sp[0]));
444
+ yvalues.push(Number(sp[1]));
445
+ yminmax.push(Number(sp[1]));
446
+ } else if (isarray) {
447
+ xvalues.push(val[0]);
448
+ yvalues.push(val[1]);
449
+ yminmax.push(val[1]);
450
+ } else {
451
+ xvalues.push(i);
452
+ if (values[i]===null || values[i]=='null') {
453
+ yvalues.push(null);
454
+ } else {
455
+ yvalues.push(Number(val));
456
+ yminmax.push(Number(val));
457
+ }
458
+ }
459
+ }
460
+ if (options.get('xvalues')) {
461
+ xvalues = options.get('xvalues');
462
+ }
463
+
464
+ var maxy = Math.max.apply(Math, yminmax);
465
+ var maxyval = maxy;
466
+ var miny = Math.min.apply(Math, yminmax);
467
+ var minyval = miny;
468
+
469
+ var maxx = Math.max.apply(Math, xvalues);
470
+ var minx = Math.min.apply(Math, xvalues);
471
+
472
+ var normalRangeMin = options.get('normalRangeMin');
473
+ var normalRangeMax = options.get('normalRangeMax');
474
+
475
+ if (normalRangeMin!==undefined) {
476
+ if (normalRangeMin<miny) {
477
+ miny = normalRangeMin;
478
+ }
479
+ if (normalRangeMax>maxy) {
480
+ maxy = normalRangeMax;
481
+ }
482
+ }
483
+ if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMin')<miny)) {
484
+ miny = options.get('chartRangeMin');
485
+ }
486
+ if (options.get('chartRangeMax')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMax')>maxy)) {
487
+ maxy = options.get('chartRangeMax');
488
+ }
489
+ if (options.get('chartRangeMinX')!==undefined && (options.get('chartRangeClipX') || options.get('chartRangeMinX')<minx)) {
490
+ minx = options.get('chartRangeMinX');
491
+ }
492
+ if (options.get('chartRangeMaxX')!==undefined && (options.get('chartRangeClipX') || options.get('chartRangeMaxX')>maxx)) {
493
+ maxx = options.get('chartRangeMaxX');
494
+ }
495
+ var rangex = maxx-minx === 0 ? 1 : maxx-minx;
496
+ var rangey = maxy-miny === 0 ? 1 : maxy-miny;
497
+ var vl = yvalues.length-1;
498
+
499
+ if (vl<1) {
500
+ this.innerHTML = '';
501
+ return;
502
+ }
503
+
504
+ var target = $(this).simpledraw(width, height, options.get('composite'));
505
+ if (target) {
506
+ var canvas_width = target.pixel_width;
507
+ var canvas_height = target.pixel_height;
508
+ var canvas_top = 0;
509
+ var canvas_left = 0;
510
+
511
+ var spotRadius = options.get('spotRadius');
512
+ if (spotRadius && (canvas_width < (spotRadius*4) || canvas_height < (spotRadius*4))) {
513
+ spotRadius = 0;
514
+ }
515
+ if (spotRadius) {
516
+ // adjust the canvas size as required so that spots will fit
517
+ if (options.get('minSpotColor') || (options.get('spotColor') && yvalues[vl]==miny)) {
518
+ canvas_height -= Math.ceil(spotRadius);
519
+ }
520
+ if (options.get('maxSpotColor') || (options.get('spotColor') && yvalues[vl]==maxy)) {
521
+ canvas_height -= Math.ceil(spotRadius);
522
+ canvas_top += Math.ceil(spotRadius);
523
+ }
524
+ if (options.get('minSpotColor') || options.get('maxSpotColor') && (yvalues[0]==miny || yvalues[0]==maxy)) {
525
+ canvas_left += Math.ceil(spotRadius);
526
+ canvas_width -= Math.ceil(spotRadius);
527
+ }
528
+ if (options.get('spotColor') || (options.get('minSpotColor') || options.get('maxSpotColor') && (yvalues[vl]==miny||yvalues[vl]==maxy))) {
529
+ canvas_width -= Math.ceil(spotRadius);
530
+ }
531
+ }
532
+
533
+
534
+ canvas_height--;
535
+
536
+ var drawNormalRange = function() {
537
+ if (normalRangeMin!==undefined) {
538
+ var ytop = canvas_top+Math.round(canvas_height-(canvas_height*((normalRangeMax-miny)/rangey)));
539
+ var height = Math.round((canvas_height*(normalRangeMax-normalRangeMin))/rangey);
540
+ target.drawRect(canvas_left, ytop, canvas_width, height, undefined, options.get('normalRangeColor'));
541
+ }
542
+ };
543
+
544
+ if (!options.get('drawNormalOnTop')) {
545
+ drawNormalRange();
546
+ }
547
+
548
+ var path = [];
549
+ var paths = [path];
550
+ var x, y, vlen=yvalues.length;
551
+ for(i=0; i<vlen; i++) {
552
+ x=xvalues[i];
553
+ y=yvalues[i];
554
+ if (y===null) {
555
+ if (i) {
556
+ if (yvalues[i-1]!==null) {
557
+ path = [];
558
+ paths.push(path);
559
+ }
560
+ }
561
+ } else {
562
+ if (y < miny) {
563
+ y=miny;
564
+ }
565
+ if (y > maxy) {
566
+ y=maxy;
567
+ }
568
+ if (!path.length) {
569
+ // previous value was null
570
+ path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+canvas_height]);
571
+ }
572
+ path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+Math.round(canvas_height-(canvas_height*((y-miny)/rangey)))]);
573
+ }
574
+ }
575
+ var lineshapes = [];
576
+ var fillshapes = [];
577
+ var plen=paths.length;
578
+ for(i=0; i<plen; i++) {
579
+ path = paths[i];
580
+ if (!path.length) {
581
+ continue; // last value was null
582
+ }
583
+ if (options.get('fillColor')) {
584
+ path.push([path[path.length-1][0], canvas_top+canvas_height-1]);
585
+ fillshapes.push(path.slice(0));
586
+ path.pop();
587
+ }
588
+ // if there's only a single point in this path, then we want to display it as a vertical line
589
+ // which means we keep path[0] as is
590
+ if (path.length>2) {
591
+ // else we want the first value
592
+ path[0] = [ path[0][0], path[1][1] ];
593
+ }
594
+ lineshapes.push(path);
595
+ }
596
+
597
+ // draw the fill first, then optionally the normal range, then the line on top of that
598
+ plen = fillshapes.length;
599
+ for(i=0; i<plen; i++) {
600
+ target.drawShape(fillshapes[i], undefined, options.get('fillColor'));
601
+ }
602
+
603
+ if (options.get('drawNormalOnTop')) {
604
+ drawNormalRange();
605
+ }
606
+
607
+ plen = lineshapes.length;
608
+ for(i=0; i<plen; i++) {
609
+ target.drawShape(lineshapes[i], options.get('lineColor'), undefined, options.get('lineWidth'));
610
+ }
611
+
612
+ if (spotRadius && options.get('spotColor')) {
613
+ target.drawCircle(canvas_left+Math.round(xvalues[xvalues.length-1]*(canvas_width/rangex)), canvas_top+Math.round(canvas_height-(canvas_height*((yvalues[vl]-miny)/rangey))), spotRadius, undefined, options.get('spotColor'));
614
+ }
615
+ if (maxy!=minyval) {
616
+ if (spotRadius && options.get('minSpotColor')) {
617
+ x = xvalues[$.inArray(minyval, yvalues)];
618
+ target.drawCircle(canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+Math.round(canvas_height-(canvas_height*((minyval-miny)/rangey))), spotRadius, undefined, options.get('minSpotColor'));
619
+ }
620
+ if (spotRadius && options.get('maxSpotColor')) {
621
+ x = xvalues[$.inArray(maxyval, yvalues)];
622
+ target.drawCircle(canvas_left+Math.round((x-minx)*(canvas_width/rangex)), canvas_top+Math.round(canvas_height-(canvas_height*((maxyval-miny)/rangey))), spotRadius, undefined, options.get('maxSpotColor'));
623
+ }
624
+ }
625
+
626
+ } else {
627
+ // Remove the tag contents if sparklines aren't supported
628
+ this.innerHTML = '';
629
+ }
630
+ };
631
+
632
+
633
+ /**
634
+ * Bar charts
635
+ */
636
+ $.fn.sparkline.bar = function(values, options, width, height) {
637
+ width = (values.length * options.get('barWidth')) + ((values.length-1) * options.get('barSpacing'));
638
+ var num_values = [];
639
+ for(var i=0, vlen=values.length; i<vlen; i++) {
640
+ if (values[i]=='null' || values[i]===null) {
641
+ values[i] = null;
642
+ } else {
643
+ values[i] = Number(values[i]);
644
+ num_values.push(Number(values[i]));
645
+ }
646
+ }
647
+ var max = Math.max.apply(Math, num_values),
648
+ min = Math.min.apply(Math, num_values);
649
+ if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMin')<min)) {
650
+ min = options.get('chartRangeMin');
651
+ }
652
+ if (options.get('chartRangeMax')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMax')>max)) {
653
+ max = options.get('chartRangeMax');
654
+ }
655
+ var zeroAxis = options.get('zeroAxis');
656
+ if (zeroAxis === undefined) {
657
+ zeroAxis = min<0;
658
+ }
659
+ var range = max-min === 0 ? 1 : max-min;
660
+
661
+ var colorMapByIndex, colorMapByValue;
662
+ if ($.isArray(options.get('colorMap'))) {
663
+ colorMapByIndex = options.get('colorMap');
664
+ colorMapByValue = null;
665
+ } else {
666
+ colorMapByIndex = null;
667
+ colorMapByValue = options.get('colorMap');
668
+ }
669
+
670
+ var target = $(this).simpledraw(width, height, options.get('composite'));
671
+ if (target) {
672
+ var color,
673
+ canvas_height = target.pixel_height,
674
+ yzero = min<0 && zeroAxis ? canvas_height-Math.round(canvas_height * (Math.abs(min)/range))-1 : canvas_height-1;
675
+
676
+ for(i=values.length; i--;) {
677
+ var x = i*(options.get('barWidth')+options.get('barSpacing')),
678
+ y,
679
+ val = values[i];
680
+ if (val===null) {
681
+ if (options.get('nullColor')) {
682
+ color = options.get('nullColor');
683
+ val = (zeroAxis && min<0) ? 0 : min;
684
+ height = 1;
685
+ y = (zeroAxis && min<0) ? yzero : canvas_height - height;
686
+ } else {
687
+ continue;
688
+ }
689
+ } else {
690
+ if (val < min) {
691
+ val=min;
692
+ }
693
+ if (val > max) {
694
+ val=max;
695
+ }
696
+ color = (val < 0) ? options.get('negBarColor') : options.get('barColor');
697
+ if (zeroAxis && min<0) {
698
+ height = Math.round(canvas_height*((Math.abs(val)/range)))+1;
699
+ y = (val < 0) ? yzero : yzero-height;
700
+ } else {
701
+ height = Math.round(canvas_height*((val-min)/range))+1;
702
+ y = canvas_height-height;
703
+ }
704
+ if (val===0 && options.get('zeroColor')!==undefined) {
705
+ color = options.get('zeroColor');
706
+ }
707
+ if (colorMapByValue && colorMapByValue[val]) {
708
+ color = colorMapByValue[val];
709
+ } else if (colorMapByIndex && colorMapByIndex.length>i) {
710
+ color = colorMapByIndex[i];
711
+ }
712
+ if (color===null) {
713
+ continue;
714
+ }
715
+ }
716
+ target.drawRect(x, y, options.get('barWidth')-1, height-1, color, color);
717
+ }
718
+ } else {
719
+ // Remove the tag contents if sparklines aren't supported
720
+ this.innerHTML = '';
721
+ }
722
+ };
723
+
724
+
725
+ /**
726
+ * Tristate charts
727
+ */
728
+ $.fn.sparkline.tristate = function(values, options, width, height) {
729
+ values = $.map(values, Number);
730
+ width = (values.length * options.get('barWidth')) + ((values.length-1) * options.get('barSpacing'));
731
+
732
+ var colorMapByIndex, colorMapByValue;
733
+ if ($.isArray(options.get('colorMap'))) {
734
+ colorMapByIndex = options.get('colorMap');
735
+ colorMapByValue = null;
736
+ } else {
737
+ colorMapByIndex = null;
738
+ colorMapByValue = options.get('colorMap');
739
+ }
740
+
741
+ var target = $(this).simpledraw(width, height, options.get('composite'));
742
+ if (target) {
743
+ var canvas_height = target.pixel_height,
744
+ half_height = Math.round(canvas_height/2);
745
+
746
+ for(var i=values.length; i--;) {
747
+ var x = i*(options.get('barWidth')+options.get('barSpacing')),
748
+ y, color;
749
+ if (values[i] < 0) {
750
+ y = half_height;
751
+ height = half_height-1;
752
+ color = options.get('negBarColor');
753
+ } else if (values[i] > 0) {
754
+ y = 0;
755
+ height = half_height-1;
756
+ color = options.get('posBarColor');
757
+ } else {
758
+ y = half_height-1;
759
+ height = 2;
760
+ color = options.get('zeroBarColor');
761
+ }
762
+ if (colorMapByValue && colorMapByValue[values[i]]) {
763
+ color = colorMapByValue[values[i]];
764
+ } else if (colorMapByIndex && colorMapByIndex.length>i) {
765
+ color = colorMapByIndex[i];
766
+ }
767
+ if (color===null) {
768
+ continue;
769
+ }
770
+ target.drawRect(x, y, options.get('barWidth')-1, height-1, color, color);
771
+ }
772
+ } else {
773
+ // Remove the tag contents if sparklines aren't supported
774
+ this.innerHTML = '';
775
+ }
776
+ };
777
+
778
+
779
+ /**
780
+ * Discrete charts
781
+ */
782
+ $.fn.sparkline.discrete = function(values, options, width, height) {
783
+ values = $.map(values, Number);
784
+ width = options.get('width')=='auto' ? values.length*2 : width;
785
+ var interval = Math.floor(width / values.length);
786
+
787
+ var target = $(this).simpledraw(width, height, options.get('composite'));
788
+ if (target) {
789
+ var canvas_height = target.pixel_height,
790
+ line_height = options.get('lineHeight') == 'auto' ? Math.round(canvas_height * 0.3) : options.get('lineHeight'),
791
+ pheight = canvas_height - line_height,
792
+ min = Math.min.apply(Math, values),
793
+ max = Math.max.apply(Math, values);
794
+ if (options.get('chartRangeMin')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMin')<min)) {
795
+ min = options.get('chartRangeMin');
796
+ }
797
+ if (options.get('chartRangeMax')!==undefined && (options.get('chartRangeClip') || options.get('chartRangeMax')>max)) {
798
+ max = options.get('chartRangeMax');
799
+ }
800
+ var range = max-min;
801
+
802
+ for(var i=values.length; i--;) {
803
+ var val = values[i];
804
+ if (val < min) {
805
+ val=min;
806
+ }
807
+ if (val > max) {
808
+ val=max;
809
+ }
810
+ var x = (i*interval),
811
+ ytop = Math.round(pheight-pheight*((val-min)/range));
812
+ target.drawLine(x, ytop, x, ytop+line_height, (options.get('thresholdColor') && val < options.get('thresholdValue')) ? options.get('thresholdColor') : options.get('lineColor'));
813
+ }
814
+ } else {
815
+ // Remove the tag contents if sparklines aren't supported
816
+ this.innerHTML = '';
817
+ }
818
+
819
+ };
820
+
821
+
822
+ /**
823
+ * Bullet charts
824
+ */
825
+ $.fn.sparkline.bullet = function(values, options, width, height) {
826
+ values = $.map(values, Number);
827
+ // target, performance, range1, range2, range3
828
+
829
+ width = options.get('width')=='auto' ? '4.0em' : width;
830
+
831
+ var target = $(this).simpledraw(width, height, options.get('composite'));
832
+ if (target && values.length>1) {
833
+ var canvas_width = target.pixel_width-Math.ceil(options.get('targetWidth')/2),
834
+ canvas_height = target.pixel_height,
835
+ min = Math.min.apply(Math, values),
836
+ max = Math.max.apply(Math, values);
837
+
838
+ if (options.get('base') === undefined) {
839
+ min = min < 0 ? min : 0;
840
+ } else {
841
+ min = options.get('base');
842
+ }
843
+ var range = max-min;
844
+
845
+ // draw range values
846
+ for(var i=2, vlen=values.length; i<vlen; i++) {
847
+ var rangeval = values[i],
848
+ rangewidth = Math.round(canvas_width*((rangeval-min)/range));
849
+ target.drawRect(0, 0, rangewidth-1, canvas_height-1, options.get('rangeColors')[i-2], options.get('rangeColors')[i-2]);
850
+ }
851
+
852
+ // draw the performance bar
853
+ var perfval = values[1],
854
+ perfwidth = Math.round(canvas_width*((perfval-min)/range));
855
+ target.drawRect(0, Math.round(canvas_height*0.3), perfwidth-1, Math.round(canvas_height*0.4)-1, options.get('performanceColor'), options.get('performanceColor'));
856
+
857
+ // draw the target line
858
+ var targetval = values[0],
859
+ x = Math.round(canvas_width*((targetval-min)/range)-(options.get('targetWidth')/2)),
860
+ targettop = Math.round(canvas_height*0.10),
861
+ targetheight = canvas_height-(targettop*2);
862
+ target.drawRect(x, targettop, options.get('targetWidth')-1, targetheight-1, options.get('targetColor'), options.get('targetColor'));
863
+ } else {
864
+ // Remove the tag contents if sparklines aren't supported
865
+ this.innerHTML = '';
866
+ }
867
+ };
868
+
869
+
870
+ /**
871
+ * Pie charts
872
+ */
873
+ $.fn.sparkline.pie = function(values, options, width, height) {
874
+ values = $.map(values, Number);
875
+ width = options.get('width')=='auto' ? height : width;
876
+
877
+ var target = $(this).simpledraw(width, height, options.get('composite'));
878
+ if (target && values.length>1) {
879
+ var canvas_width = target.pixel_width,
880
+ canvas_height = target.pixel_height,
881
+ radius = Math.floor(Math.min(canvas_width, canvas_height)/2),
882
+ total = 0,
883
+ next = 0,
884
+ circle = 2*Math.PI;
885
+
886
+ for(var i=values.length; i--;) {
887
+ total += values[i];
888
+ }
889
+
890
+ if (options.get('offset')) {
891
+ next += (2*Math.PI)*(options.get('offset')/360);
892
+ }
893
+ var vlen = values.length;
894
+ for(i=0; i<vlen; i++) {
895
+ var start = next;
896
+ var end = next;
897
+ if (total > 0) { // avoid divide by zero
898
+ end = next + (circle*(values[i]/total));
899
+ }
900
+ target.drawPieSlice(radius, radius, radius, start, end, undefined, options.get('sliceColors')[i % options.get('sliceColors').length]);
901
+ next = end;
902
+ }
903
+ }
904
+ };
905
+
906
+
907
+ /**
908
+ * Box plots
909
+ */
910
+ var quartile = function(values, q) {
911
+ if (q==2) {
912
+ var vl2 = Math.floor(values.length/2);
913
+ return values.length % 2 ? values[vl2] : (values[vl2]+values[vl2+1])/2;
914
+ } else {
915
+ var vl4 = Math.floor(values.length/4);
916
+ return values.length % 2 ? (values[vl4*q]+values[vl4*q+1])/2 : values[vl4*q];
917
+ }
918
+ };
919
+
920
+ $.fn.sparkline.box = function(values, options, width, height) {
921
+ values = $.map(values, Number);
922
+ width = options.get('width')=='auto' ? '4.0em' : width;
923
+
924
+ var minvalue = options.get('chartRangeMin')===undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'),
925
+ maxvalue = options.get('chartRangeMax')===undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'),
926
+ target = $(this).simpledraw(width, height, options.get('composite')),
927
+ vlen = values.length,
928
+ lwhisker, loutlier, q1, q2, q3, rwhisker, routlier;
929
+
930
+ if (target && values.length>1) {
931
+ var canvas_width = target.pixel_width,
932
+ canvas_height = target.pixel_height;
933
+ if (options.get('raw')) {
934
+ if (options.get('showOutliers') && values.length>5) {
935
+ loutlier=values[0]; lwhisker=values[1]; q1=values[2]; q2=values[3]; q3=values[4]; rwhisker=values[5]; routlier=values[6];
936
+ } else {
937
+ lwhisker=values[0]; q1=values[1]; q2=values[2]; q3=values[3]; rwhisker=values[4];
938
+ }
939
+ } else {
940
+ values.sort(function(a, b) { return a-b; });
941
+ q1 = quartile(values, 1);
942
+ q2 = quartile(values, 2);
943
+ q3 = quartile(values, 3);
944
+ var iqr = q3-q1;
945
+ if (options.get('showOutliers')) {
946
+ lwhisker=undefined; rwhisker=undefined;
947
+ for(var i=0; i<vlen; i++) {
948
+ if (lwhisker===undefined && values[i] > q1-(iqr*options.get('outlierIQR'))) {
949
+ lwhisker = values[i];
950
+ }
951
+ if (values[i] < q3+(iqr*options.get('outlierIQR'))) {
952
+ rwhisker = values[i];
953
+ }
954
+ }
955
+ loutlier = values[0];
956
+ routlier = values[vlen-1];
957
+ } else {
958
+ lwhisker = values[0];
959
+ rwhisker = values[vlen-1];
960
+ }
961
+ }
962
+
963
+ var unitsize = canvas_width / (maxvalue-minvalue+1),
964
+ canvas_left = 0;
965
+ if (options.get('showOutliers')) {
966
+ canvas_left = Math.ceil(options.get('spotRadius'));
967
+ canvas_width -= 2*Math.ceil(options.get('spotRadius'));
968
+ unitsize = canvas_width / (maxvalue-minvalue+1);
969
+ if (loutlier < lwhisker) {
970
+ target.drawCircle((loutlier-minvalue)*unitsize+canvas_left, canvas_height/2, options.get('spotRadius'), options.get('outlierLineColor'), options.get('outlierFillColor'));
971
+ }
972
+ if (routlier > rwhisker) {
973
+ target.drawCircle((routlier-minvalue)*unitsize+canvas_left, canvas_height/2, options.get('spotRadius'), options.get('outlierLineColor'), options.get('outlierFillColor'));
974
+ }
975
+ }
976
+
977
+ // box
978
+ target.drawRect(
979
+ Math.round((q1-minvalue)*unitsize+canvas_left),
980
+ Math.round(canvas_height*0.1),
981
+ Math.round((q3-q1)*unitsize),
982
+ Math.round(canvas_height*0.8),
983
+ options.get('boxLineColor'),
984
+ options.get('boxFillColor'));
985
+ // left whisker
986
+ target.drawLine(
987
+ Math.round((lwhisker-minvalue)*unitsize+canvas_left),
988
+ Math.round(canvas_height/2),
989
+ Math.round((q1-minvalue)*unitsize+canvas_left),
990
+ Math.round(canvas_height/2),
991
+ options.get('lineColor'));
992
+ target.drawLine(
993
+ Math.round((lwhisker-minvalue)*unitsize+canvas_left),
994
+ Math.round(canvas_height/4),
995
+ Math.round((lwhisker-minvalue)*unitsize+canvas_left),
996
+ Math.round(canvas_height-canvas_height/4),
997
+ options.get('whiskerColor'));
998
+ // right whisker
999
+ target.drawLine(Math.round((rwhisker-minvalue)*unitsize+canvas_left),
1000
+ Math.round(canvas_height/2),
1001
+ Math.round((q3-minvalue)*unitsize+canvas_left),
1002
+ Math.round(canvas_height/2),
1003
+ options.get('lineColor'));
1004
+ target.drawLine(
1005
+ Math.round((rwhisker-minvalue)*unitsize+canvas_left),
1006
+ Math.round(canvas_height/4),
1007
+ Math.round((rwhisker-minvalue)*unitsize+canvas_left),
1008
+ Math.round(canvas_height-canvas_height/4),
1009
+ options.get('whiskerColor'));
1010
+ // median line
1011
+ target.drawLine(
1012
+ Math.round((q2-minvalue)*unitsize+canvas_left),
1013
+ Math.round(canvas_height*0.1),
1014
+ Math.round((q2-minvalue)*unitsize+canvas_left),
1015
+ Math.round(canvas_height*0.9),
1016
+ options.get('medianColor'));
1017
+ if (options.get('target')) {
1018
+ var size = Math.ceil(options.get('spotRadius'));
1019
+ target.drawLine(
1020
+ Math.round((options.get('target')-minvalue)*unitsize+canvas_left),
1021
+ Math.round((canvas_height/2)-size),
1022
+ Math.round((options.get('target')-minvalue)*unitsize+canvas_left),
1023
+ Math.round((canvas_height/2)+size),
1024
+ options.get('targetColor'));
1025
+ target.drawLine(
1026
+ Math.round((options.get('target')-minvalue)*unitsize+canvas_left-size),
1027
+ Math.round(canvas_height/2),
1028
+ Math.round((options.get('target')-minvalue)*unitsize+canvas_left+size),
1029
+ Math.round(canvas_height/2),
1030
+ options.get('targetColor'));
1031
+ }
1032
+ } else {
1033
+ // Remove the tag contents if sparklines aren't supported
1034
+ this.innerHTML = '';
1035
+ }
1036
+ };
1037
+
1038
+
1039
+ // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier
1040
+ // This is accessible as $(foo).simpledraw()
1041
+
1042
+ if ($.browser.msie && !document.namespaces.v) {
1043
+ document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML');
1044
+ }
1045
+
1046
+ if ($.browser.hasCanvas === undefined) {
1047
+ var t = document.createElement('canvas');
1048
+ $.browser.hasCanvas = t.getContext!==undefined;
1049
+ }
1050
+
1051
+ VCanvas_base = function(width, height, target) {
1052
+ };
1053
+
1054
+ VCanvas_base.prototype = {
1055
+ init : function(width, height, target) {
1056
+ this.width = width;
1057
+ this.height = height;
1058
+ this.target = target;
1059
+ if (target[0]) {
1060
+ target=target[0];
1061
+ }
1062
+ target.VCanvas = this;
1063
+ },
1064
+
1065
+ drawShape : function(path, lineColor, fillColor, lineWidth) {
1066
+ alert('drawShape not implemented');
1067
+ },
1068
+
1069
+ drawLine : function(x1, y1, x2, y2, lineColor, lineWidth) {
1070
+ return this.drawShape([ [x1,y1], [x2,y2] ], lineColor, lineWidth);
1071
+ },
1072
+
1073
+ drawCircle : function(x, y, radius, lineColor, fillColor) {
1074
+ alert('drawCircle not implemented');
1075
+ },
1076
+
1077
+ drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) {
1078
+ alert('drawPieSlice not implemented');
1079
+ },
1080
+
1081
+ drawRect : function(x, y, width, height, lineColor, fillColor) {
1082
+ alert('drawRect not implemented');
1083
+ },
1084
+
1085
+ getElement : function() {
1086
+ return this.canvas;
1087
+ },
1088
+
1089
+ _insert : function(el, target) {
1090
+ $(target).html(el);
1091
+ }
1092
+ };
1093
+
1094
+ VCanvas_canvas = function(width, height, target) {
1095
+ return this.init(width, height, target);
1096
+ };
1097
+
1098
+ VCanvas_canvas.prototype = $.extend(new VCanvas_base(), {
1099
+ _super : VCanvas_base.prototype,
1100
+
1101
+ init : function(width, height, target) {
1102
+ this._super.init(width, height, target);
1103
+ this.canvas = document.createElement('canvas');
1104
+ if (target[0]) {
1105
+ target=target[0];
1106
+ }
1107
+ target.VCanvas = this;
1108
+ $(this.canvas).css({ display:'inline-block', width:width, height:height, verticalAlign:'top' });
1109
+ this._insert(this.canvas, target);
1110
+ this.pixel_height = $(this.canvas).height();
1111
+ this.pixel_width = $(this.canvas).width();
1112
+ this.canvas.width = this.pixel_width;
1113
+ this.canvas.height = this.pixel_height;
1114
+ $(this.canvas).css({width: this.pixel_width, height: this.pixel_height});
1115
+ },
1116
+
1117
+ _getContext : function(lineColor, fillColor, lineWidth) {
1118
+ var context = this.canvas.getContext('2d');
1119
+ if (lineColor !== undefined) {
1120
+ context.strokeStyle = lineColor;
1121
+ }
1122
+ context.lineWidth = lineWidth===undefined ? 1 : lineWidth;
1123
+ if (fillColor !== undefined) {
1124
+ context.fillStyle = fillColor;
1125
+ }
1126
+ return context;
1127
+ },
1128
+
1129
+ drawShape : function(path, lineColor, fillColor, lineWidth) {
1130
+ var context = this._getContext(lineColor, fillColor, lineWidth);
1131
+ context.beginPath();
1132
+ context.moveTo(path[0][0]+0.5, path[0][1]+0.5);
1133
+ for(var i=1, plen=path.length; i<plen; i++) {
1134
+ context.lineTo(path[i][0]+0.5, path[i][1]+0.5); // the 0.5 offset gives us crisp pixel-width lines
1135
+ }
1136
+ if (lineColor !== undefined) {
1137
+ context.stroke();
1138
+ }
1139
+ if (fillColor !== undefined) {
1140
+ context.fill();
1141
+ }
1142
+ },
1143
+
1144
+ drawCircle : function(x, y, radius, lineColor, fillColor) {
1145
+ var context = this._getContext(lineColor, fillColor);
1146
+ context.beginPath();
1147
+ context.arc(x, y, radius, 0, 2*Math.PI, false);
1148
+ if (lineColor !== undefined) {
1149
+ context.stroke();
1150
+ }
1151
+ if (fillColor !== undefined) {
1152
+ context.fill();
1153
+ }
1154
+ },
1155
+
1156
+ drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) {
1157
+ var context = this._getContext(lineColor, fillColor);
1158
+ context.beginPath();
1159
+ context.moveTo(x, y);
1160
+ context.arc(x, y, radius, startAngle, endAngle, false);
1161
+ context.lineTo(x, y);
1162
+ context.closePath();
1163
+ if (lineColor !== undefined) {
1164
+ context.stroke();
1165
+ }
1166
+ if (fillColor) {
1167
+ context.fill();
1168
+ }
1169
+ },
1170
+
1171
+ drawRect : function(x, y, width, height, lineColor, fillColor) {
1172
+ return this.drawShape([ [x,y], [x+width, y], [x+width, y+height], [x, y+height], [x, y] ], lineColor, fillColor);
1173
+ }
1174
+
1175
+ });
1176
+
1177
+ VCanvas_vml = function(width, height, target) {
1178
+ return this.init(width, height, target);
1179
+ };
1180
+
1181
+ VCanvas_vml.prototype = $.extend(new VCanvas_base(), {
1182
+ _super : VCanvas_base.prototype,
1183
+
1184
+ init : function(width, height, target) {
1185
+ this._super.init(width, height, target);
1186
+ if (target[0]) {
1187
+ target=target[0];
1188
+ }
1189
+ target.VCanvas = this;
1190
+ this.canvas = document.createElement('span');
1191
+ $(this.canvas).css({ display:'inline-block', position: 'relative', overflow:'hidden', width:width, height:height, margin:'0px', padding:'0px', verticalAlign: 'top'});
1192
+ this._insert(this.canvas, target);
1193
+ this.pixel_height = $(this.canvas).height();
1194
+ this.pixel_width = $(this.canvas).width();
1195
+ this.canvas.width = this.pixel_width;
1196
+ this.canvas.height = this.pixel_height;
1197
+ var groupel = '<v:group coordorigin="0 0" coordsize="'+this.pixel_width+' '+this.pixel_height+'"' +
1198
+ ' style="position:absolute;top:0;left:0;width:'+this.pixel_width+'px;height='+this.pixel_height+'px;"></v:group>';
1199
+ this.canvas.insertAdjacentHTML('beforeEnd', groupel);
1200
+ this.group = $(this.canvas).children()[0];
1201
+ },
1202
+
1203
+ drawShape : function(path, lineColor, fillColor, lineWidth) {
1204
+ var vpath = [];
1205
+ for(var i=0, plen=path.length; i<plen; i++) {
1206
+ vpath[i] = ''+(path[i][0])+','+(path[i][1]);
1207
+ }
1208
+ var initial = vpath.splice(0,1);
1209
+ lineWidth = lineWidth === undefined ? 1 : lineWidth;
1210
+ var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="'+lineWidth+'" strokeColor="'+lineColor+'" ';
1211
+ var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" ';
1212
+ var closed = vpath[0] == vpath[vpath.length-1] ? 'x ' : '';
1213
+ var vel = '<v:shape coordorigin="0 0" coordsize="'+this.pixel_width+' '+this.pixel_height+'" ' +
1214
+ stroke +
1215
+ fill +
1216
+ ' style="position:absolute;left:0px;top:0px;height:'+this.pixel_height+'px;width:'+this.pixel_width+'px;padding:0px;margin:0px;" ' +
1217
+ ' path="m '+initial+' l '+vpath.join(', ')+' '+closed+'e">' +
1218
+ ' </v:shape>';
1219
+ this.group.insertAdjacentHTML('beforeEnd', vel);
1220
+ },
1221
+
1222
+ drawCircle : function(x, y, radius, lineColor, fillColor) {
1223
+ x -= radius+1;
1224
+ y -= radius+1;
1225
+ var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1" strokeColor="'+lineColor+'" ';
1226
+ var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" ';
1227
+ var vel = '<v:oval ' +
1228
+ stroke +
1229
+ fill +
1230
+ ' style="position:absolute;top:'+y+'px; left:'+x+'px; width:'+(radius*2)+'px; height:'+(radius*2)+'px"></v:oval>';
1231
+ this.group.insertAdjacentHTML('beforeEnd', vel);
1232
+
1233
+ },
1234
+
1235
+ drawPieSlice : function(x, y, radius, startAngle, endAngle, lineColor, fillColor) {
1236
+ if (startAngle == endAngle) {
1237
+ return; // VML seems to have problem when start angle equals end angle.
1238
+ }
1239
+ if ((endAngle - startAngle) == (2*Math.PI)) {
1240
+ startAngle = 0.0; // VML seems to have a problem when drawing a full circle that doesn't start 0
1241
+ endAngle = (2*Math.PI);
1242
+ }
1243
+
1244
+ var startx = x + Math.round(Math.cos(startAngle) * radius);
1245
+ var starty = y + Math.round(Math.sin(startAngle) * radius);
1246
+ var endx = x + Math.round(Math.cos(endAngle) * radius);
1247
+ var endy = y + Math.round(Math.sin(endAngle) * radius);
1248
+
1249
+ // Prevent very small slices from being mistaken as a whole pie
1250
+ if (startx==endx && starty==endy && (endAngle-startAngle) < Math.PI) {
1251
+ return;
1252
+ }
1253
+
1254
+ var vpath = [ x-radius, y-radius, x+radius, y+radius, startx, starty, endx, endy ];
1255
+ var stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1" strokeColor="'+lineColor+'" ';
1256
+ var fill = fillColor === undefined ? ' filled="false"' : ' fillColor="'+fillColor+'" filled="true" ';
1257
+ var vel = '<v:shape coordorigin="0 0" coordsize="'+this.pixel_width+' '+this.pixel_height+'" ' +
1258
+ stroke +
1259
+ fill +
1260
+ ' style="position:absolute;left:0px;top:0px;height:'+this.pixel_height+'px;width:'+this.pixel_width+'px;padding:0px;margin:0px;" ' +
1261
+ ' path="m '+x+','+y+' wa '+vpath.join(', ')+' x e">' +
1262
+ ' </v:shape>';
1263
+ this.group.insertAdjacentHTML('beforeEnd', vel);
1264
+ },
1265
+
1266
+ drawRect : function(x, y, width, height, lineColor, fillColor) {
1267
+ return this.drawShape( [ [x, y], [x, y+height], [x+width, y+height], [x+width, y], [x, y] ], lineColor, fillColor);
1268
+ }
1269
+ });
1270
+
1271
+ })(jQuery);