how_are_we_doing 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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);