how_are_we_doing 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/app/controllers/reports_controller.rb +1 -52
  2. data/app/helpers/reports_helper.rb +30 -4
  3. data/app/models/print.rb +1 -29
  4. data/app/models/share.rb +3 -29
  5. data/app/models/total.rb +2 -21
  6. data/app/models/view.rb +1 -29
  7. data/app/views/reports/_bar_graph.html.erb +48 -0
  8. data/app/views/reports/_line_graph.html.erb +40 -0
  9. data/app/views/reports/_recent_bar_graph.html.erb +41 -0
  10. data/app/views/reports/index.html.erb +12 -22
  11. data/config/locales/en.yml +16 -0
  12. data/lib/generators/how_are_we_doing/install/install_generator.rb +1 -0
  13. data/lib/generators/how_are_we_doing/install/templates/public/images/hawd/blank.png +0 -0
  14. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/API.txt +85 -68
  15. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/FAQ.txt +0 -0
  16. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/NEWS.txt +12 -32
  17. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/PLUGINS.txt +0 -0
  18. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/README.txt +3 -3
  19. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/excanvas.js +160 -663
  20. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/excanvas.min.js +1 -1
  21. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/jquery.flot.crosshair.js +12 -51
  22. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/jquery.flot.image.js +0 -0
  23. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/jquery.flot.js +509 -155
  24. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/jquery.flot.navigate.js +0 -1
  25. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/jquery.flot.stack.js +0 -0
  26. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/jquery.flot.threshold.js +0 -0
  27. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/flot/jquery.js +0 -0
  28. data/lib/generators/how_are_we_doing/install/templates/public/javascripts/how_are_we_doing.js +50 -8
  29. data/lib/how_are_we_doing/acts_as_analytical.rb +98 -0
  30. data/lib/{acts_as_printable.rb → how_are_we_doing/acts_as_printable.rb} +0 -0
  31. data/lib/{acts_as_shareable.rb → how_are_we_doing/acts_as_shareable.rb} +0 -0
  32. data/lib/{acts_as_totalable.rb → how_are_we_doing/acts_as_totalable.rb} +0 -0
  33. data/lib/{acts_as_viewable.rb → how_are_we_doing/acts_as_viewable.rb} +0 -0
  34. data/lib/how_are_we_doing/controllers/reports_helpers.rb +92 -0
  35. data/lib/how_are_we_doing/version.rb +1 -1
  36. data/lib/how_are_we_doing.rb +16 -8
  37. metadata +13 -6
@@ -1,33 +1,9 @@
1
- /* Javascript plotting library for jQuery, v. 0.6.
1
+ /* Javascript plotting library for jQuery, v. 0.5.
2
2
  *
3
3
  * Released under the MIT license by IOLA, December 2007.
4
4
  *
5
5
  */
6
6
 
7
- // first an inline dependency, jquery.colorhelpers.js, we inline it here
8
- // for convenience
9
-
10
- /* Plugin for jQuery for working with colors.
11
- *
12
- * Version 1.0.
13
- *
14
- * Inspiration from jQuery color animation plugin by John Resig.
15
- *
16
- * Released under the MIT license by Ole Laursen, October 2009.
17
- *
18
- * Examples:
19
- *
20
- * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
21
- * var c = $.color.extract($("#mydiv"), 'background-color');
22
- * console.log(c.r, c.g, c.b, c.a);
23
- * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
24
- *
25
- * Note that .scale() and .add() work in-place instead of returning
26
- * new objects.
27
- */
28
- (function(){jQuery.color={};jQuery.color.make=function(E,D,B,C){var F={};F.r=E||0;F.g=D||0;F.b=B||0;F.a=C!=null?C:1;F.add=function(I,H){for(var G=0;G<I.length;++G){F[I.charAt(G)]+=H}return F.normalize()};F.scale=function(I,H){for(var G=0;G<I.length;++G){F[I.charAt(G)]*=H}return F.normalize()};F.toString=function(){if(F.a>=1){return"rgb("+[F.r,F.g,F.b].join(",")+")"}else{return"rgba("+[F.r,F.g,F.b,F.a].join(",")+")"}};F.normalize=function(){function G(I,J,H){return J<I?I:(J>H?H:J)}F.r=G(0,parseInt(F.r),255);F.g=G(0,parseInt(F.g),255);F.b=G(0,parseInt(F.b),255);F.a=G(0,F.a,1);return F};F.clone=function(){return jQuery.color.make(F.r,F.b,F.g,F.a)};return F.normalize()};jQuery.color.extract=function(C,B){var D;do{D=C.css(B).toLowerCase();if(D!=""&&D!="transparent"){break}C=C.parent()}while(!jQuery.nodeName(C.get(0),"body"));if(D=="rgba(0, 0, 0, 0)"){D="transparent"}return jQuery.color.parse(D)};jQuery.color.parse=function(E){var D,B=jQuery.color.make;if(D=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(E)){return B(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10))}if(D=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){return B(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10),parseFloat(D[4]))}if(D=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(E)){return B(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55)}if(D=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){return B(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55,parseFloat(D[4]))}if(D=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(E)){return B(parseInt(D[1],16),parseInt(D[2],16),parseInt(D[3],16))}if(D=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(E)){return B(parseInt(D[1]+D[1],16),parseInt(D[2]+D[2],16),parseInt(D[3]+D[3],16))}var C=jQuery.trim(E).toLowerCase();if(C=="transparent"){return B(255,255,255,0)}else{D=A[C];return B(D[0],D[1],D[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})();
29
-
30
- // the actual Flot code
31
7
  (function($) {
32
8
  function Plot(placeholder, data_, options_, plugins) {
33
9
  // data is on the form:
@@ -52,8 +28,6 @@
52
28
  },
53
29
  xaxis: {
54
30
  mode: null, // null or "time"
55
- transform: null, // null or f: number -> number to transform axis
56
- inverseTransform: null, // if transform is set, this should be the inverse function
57
31
  min: null, // min. value to show, null means set automatically
58
32
  max: null, // max. value to show, null means set automatically
59
33
  autoscaleMargin: null, // margin in % to add if auto-setting min/max
@@ -67,8 +41,7 @@
67
41
  tickSize: null, // number or [number, "unit"]
68
42
  minTickSize: null, // number or [number, "unit"]
69
43
  monthNames: null, // list of names of months
70
- timeformat: null, // format string to use
71
- twelveHourClock: false // 12 or 24 time in time mode
44
+ timeformat: null // format string to use
72
45
  },
73
46
  yaxis: {
74
47
  autoscaleMargin: 0.02
@@ -102,7 +75,8 @@
102
75
  fill: true,
103
76
  fillColor: null,
104
77
  align: "left", // or "center"
105
- horizontal: false // when horizontal, left is now top
78
+ horizontal: false, // when horizontal, left is now top
79
+ series_spread: false // display series side by side
106
80
  },
107
81
  shadowSize: 3
108
82
  },
@@ -124,7 +98,10 @@
124
98
  autoHighlight: true, // highlight in case mouse is near
125
99
  mouseActiveRadius: 10 // how far the mouse can be away to activate an item
126
100
  },
127
- hooks: {}
101
+ selection: {
102
+ mode: null, // one of null, "x", "y" or "xy"
103
+ color: "#e8cfac"
104
+ }
128
105
  },
129
106
  canvas = null, // the canvas for the plot itself
130
107
  overlay = null, // canvas for interactive stuff on top of plot
@@ -142,17 +119,22 @@
142
119
  bindEvents: [],
143
120
  drawOverlay: []
144
121
  },
145
- plot = this;
122
+ plot = this,
123
+ // dedicated to storing data for buggy standard compliance cases
124
+ workarounds = {};
146
125
 
147
126
  // public functions
148
127
  plot.setData = setData;
149
128
  plot.setupGrid = setupGrid;
150
129
  plot.draw = draw;
130
+ plot.clearSelection = clearSelection;
131
+ plot.setSelection = setSelection;
132
+ plot.getSelection = getSelection;
151
133
  plot.getPlaceholder = function() { return placeholder; };
152
134
  plot.getCanvas = function() { return canvas; };
153
135
  plot.getPlotOffset = function() { return plotOffset; };
154
- plot.width = function () { return plotWidth; };
155
- plot.height = function () { return plotHeight; };
136
+ plot.width = function () { return plotWidth; }
137
+ plot.height = function () { return plotHeight; }
156
138
  plot.offset = function () {
157
139
  var o = eventHolder.offset();
158
140
  o.left += plotOffset.left;
@@ -168,7 +150,7 @@
168
150
  plot.pointOffset = function(point) {
169
151
  return { left: parseInt(axisSpecToRealAxis(point, "xaxis").p2c(+point.x) + plotOffset.left),
170
152
  top: parseInt(axisSpecToRealAxis(point, "yaxis").p2c(+point.y) + plotOffset.top) };
171
- };
153
+ }
172
154
 
173
155
 
174
156
  // public attributes
@@ -202,7 +184,7 @@
202
184
  function parseOptions(opts) {
203
185
  $.extend(true, options, opts);
204
186
  if (options.grid.borderColor == null)
205
- options.grid.borderColor = options.grid.color;
187
+ options.grid.borderColor = options.grid.color
206
188
  // backwards compatibility, to be removed in future
207
189
  if (options.xaxis.noTicks && options.xaxis.ticks == null)
208
190
  options.xaxis.ticks = options.xaxis.noTicks;
@@ -221,10 +203,6 @@
221
203
  if (options.shadowSize)
222
204
  options.series.shadowSize = options.shadowSize;
223
205
 
224
- for (var n in hooks)
225
- if (options.hooks[n] && options.hooks[n].length)
226
- hooks[n] = hooks[n].concat(options.hooks[n]);
227
-
228
206
  executeHooks(hooks.processOptions, [options]);
229
207
  }
230
208
 
@@ -278,7 +256,7 @@
278
256
  if (typeof sc == "number")
279
257
  assignedColors.push(sc);
280
258
  else
281
- usedColors.push($.color.parse(series[i].color));
259
+ usedColors.push(parseColor(series[i].color));
282
260
  }
283
261
  }
284
262
 
@@ -294,13 +272,14 @@
294
272
  while (colors.length < neededColors) {
295
273
  var c;
296
274
  if (options.colors.length == i) // check degenerate case
297
- c = $.color.make(100, 100, 100);
275
+ c = new Color(100, 100, 100);
298
276
  else
299
- c = $.color.parse(options.colors[i]);
277
+ c = parseColor(options.colors[i]);
300
278
 
301
279
  // vary color if needed
302
280
  var sign = variation % 2 == 1 ? -1 : 1;
303
- c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2)
281
+ var factor = 1 + sign * Math.ceil(variation / 2) * 0.2;
282
+ c.scale(factor, factor, factor);
304
283
 
305
284
  // FIXME: if we're getting to close to something else,
306
285
  // we should probably skip this one
@@ -329,7 +308,7 @@
329
308
  // turn on lines automatically in case nothing is set
330
309
  if (s.lines.show == null) {
331
310
  var v, show = true;
332
- for (v in s)
311
+ for (var v in s)
333
312
  if (s[v].show) {
334
313
  show = false;
335
314
  break;
@@ -353,6 +332,8 @@
353
332
  for (axis in axes) {
354
333
  axes[axis].datamin = topSentry;
355
334
  axes[axis].datamax = bottomSentry;
335
+ axes[axis].min = options[axis].min;
336
+ axes[axis].max = options[axis].max;
356
337
  axes[axis].used = false;
357
338
  }
358
339
 
@@ -377,10 +358,10 @@
377
358
  var data = s.data, format = s.datapoints.format;
378
359
 
379
360
  if (!format) {
380
- format = [];
361
+ format = []
381
362
  // find out how to copy
382
- format.push({ x: true, number: true, required: true });
383
- format.push({ y: true, number: true, required: true });
363
+ format.push({ x: true, number: true, required: true })
364
+ format.push({ y: true, number: true, required: true })
384
365
 
385
366
  if (s.bars.show)
386
367
  format.push({ y: true, number: true, required: false, defaultValue: 0 });
@@ -417,8 +398,15 @@
417
398
  }
418
399
 
419
400
  if (val == null) {
420
- if (f.required)
401
+ if (f.required) {
402
+ // extract min/max info before we whack it
403
+ if (f.x)
404
+ updateAxis(s.xaxis, val, val)
405
+ if (f.y)
406
+ updateAxis(s.yaxis, val, val)
407
+ val = null;
421
408
  nullify = true;
409
+ }
422
410
 
423
411
  if (f.defaultValue != null)
424
412
  val = f.defaultValue;
@@ -430,18 +418,8 @@
430
418
  }
431
419
 
432
420
  if (nullify) {
433
- for (m = 0; m < ps; ++m) {
434
- val = points[k + m];
435
- if (val != null) {
436
- f = format[m];
437
- // extract min/max info
438
- if (f.x)
439
- updateAxis(s.xaxis, val, val);
440
- if (f.y)
441
- updateAxis(s.yaxis, val, val);
442
- }
421
+ for (m = 0; m < ps; ++m)
443
422
  points[k + m] = null;
444
- }
445
423
  }
446
424
  else {
447
425
  // a little bit of line specific stuff that
@@ -465,7 +443,6 @@
465
443
  }
466
444
  }
467
445
 
468
- // give the hooks a chance to run
469
446
  for (i = 0; i < series.length; ++i) {
470
447
  s = series[i];
471
448
 
@@ -489,7 +466,7 @@
489
466
  val = points[j + m];
490
467
  f = format[m];
491
468
  if (!f)
492
- continue;
469
+ continue
493
470
 
494
471
  if (f.x) {
495
472
  if (val < xmin)
@@ -569,9 +546,13 @@
569
546
  eventHolder = $([overlay, canvas]);
570
547
 
571
548
  // bind events
572
- if (options.grid.hoverable)
549
+ if (options.selection.mode != null
550
+ || options.grid.hoverable)
573
551
  eventHolder.mousemove(onMouseMove);
574
552
 
553
+ if (options.selection.mode != null)
554
+ eventHolder.mousedown(onMouseDown);
555
+
575
556
  if (options.grid.clickable)
576
557
  eventHolder.click(onClick);
577
558
 
@@ -579,42 +560,27 @@
579
560
  }
580
561
 
581
562
  function setupGrid() {
582
- function setTransformationHelpers(axis, o) {
583
- function identity(x) { return x; }
584
-
585
- var s, m, t = o.transform || identity,
586
- it = o.inverseTransform;
563
+ function setTransformationHelpers(axis) {
564
+ var s, m;
587
565
 
588
566
  // add transformation helpers
589
567
  if (axis == axes.xaxis || axis == axes.x2axis) {
590
568
  // precompute how much the axis is scaling a point
591
569
  // in canvas space
592
- s = axis.scale = plotWidth / (t(axis.max) - t(axis.min));
593
- m = t(axis.min);
594
-
570
+ s = axis.scale = plotWidth / (axis.max - axis.min);
571
+ m = axis.min;
572
+
595
573
  // data point to canvas coordinate
596
- if (t == identity) // slight optimization
597
- axis.p2c = function (p) { return (p - m) * s; };
598
- else
599
- axis.p2c = function (p) { return (t(p) - m) * s; };
600
- // canvas coordinate to data point
601
- if (!it)
602
- axis.c2p = function (c) { return m + c / s; };
603
- else
604
- axis.c2p = function (c) { return it(m + c / s); };
574
+ axis.p2c = function (p) { return (p - m) * s; };
575
+ // canvas coordinate to data point
576
+ axis.c2p = function (c) { return m + c / s; };
605
577
  }
606
578
  else {
607
- s = axis.scale = plotHeight / (t(axis.max) - t(axis.min));
608
- m = t(axis.max);
579
+ s = axis.scale = plotHeight / (axis.max - axis.min)
580
+ m = axis.max;
609
581
 
610
- if (t == identity)
611
- axis.p2c = function (p) { return (m - p) * s; };
612
- else
613
- axis.p2c = function (p) { return (m - t(p)) * s; };
614
- if (!it)
615
- axis.c2p = function (c) { return m - c / s; };
616
- else
617
- axis.c2p = function (c) { return it(m - c / s); };
582
+ axis.p2c = function (p) { return (m - p) * s; };
583
+ axis.c2p = function (p) { return m - p / s; };
618
584
  }
619
585
  }
620
586
 
@@ -630,7 +596,7 @@
630
596
  // them, we don't need the exact figures and the
631
597
  // fixed-size box content is easy to center
632
598
  if (axis.labelWidth == null)
633
- axis.labelWidth = canvasWidth / (axis.ticks.length > 0 ? axis.ticks.length : 1);
599
+ axis.labelWidth = canvasWidth / 6;
634
600
 
635
601
  // measure x label heights
636
602
  if (axis.labelHeight == null) {
@@ -719,7 +685,7 @@
719
685
  }
720
686
 
721
687
  for (axis in axes)
722
- setTransformationHelpers(axes[axis], options[axis]);
688
+ setTransformationHelpers(axes[axis]);
723
689
 
724
690
  if (options.grid.show)
725
691
  insertLabels();
@@ -918,15 +884,14 @@
918
884
 
919
885
  var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
920
886
  var span = axis.max - axis.min;
921
- var suffix = (axisOptions.twelveHourClock) ? " %p" : "";
922
887
 
923
888
  if (t < timeUnitSize.minute)
924
- fmt = "%h:%M:%S" + suffix;
889
+ fmt = "%h:%M:%S";
925
890
  else if (t < timeUnitSize.day) {
926
891
  if (span < 2 * timeUnitSize.day)
927
- fmt = "%h:%M" + suffix;
892
+ fmt = "%h:%M";
928
893
  else
929
- fmt = "%b %d %h:%M" + suffix;
894
+ fmt = "%b %d %h:%M";
930
895
  }
931
896
  else if (t < timeUnitSize.month)
932
897
  fmt = "%b %d";
@@ -1152,8 +1117,8 @@
1152
1117
 
1153
1118
  if (xrange.from == xrange.to || yrange.from == yrange.to) {
1154
1119
  // draw line
1155
- ctx.beginPath();
1156
1120
  ctx.strokeStyle = m.color || options.grid.markingsColor;
1121
+ ctx.beginPath();
1157
1122
  ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
1158
1123
  //ctx.moveTo(Math.floor(xrange.from), yrange.from);
1159
1124
  //ctx.lineTo(Math.floor(xrange.to), yrange.to);
@@ -1569,7 +1534,7 @@
1569
1534
  ctx.restore();
1570
1535
  }
1571
1536
 
1572
- function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal) {
1537
+ function drawBar(x, y, b, barLeft, barRight, yoffset, x_line_adjustment, fillStyleCallback, axisx, axisy, c, horizontal) {
1573
1538
  var left, right, bottom, top,
1574
1539
  drawLeft, drawRight, drawTop, drawBottom,
1575
1540
  tmp;
@@ -1649,53 +1614,76 @@
1649
1614
  c.fillStyle = fillStyleCallback(bottom, top);
1650
1615
  c.fill();
1651
1616
  }
1617
+
1618
+ if (yoffset) {
1619
+ bottom += yoffset;
1620
+ top += yoffset;
1621
+ }
1622
+
1623
+ if (x_line_adjustment) {
1624
+ left += x_line_adjustment;
1625
+ right -= x_line_adjustment;
1626
+ }
1652
1627
 
1653
1628
  // draw outline
1654
1629
  if (drawLeft || drawRight || drawTop || drawBottom) {
1655
1630
  c.beginPath();
1656
1631
 
1657
1632
  // FIXME: inline moveTo is buggy with excanvas
1658
- c.moveTo(left, bottom + offset);
1633
+ c.moveTo(left, bottom);
1659
1634
  if (drawLeft)
1660
- c.lineTo(left, top + offset);
1635
+ c.lineTo(left, top);
1661
1636
  else
1662
- c.moveTo(left, top + offset);
1637
+ c.moveTo(left, top);
1663
1638
  if (drawTop)
1664
- c.lineTo(right, top + offset);
1639
+ c.lineTo(right, top);
1665
1640
  else
1666
- c.moveTo(right, top + offset);
1641
+ c.moveTo(right, top);
1667
1642
  if (drawRight)
1668
- c.lineTo(right, bottom + offset);
1643
+ c.lineTo(right, bottom);
1669
1644
  else
1670
- c.moveTo(right, bottom + offset);
1645
+ c.moveTo(right, bottom);
1671
1646
  if (drawBottom)
1672
- c.lineTo(left, bottom + offset);
1647
+ c.lineTo(left, bottom);
1673
1648
  else
1674
- c.moveTo(left, bottom + offset);
1649
+ c.moveTo(left, bottom);
1675
1650
  c.stroke();
1676
1651
  }
1677
1652
  }
1678
1653
 
1679
1654
  function drawSeriesBars(series) {
1680
- function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {
1655
+ function plotBars(datapoints, barLeft, barRight, yoffset, x_line_adjustment, fillStyleCallback, axisx, axisy) {
1681
1656
  var points = datapoints.points, ps = datapoints.pointsize;
1682
1657
 
1683
1658
  for (var i = 0; i < points.length; i += ps) {
1684
1659
  if (points[i] == null)
1685
1660
  continue;
1686
- drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal);
1661
+ drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, yoffset, x_line_adjustment, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal);
1687
1662
  }
1688
1663
  }
1689
1664
 
1690
1665
  ctx.save();
1691
1666
  ctx.translate(plotOffset.left, plotOffset.top);
1692
-
1667
+
1693
1668
  // FIXME: figure out a way to add shadows (for instance along the right edge)
1694
1669
  ctx.lineWidth = series.bars.lineWidth;
1695
1670
  ctx.strokeStyle = series.color;
1696
1671
  var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
1672
+
1673
+ if (series.bars.series_spread) {
1674
+ var series_index = indexOfSeries(series);
1675
+ var series_length = plot.getData().length;
1676
+ var actualBarWidth = ((1.0/series_length) * (series.bars.barWidth));
1677
+ barLeft = barLeft + (series_index * actualBarWidth);
1678
+ }
1679
+
1697
1680
  var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
1698
- plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis);
1681
+
1682
+ if (series.bars.series_spread) {
1683
+ plotBars(series.datapoints, barLeft, barLeft + actualBarWidth, 0, series.bars.lineWidth, fillStyleCallback, series.xaxis, series.yaxis);
1684
+ } else {
1685
+ plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, 0, fillStyleCallback, series.xaxis, series.yaxis);
1686
+ }
1699
1687
  ctx.restore();
1700
1688
  }
1701
1689
 
@@ -1707,7 +1695,7 @@
1707
1695
  if (filloptions.fillColor)
1708
1696
  return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
1709
1697
 
1710
- var c = $.color.parse(seriesColor);
1698
+ var c = parseColor(seriesColor);
1711
1699
  c.a = typeof fill == "number" ? fill : 0.4;
1712
1700
  c.normalize();
1713
1701
  return c.toString();
@@ -1771,13 +1759,12 @@
1771
1759
  // label boxes
1772
1760
  var c = options.legend.backgroundColor;
1773
1761
  if (c == null) {
1774
- c = options.grid.backgroundColor;
1775
- if (c && typeof c == "string")
1776
- c = $.color.parse(c);
1762
+ var tmp;
1763
+ if (options.grid.backgroundColor && typeof options.grid.backgroundColor == "string")
1764
+ tmp = options.grid.backgroundColor;
1777
1765
  else
1778
- c = $.color.extract(legend, 'background-color');
1779
- c.a = 1;
1780
- c = c.toString();
1766
+ tmp = extractColor(legend);
1767
+ c = parseColor(tmp).adjust(null, null, null, 1).toString();
1781
1768
  }
1782
1769
  var div = legend.children();
1783
1770
  $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
@@ -1788,16 +1775,24 @@
1788
1775
 
1789
1776
  // interactive features
1790
1777
 
1791
- var highlights = [],
1792
- redrawTimeout = null;
1778
+ var lastMousePos = { pageX: null, pageY: null },
1779
+ selection = {
1780
+ first: { x: -1, y: -1}, second: { x: -1, y: -1},
1781
+ show: false,
1782
+ active: false
1783
+ },
1784
+ highlights = [],
1785
+ clickIsMouseUp = false,
1786
+ redrawTimeout = null,
1787
+ hoverTimeout = null;
1793
1788
 
1794
1789
  // returns the data item the mouse is over, or null if none is found
1795
1790
  function findNearbyItem(mouseX, mouseY, seriesFilter) {
1796
1791
  var maxDistance = options.grid.mouseActiveRadius,
1797
- smallestDistance = maxDistance * maxDistance + 1,
1792
+ lowestDistance = maxDistance * maxDistance + 1,
1798
1793
  item = null, foundPoint = false, i, j;
1799
1794
 
1800
- for (i = 0; i < series.length; ++i) {
1795
+ for (var i = 0; i < series.length; ++i) {
1801
1796
  if (!seriesFilter(series[i]))
1802
1797
  continue;
1803
1798
 
@@ -1827,12 +1822,9 @@
1827
1822
  // data units, because the scales of the axes may be different
1828
1823
  var dx = Math.abs(axisx.p2c(x) - mouseX),
1829
1824
  dy = Math.abs(axisy.p2c(y) - mouseY),
1830
- dist = dx * dx + dy * dy; // we save the sqrt
1831
-
1832
- // use <= to ensure last point takes precedence
1833
- // (last generally means on top of)
1834
- if (dist <= smallestDistance) {
1835
- smallestDistance = dist;
1825
+ dist = dx * dx + dy * dy; // no idea in taking sqrt
1826
+ if (dist < lowestDistance) {
1827
+ lowestDistance = dist;
1836
1828
  item = [i, j / ps];
1837
1829
  }
1838
1830
  }
@@ -1842,6 +1834,12 @@
1842
1834
  var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2,
1843
1835
  barRight = barLeft + s.bars.barWidth;
1844
1836
 
1837
+ if (s.bars.series_spread) {
1838
+ var actualBarWidth = ((1.0/series.length) * (s.bars.barWidth));
1839
+ barLeft = barLeft + (i * actualBarWidth);
1840
+ barRight = barLeft + actualBarWidth;
1841
+ }
1842
+
1845
1843
  for (j = 0; j < points.length; j += ps) {
1846
1844
  var x = points[j], y = points[j + 1], b = points[j + 2];
1847
1845
  if (x == null)
@@ -1861,7 +1859,6 @@
1861
1859
  if (item) {
1862
1860
  i = item[0];
1863
1861
  j = item[1];
1864
- ps = series[i].datapoints.pointsize;
1865
1862
 
1866
1863
  return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
1867
1864
  dataIndex: j,
@@ -1873,12 +1870,50 @@
1873
1870
  }
1874
1871
 
1875
1872
  function onMouseMove(e) {
1873
+ lastMousePos.pageX = e.pageX;
1874
+ lastMousePos.pageY = e.pageY;
1875
+
1876
1876
  if (options.grid.hoverable)
1877
- triggerClickHoverEvent("plothover", e,
1877
+ triggerClickHoverEvent("plothover", lastMousePos,
1878
1878
  function (s) { return s["hoverable"] != false; });
1879
+
1880
+ if (selection.active) {
1881
+ placeholder.trigger("plotselecting", [ getSelection() ]);
1882
+
1883
+ updateSelection(lastMousePos);
1884
+ }
1879
1885
  }
1880
1886
 
1887
+ function onMouseDown(e) {
1888
+ if (e.which != 1) // only accept left-click
1889
+ return;
1890
+
1891
+ // cancel out any text selections
1892
+ document.body.focus();
1893
+
1894
+ // prevent text selection and drag in old-school browsers
1895
+ if (document.onselectstart !== undefined && workarounds.onselectstart == null) {
1896
+ workarounds.onselectstart = document.onselectstart;
1897
+ document.onselectstart = function () { return false; };
1898
+ }
1899
+ if (document.ondrag !== undefined && workarounds.ondrag == null) {
1900
+ workarounds.ondrag = document.ondrag;
1901
+ document.ondrag = function () { return false; };
1902
+ }
1903
+
1904
+ setSelectionPos(selection.first, e);
1905
+
1906
+ lastMousePos.pageX = null;
1907
+ selection.active = true;
1908
+ $(document).one("mouseup", onSelectionMouseUp);
1909
+ }
1910
+
1881
1911
  function onClick(e) {
1912
+ if (clickIsMouseUp) {
1913
+ clickIsMouseUp = false;
1914
+ return;
1915
+ }
1916
+
1882
1917
  triggerClickHoverEvent("plotclick", e,
1883
1918
  function (s) { return s["clickable"] != false; });
1884
1919
  }
@@ -1946,6 +1981,22 @@
1946
1981
  else
1947
1982
  drawPointHighlight(hi.series, hi.point);
1948
1983
  }
1984
+
1985
+ // draw selection
1986
+ if (selection.show && selectionIsSane()) {
1987
+ octx.strokeStyle = parseColor(options.selection.color).scale(null, null, null, 0.8).toString();
1988
+ octx.lineWidth = 1;
1989
+ ctx.lineJoin = "round";
1990
+ octx.fillStyle = parseColor(options.selection.color).scale(null, null, null, 0.4).toString();
1991
+
1992
+ var x = Math.min(selection.first.x, selection.second.x),
1993
+ y = Math.min(selection.first.y, selection.second.y),
1994
+ w = Math.abs(selection.second.x - selection.first.x),
1995
+ h = Math.abs(selection.second.y - selection.first.y);
1996
+
1997
+ octx.fillRect(x, y, w, h);
1998
+ octx.strokeRect(x, y, w, h);
1999
+ }
1949
2000
  octx.restore();
1950
2001
 
1951
2002
  executeHooks(hooks.drawOverlay, [octx]);
@@ -1998,6 +2049,15 @@
1998
2049
  return -1;
1999
2050
  }
2000
2051
 
2052
+ function indexOfSeries(s) {
2053
+ for (var i = 0; i < series.length; ++i) {
2054
+ var os = series[i];
2055
+ if (os == s)
2056
+ return i;
2057
+ }
2058
+ return -1;
2059
+ }
2060
+
2001
2061
  function drawPointHighlight(series, point) {
2002
2062
  var x = point[0], y = point[1],
2003
2063
  axisx = series.xaxis, axisy = series.yaxis;
@@ -2007,7 +2067,7 @@
2007
2067
 
2008
2068
  var pointRadius = series.points.radius + series.points.lineWidth / 2;
2009
2069
  octx.lineWidth = pointRadius;
2010
- octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
2070
+ octx.strokeStyle = parseColor(series.color).scale(1, 1, 1, 0.5).toString();
2011
2071
  var radius = 1.5 * pointRadius;
2012
2072
  octx.beginPath();
2013
2073
  octx.arc(axisx.p2c(x), axisy.p2c(y), radius, 0, 2 * Math.PI, false);
@@ -2016,13 +2076,161 @@
2016
2076
 
2017
2077
  function drawBarHighlight(series, point) {
2018
2078
  octx.lineWidth = series.bars.lineWidth;
2019
- octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
2020
- var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString();
2079
+ octx.strokeStyle = parseColor(series.color).scale(1, 1, 1, 0.5).toString();
2080
+ var fillStyle = parseColor(series.color).scale(1, 1, 1, 0.5).toString();
2021
2081
  var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
2022
- drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
2023
- 0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal);
2082
+
2083
+
2084
+ if (series.bars.series_spread) {
2085
+ s = plot.getData();
2086
+ series_index = indexOfSeries(series);
2087
+ var actualBarWidth = ((1.0/s.length) * (series.bars.barWidth));
2088
+ barLeft = barLeft + (series_index * actualBarWidth);
2089
+ }
2090
+
2091
+ if (series.bars.series_spread) {
2092
+ drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + actualBarWidth,
2093
+ 0, series.bars.lineWidth, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal);
2094
+ } else {
2095
+ drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
2096
+ 0, 0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal);
2097
+ }
2024
2098
  }
2025
2099
 
2100
+ function getSelection() {
2101
+ if (!selectionIsSane())
2102
+ return null;
2103
+
2104
+ var x1 = Math.min(selection.first.x, selection.second.x),
2105
+ x2 = Math.max(selection.first.x, selection.second.x),
2106
+ y1 = Math.max(selection.first.y, selection.second.y),
2107
+ y2 = Math.min(selection.first.y, selection.second.y);
2108
+
2109
+ var r = {};
2110
+ if (axes.xaxis.used)
2111
+ r.xaxis = { from: axes.xaxis.c2p(x1), to: axes.xaxis.c2p(x2) };
2112
+ if (axes.x2axis.used)
2113
+ r.x2axis = { from: axes.x2axis.c2p(x1), to: axes.x2axis.c2p(x2) };
2114
+ if (axes.yaxis.used)
2115
+ r.yaxis = { from: axes.yaxis.c2p(y1), to: axes.yaxis.c2p(y2) };
2116
+ if (axes.y2axis.used)
2117
+ r.y2axis = { from: axes.y2axis.c2p(y1), to: axes.y2axis.c2p(y2) };
2118
+ return r;
2119
+ }
2120
+
2121
+ function triggerSelectedEvent() {
2122
+ var r = getSelection();
2123
+
2124
+ placeholder.trigger("plotselected", [ r ]);
2125
+
2126
+ // backwards-compat stuff, to be removed in future
2127
+ if (axes.xaxis.used && axes.yaxis.used)
2128
+ placeholder.trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);
2129
+ }
2130
+
2131
+ function onSelectionMouseUp(e) {
2132
+ // revert drag stuff for old-school browsers
2133
+ if (document.onselectstart !== undefined)
2134
+ document.onselectstart = workarounds.onselectstart;
2135
+ if (document.ondrag !== undefined)
2136
+ document.ondrag = workarounds.ondrag;
2137
+
2138
+ // no more draggy-dee-drag
2139
+ selection.active = false;
2140
+ updateSelection(e);
2141
+
2142
+ if (selectionIsSane()) {
2143
+ triggerSelectedEvent();
2144
+ clickIsMouseUp = true;
2145
+ }
2146
+ else {
2147
+ // this counts as a clear
2148
+ placeholder.trigger("plotunselected", [ ]);
2149
+ placeholder.trigger("plotselecting", [ null ]);
2150
+ }
2151
+
2152
+ return false;
2153
+ }
2154
+
2155
+ function setSelectionPos(pos, e) {
2156
+ var offset = eventHolder.offset();
2157
+ pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plotWidth);
2158
+ pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plotHeight);
2159
+
2160
+ if (options.selection.mode == "y") {
2161
+ if (pos == selection.first)
2162
+ pos.x = 0;
2163
+ else
2164
+ pos.x = plotWidth;
2165
+ }
2166
+
2167
+ if (options.selection.mode == "x") {
2168
+ if (pos == selection.first)
2169
+ pos.y = 0;
2170
+ else
2171
+ pos.y = plotHeight;
2172
+ }
2173
+ }
2174
+
2175
+ function updateSelection(pos) {
2176
+ if (pos.pageX == null)
2177
+ return;
2178
+
2179
+ setSelectionPos(selection.second, pos);
2180
+ if (selectionIsSane()) {
2181
+ selection.show = true;
2182
+ triggerRedrawOverlay();
2183
+ }
2184
+ else
2185
+ clearSelection(true);
2186
+ }
2187
+
2188
+ function clearSelection(preventEvent) {
2189
+ if (selection.show) {
2190
+ selection.show = false;
2191
+ triggerRedrawOverlay();
2192
+ if (!preventEvent)
2193
+ placeholder.trigger("plotunselected", [ ]);
2194
+ }
2195
+ }
2196
+
2197
+ function setSelection(ranges, preventEvent) {
2198
+ var range;
2199
+
2200
+ if (options.selection.mode == "y") {
2201
+ selection.first.x = 0;
2202
+ selection.second.x = plotWidth;
2203
+ }
2204
+ else {
2205
+ range = extractRange(ranges, "x");
2206
+
2207
+ selection.first.x = range.axis.p2c(range.from);
2208
+ selection.second.x = range.axis.p2c(range.to);
2209
+ }
2210
+
2211
+ if (options.selection.mode == "x") {
2212
+ selection.first.y = 0;
2213
+ selection.second.y = plotHeight;
2214
+ }
2215
+ else {
2216
+ range = extractRange(ranges, "y");
2217
+
2218
+ selection.first.y = range.axis.p2c(range.from);
2219
+ selection.second.y = range.axis.p2c(range.to);
2220
+ }
2221
+
2222
+ selection.show = true;
2223
+ triggerRedrawOverlay();
2224
+ if (!preventEvent)
2225
+ triggerSelectedEvent();
2226
+ }
2227
+
2228
+ function selectionIsSane() {
2229
+ var minSize = 5;
2230
+ return Math.abs(selection.second.x - selection.first.x) >= minSize &&
2231
+ Math.abs(selection.second.y - selection.first.y) >= minSize;
2232
+ }
2233
+
2026
2234
  function getColorOrGradient(spec, bottom, top, defaultColor) {
2027
2235
  if (typeof spec == "string")
2028
2236
  return spec;
@@ -2034,12 +2242,7 @@
2034
2242
 
2035
2243
  for (var i = 0, l = spec.colors.length; i < l; ++i) {
2036
2244
  var c = spec.colors[i];
2037
- if (typeof c != "string") {
2038
- c = $.color.parse(defaultColor).scale('rgb', c.brightness);
2039
- c.a *= c.opacity;
2040
- c = c.toString();
2041
- }
2042
- gradient.addColorStop(i / (l - 1), c);
2245
+ gradient.addColorStop(i / (l - 1), typeof c == "string" ? c : parseColor(defaultColor).scale(c.brightness, c.brightness, c.brightness, c.opacity));
2043
2246
  }
2044
2247
 
2045
2248
  return gradient;
@@ -2070,33 +2273,21 @@
2070
2273
 
2071
2274
  var r = [];
2072
2275
  var escape = false;
2073
- var hours = d.getUTCHours();
2074
- var isAM = hours < 12;
2075
2276
  if (monthNames == null)
2076
2277
  monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
2077
-
2078
- if (fmt.search(/%p|%P/) != -1) {
2079
- if (hours > 12) {
2080
- hours = hours - 12;
2081
- } else if (hours == 0) {
2082
- hours = 12;
2083
- }
2084
- }
2085
2278
  for (var i = 0; i < fmt.length; ++i) {
2086
2279
  var c = fmt.charAt(i);
2087
2280
 
2088
2281
  if (escape) {
2089
2282
  switch (c) {
2090
- case 'h': c = "" + hours; break;
2091
- case 'H': c = leftPad(hours); break;
2283
+ case 'h': c = "" + d.getUTCHours(); break;
2284
+ case 'H': c = leftPad(d.getUTCHours()); break;
2092
2285
  case 'M': c = leftPad(d.getUTCMinutes()); break;
2093
2286
  case 'S': c = leftPad(d.getUTCSeconds()); break;
2094
2287
  case 'd': c = "" + d.getUTCDate(); break;
2095
2288
  case 'm': c = "" + (d.getUTCMonth() + 1); break;
2096
2289
  case 'y': c = "" + d.getUTCFullYear(); break;
2097
2290
  case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
2098
- case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
2099
- case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
2100
2291
  }
2101
2292
  r.push(c);
2102
2293
  escape = false;
@@ -2116,4 +2307,167 @@
2116
2307
  return base * Math.floor(n / base);
2117
2308
  }
2118
2309
 
2310
+ function clamp(min, value, max) {
2311
+ if (value < min)
2312
+ return min;
2313
+ else if (value > max)
2314
+ return max;
2315
+ else
2316
+ return value;
2317
+ }
2318
+
2319
+ // color helpers, inspiration from the jquery color animation
2320
+ // plugin by John Resig
2321
+ function Color (r, g, b, a) {
2322
+
2323
+ var rgba = ['r','g','b','a'];
2324
+ var x = 4; //rgba.length
2325
+
2326
+ while (-1<--x) {
2327
+ this[rgba[x]] = arguments[x] || ((x==3) ? 1.0 : 0);
2328
+ }
2329
+
2330
+ this.toString = function() {
2331
+ if (this.a >= 1.0) {
2332
+ return "rgb("+[this.r,this.g,this.b].join(",")+")";
2333
+ } else {
2334
+ return "rgba("+[this.r,this.g,this.b,this.a].join(",")+")";
2335
+ }
2336
+ };
2337
+
2338
+ this.scale = function(rf, gf, bf, af) {
2339
+ x = 4; //rgba.length
2340
+ while (-1<--x) {
2341
+ if (arguments[x] != null)
2342
+ this[rgba[x]] *= arguments[x];
2343
+ }
2344
+ return this.normalize();
2345
+ };
2346
+
2347
+ this.adjust = function(rd, gd, bd, ad) {
2348
+ x = 4; //rgba.length
2349
+ while (-1<--x) {
2350
+ if (arguments[x] != null)
2351
+ this[rgba[x]] += arguments[x];
2352
+ }
2353
+ return this.normalize();
2354
+ };
2355
+
2356
+ this.clone = function() {
2357
+ return new Color(this.r, this.b, this.g, this.a);
2358
+ };
2359
+
2360
+ this.normalize = function() {
2361
+ this.r = clamp(0, parseInt(this.r), 255);
2362
+ this.g = clamp(0, parseInt(this.g), 255);
2363
+ this.b = clamp(0, parseInt(this.b), 255);
2364
+ this.a = clamp(0, this.a, 1);
2365
+ return this;
2366
+ };
2367
+
2368
+ this.normalize();
2369
+ }
2370
+
2371
+ var lookupColors = {
2372
+ aqua:[0,255,255],
2373
+ azure:[240,255,255],
2374
+ beige:[245,245,220],
2375
+ black:[0,0,0],
2376
+ blue:[0,0,255],
2377
+ brown:[165,42,42],
2378
+ cyan:[0,255,255],
2379
+ darkblue:[0,0,139],
2380
+ darkcyan:[0,139,139],
2381
+ darkgrey:[169,169,169],
2382
+ darkgreen:[0,100,0],
2383
+ darkkhaki:[189,183,107],
2384
+ darkmagenta:[139,0,139],
2385
+ darkolivegreen:[85,107,47],
2386
+ darkorange:[255,140,0],
2387
+ darkorchid:[153,50,204],
2388
+ darkred:[139,0,0],
2389
+ darksalmon:[233,150,122],
2390
+ darkviolet:[148,0,211],
2391
+ fuchsia:[255,0,255],
2392
+ gold:[255,215,0],
2393
+ green:[0,128,0],
2394
+ indigo:[75,0,130],
2395
+ khaki:[240,230,140],
2396
+ lightblue:[173,216,230],
2397
+ lightcyan:[224,255,255],
2398
+ lightgreen:[144,238,144],
2399
+ lightgrey:[211,211,211],
2400
+ lightpink:[255,182,193],
2401
+ lightyellow:[255,255,224],
2402
+ lime:[0,255,0],
2403
+ magenta:[255,0,255],
2404
+ maroon:[128,0,0],
2405
+ navy:[0,0,128],
2406
+ olive:[128,128,0],
2407
+ orange:[255,165,0],
2408
+ pink:[255,192,203],
2409
+ purple:[128,0,128],
2410
+ violet:[128,0,128],
2411
+ red:[255,0,0],
2412
+ silver:[192,192,192],
2413
+ white:[255,255,255],
2414
+ yellow:[255,255,0]
2415
+ };
2416
+
2417
+ function extractColor(element) {
2418
+ var color, elem = element;
2419
+ do {
2420
+ color = elem.css("background-color").toLowerCase();
2421
+ // keep going until we find an element that has color, or
2422
+ // we hit the body
2423
+ if (color != '' && color != 'transparent')
2424
+ break;
2425
+ elem = elem.parent();
2426
+ } while (!$.nodeName(elem.get(0), "body"));
2427
+
2428
+ // catch Safari's way of signalling transparent
2429
+ if (color == "rgba(0, 0, 0, 0)")
2430
+ return "transparent";
2431
+
2432
+ return color;
2433
+ }
2434
+
2435
+ // parse string, returns Color
2436
+ function parseColor(str) {
2437
+ var result;
2438
+
2439
+ // Look for rgb(num,num,num)
2440
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))
2441
+ return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10));
2442
+
2443
+ // Look for rgba(num,num,num,num)
2444
+ if (result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
2445
+ return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10), parseFloat(result[4]));
2446
+
2447
+ // Look for rgb(num%,num%,num%)
2448
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))
2449
+ return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55);
2450
+
2451
+ // Look for rgba(num%,num%,num%,num)
2452
+ if (result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))
2453
+ return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55, parseFloat(result[4]));
2454
+
2455
+ // Look for #a0b1c2
2456
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))
2457
+ return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16));
2458
+
2459
+ // Look for #fff
2460
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))
2461
+ return new Color(parseInt(result[1]+result[1], 16), parseInt(result[2]+result[2], 16), parseInt(result[3]+result[3], 16));
2462
+
2463
+ // Otherwise, we're most likely dealing with a named color
2464
+ var name = $.trim(str).toLowerCase();
2465
+ if (name == "transparent")
2466
+ return new Color(255, 255, 255, 0);
2467
+ else {
2468
+ result = lookupColors[name];
2469
+ return new Color(result[0], result[1], result[2]);
2470
+ }
2471
+ }
2472
+
2119
2473
  })(jQuery);