riemann-dash 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/lib/riemann/dash/public/clock.js +12 -8
  2. data/lib/riemann/dash/public/strings.js +8 -2
  3. data/lib/riemann/dash/public/subs.js +8 -4
  4. data/lib/riemann/dash/public/util.js +19 -5
  5. data/lib/riemann/dash/public/vendor/flot/jquery.colorhelpers.js +179 -0
  6. data/lib/riemann/dash/public/vendor/flot/jquery.colorhelpers.min.js +21 -0
  7. data/lib/riemann/dash/public/vendor/flot/jquery.flot.canvas.js +345 -0
  8. data/lib/riemann/dash/public/vendor/flot/jquery.flot.canvas.min.js +28 -0
  9. data/lib/riemann/dash/public/vendor/flot/jquery.flot.categories.js +190 -0
  10. data/lib/riemann/dash/public/vendor/flot/jquery.flot.categories.min.js +44 -0
  11. data/lib/riemann/dash/public/vendor/flot/jquery.flot.crosshair.js +176 -0
  12. data/lib/riemann/dash/public/vendor/flot/jquery.flot.crosshair.min.js +59 -0
  13. data/lib/riemann/dash/public/vendor/flot/jquery.flot.errorbars.js +353 -0
  14. data/lib/riemann/dash/public/vendor/flot/jquery.flot.errorbars.min.js +63 -0
  15. data/lib/riemann/dash/public/vendor/flot/jquery.flot.fillbetween.js +226 -0
  16. data/lib/riemann/dash/public/vendor/flot/jquery.flot.fillbetween.min.js +30 -0
  17. data/lib/riemann/dash/public/vendor/flot/jquery.flot.image.js +241 -0
  18. data/lib/riemann/dash/public/vendor/flot/jquery.flot.image.min.js +53 -0
  19. data/lib/riemann/dash/public/vendor/flot/jquery.flot.js +3061 -0
  20. data/lib/riemann/dash/public/vendor/flot/jquery.flot.min.js +29 -0
  21. data/lib/riemann/dash/public/vendor/flot/jquery.flot.navigate.js +346 -0
  22. data/lib/riemann/dash/public/vendor/flot/jquery.flot.navigate.min.js +86 -0
  23. data/lib/riemann/dash/public/vendor/flot/jquery.flot.pie.js +817 -0
  24. data/lib/riemann/dash/public/vendor/flot/jquery.flot.pie.min.js +56 -0
  25. data/lib/riemann/dash/public/vendor/flot/jquery.flot.resize.js +60 -0
  26. data/lib/riemann/dash/public/vendor/flot/jquery.flot.resize.min.js +19 -0
  27. data/lib/riemann/dash/public/vendor/flot/jquery.flot.selection.js +360 -0
  28. data/lib/riemann/dash/public/vendor/flot/jquery.flot.selection.min.js +79 -0
  29. data/lib/riemann/dash/public/vendor/flot/jquery.flot.stack.js +188 -0
  30. data/lib/riemann/dash/public/vendor/flot/jquery.flot.stack.min.js +36 -0
  31. data/lib/riemann/dash/public/vendor/flot/jquery.flot.symbol.js +71 -0
  32. data/lib/riemann/dash/public/vendor/flot/jquery.flot.symbol.min.js +14 -0
  33. data/lib/riemann/dash/public/vendor/flot/jquery.flot.threshold.js +142 -0
  34. data/lib/riemann/dash/public/vendor/flot/jquery.flot.threshold.min.js +43 -0
  35. data/lib/riemann/dash/public/vendor/flot/jquery.flot.time.js +431 -0
  36. data/lib/riemann/dash/public/vendor/flot/jquery.flot.time.min.js +9 -0
  37. data/lib/riemann/dash/public/view.js +11 -7
  38. data/lib/riemann/dash/public/views/flot.js +248 -0
  39. data/lib/riemann/dash/public/views/grid.js +1 -5
  40. data/lib/riemann/dash/version.rb +1 -1
  41. data/lib/riemann/dash/views/css.scss +24 -0
  42. data/lib/riemann/dash/views/index.erubis +6 -0
  43. metadata +35 -2
@@ -9,37 +9,41 @@ var clock = (function() {
9
9
  if (clock < t) {
10
10
  // New local offset
11
11
  offset = t - (new Date());
12
- //console.log("new offset is", offset);
12
+ // console.log("new offset is", offset);
13
13
 
14
14
  clock = t;
15
+ // console.log("Clock advanced to", t / 1000);
15
16
  }
16
- //console.log("Clock is", clock);
17
17
  }
18
18
 
19
- var register = function(f) {
19
+ var subscribe = function(f) {
20
20
  callback_i = callback_i + 1;
21
21
  callbacks[callback_i] = f;
22
22
  return callback_i;
23
23
  }
24
24
 
25
- var unregister = function(i) {
26
- callbacks.delete(i);
25
+ var unsubscribe = function(i) {
26
+ delete callbacks[i];
27
27
  }
28
28
 
29
29
  // Automatically advance clock.
30
30
  setInterval(function() {
31
+ t1 = new Date();
31
32
  offset = offset * 0.99;
32
- advance(new Date(new Date().valueOf() + offset));
33
+ advance(new Date(t1.valueOf() + offset));
33
34
  $.each(callbacks, function(k, f) {
34
35
  f(clock);
35
36
  });
37
+ t2 = new Date();
38
+ subs.load1(t1, t2);
39
+ subs.load5(t1, t2);
36
40
  }, 1000);
37
41
 
38
42
  return {
39
43
  'clock': clock,
40
44
  'offset': offset,
41
45
  'advance': advance,
42
- 'register': register,
43
- 'unregister': unregister
46
+ 'subscribe': subscribe,
47
+ 'unsubscribe': unsubscribe
44
48
  }
45
49
  })();
@@ -27,8 +27,14 @@ var strings = (function() {
27
27
 
28
28
  // Like longestCommonPrefix, but only breaks at whitespace
29
29
  var commonPrefix = function(strings) {
30
- var regex = /(^.*\s+)/;
31
- var match = regex.exec(longestCommonPrefix(strings));
30
+ var prefix = longestCommonPrefix(strings);
31
+ if (strings[0] && strings[0] === prefix) {
32
+ // All strings are the same
33
+ return prefix;
34
+ }
35
+
36
+ var regex = /(^.*[\s\.]+)/;
37
+ var match = regex.exec(prefix);
32
38
  if (match) {
33
39
  return match[1];
34
40
  }
@@ -59,7 +59,7 @@ var subs = (function() {
59
59
  isClosed: function() { return !this.isOpen() },
60
60
 
61
61
  url: function() {
62
- var queryString = "query=" + encodeURI(this.query);
62
+ var queryString = "query=" + encodeURIComponent(this.query);
63
63
  var loc = window.location, ws_uri;
64
64
  if (loc.protocol === "https:") {
65
65
  ws_uri = "wss://";
@@ -91,10 +91,14 @@ var subs = (function() {
91
91
  ws.onmessage = _.bind(function(e) {
92
92
  t1 = Date.now();
93
93
  if (active) {
94
- this.f(JSON.parse(e.data));
94
+ var event = JSON.parse(e.data);
95
+ event.time = Date.parse(event.time);
96
+ clock.advance(event.time);
97
+ this.f(event);
95
98
  }
96
- load1(t1, Date.now());
97
- load5(t1, Date.now());
99
+ var t2 = Date.now();
100
+ load1(t1, t2);
101
+ load5(t1, t2);
98
102
  }, this);
99
103
 
100
104
  return this;
@@ -1,13 +1,26 @@
1
1
  var util = (function() {
2
+ // Takes the value of x as a string, or if x is undefined/null, a special
3
+ // marker string. Used because JS maps can't contain nil.
4
+ var nullableKey = function(x) {
5
+ return x || "\uffff";
6
+ };
7
+
2
8
  return {
9
+ nullableKey: nullableKey,
10
+
11
+ // A string key uniquely identifying an event by host and service.
12
+ eventKey: function(e) {
13
+ return nullableKey(e.host) + "\ufffe" + nullableKey(e.service);
14
+ },
15
+
16
+ // Merge two maps nondestructively.
3
17
  merge: function(m1, m2) {
4
- // Merge two maps nondestructively.
5
18
  return _.extend({}, m1, m2)
6
19
  },
20
+
21
+ // Wraps a function in another, which calls f at most once every period
22
+ // milliseconds. Tries to minimize latency.
7
23
  slur: function(period, f) {
8
- // Wraps a function in another, which calls f at most once every period
9
- // milliseconds. Tries to minimize latency.
10
-
11
24
  var lastRun = new Date();
12
25
  lastRun.setYear(0);
13
26
  var queued = false;
@@ -38,8 +51,9 @@ var util = (function() {
38
51
  }
39
52
  }
40
53
  },
54
+
55
+ // Unique-ish IDs as a length sized string of hex
41
56
  uniqueId: function(length) {
42
- // Unique-ish IDs as a length sized string of hex
43
57
  var id = '', hex = '0123456789abcdef';
44
58
  _(length || 40).times(function() { id += hex[_.random(15)]; });
45
59
  return id;
@@ -0,0 +1,179 @@
1
+ /* Plugin for jQuery for working with colors.
2
+ *
3
+ * Version 1.1.
4
+ *
5
+ * Inspiration from jQuery color animation plugin by John Resig.
6
+ *
7
+ * Released under the MIT license by Ole Laursen, October 2009.
8
+ *
9
+ * Examples:
10
+ *
11
+ * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
12
+ * var c = $.color.extract($("#mydiv"), 'background-color');
13
+ * console.log(c.r, c.g, c.b, c.a);
14
+ * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
15
+ *
16
+ * Note that .scale() and .add() return the same modified object
17
+ * instead of making a new one.
18
+ *
19
+ * V. 1.1: Fix error handling so e.g. parsing an empty string does
20
+ * produce a color rather than just crashing.
21
+ */
22
+
23
+ (function($) {
24
+ $.color = {};
25
+
26
+ // construct color object with some convenient chainable helpers
27
+ $.color.make = function (r, g, b, a) {
28
+ var o = {};
29
+ o.r = r || 0;
30
+ o.g = g || 0;
31
+ o.b = b || 0;
32
+ o.a = a != null ? a : 1;
33
+
34
+ o.add = function (c, d) {
35
+ for (var i = 0; i < c.length; ++i)
36
+ o[c.charAt(i)] += d;
37
+ return o.normalize();
38
+ };
39
+
40
+ o.scale = function (c, f) {
41
+ for (var i = 0; i < c.length; ++i)
42
+ o[c.charAt(i)] *= f;
43
+ return o.normalize();
44
+ };
45
+
46
+ o.toString = function () {
47
+ if (o.a >= 1.0) {
48
+ return "rgb("+[o.r, o.g, o.b].join(",")+")";
49
+ } else {
50
+ return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")";
51
+ }
52
+ };
53
+
54
+ o.normalize = function () {
55
+ function clamp(min, value, max) {
56
+ return value < min ? min: (value > max ? max: value);
57
+ }
58
+
59
+ o.r = clamp(0, parseInt(o.r), 255);
60
+ o.g = clamp(0, parseInt(o.g), 255);
61
+ o.b = clamp(0, parseInt(o.b), 255);
62
+ o.a = clamp(0, o.a, 1);
63
+ return o;
64
+ };
65
+
66
+ o.clone = function () {
67
+ return $.color.make(o.r, o.b, o.g, o.a);
68
+ };
69
+
70
+ return o.normalize();
71
+ }
72
+
73
+ // extract CSS color property from element, going up in the DOM
74
+ // if it's "transparent"
75
+ $.color.extract = function (elem, css) {
76
+ var c;
77
+ do {
78
+ c = elem.css(css).toLowerCase();
79
+ // keep going until we find an element that has color, or
80
+ // we hit the body
81
+ if (c != '' && c != 'transparent')
82
+ break;
83
+ elem = elem.parent();
84
+ } while (!$.nodeName(elem.get(0), "body"));
85
+
86
+ // catch Safari's way of signalling transparent
87
+ if (c == "rgba(0, 0, 0, 0)")
88
+ c = "transparent";
89
+
90
+ return $.color.parse(c);
91
+ }
92
+
93
+ // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"),
94
+ // returns color object, if parsing failed, you get black (0, 0,
95
+ // 0) out
96
+ $.color.parse = function (str) {
97
+ var res, m = $.color.make;
98
+
99
+ // Look for rgb(num,num,num)
100
+ if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))
101
+ return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10));
102
+
103
+ // Look for rgba(num,num,num,num)
104
+ if (res = /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))
105
+ return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4]));
106
+
107
+ // Look for rgb(num%,num%,num%)
108
+ if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))
109
+ return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55);
110
+
111
+ // Look for rgba(num%,num%,num%,num)
112
+ if (res = /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))
113
+ return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4]));
114
+
115
+ // Look for #a0b1c2
116
+ if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))
117
+ return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16));
118
+
119
+ // Look for #fff
120
+ if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))
121
+ return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16));
122
+
123
+ // Otherwise, we're most likely dealing with a named color
124
+ var name = $.trim(str).toLowerCase();
125
+ if (name == "transparent")
126
+ return m(255, 255, 255, 0);
127
+ else {
128
+ // default to black
129
+ res = lookupColors[name] || [0, 0, 0];
130
+ return m(res[0], res[1], res[2]);
131
+ }
132
+ }
133
+
134
+ var lookupColors = {
135
+ aqua:[0,255,255],
136
+ azure:[240,255,255],
137
+ beige:[245,245,220],
138
+ black:[0,0,0],
139
+ blue:[0,0,255],
140
+ brown:[165,42,42],
141
+ cyan:[0,255,255],
142
+ darkblue:[0,0,139],
143
+ darkcyan:[0,139,139],
144
+ darkgrey:[169,169,169],
145
+ darkgreen:[0,100,0],
146
+ darkkhaki:[189,183,107],
147
+ darkmagenta:[139,0,139],
148
+ darkolivegreen:[85,107,47],
149
+ darkorange:[255,140,0],
150
+ darkorchid:[153,50,204],
151
+ darkred:[139,0,0],
152
+ darksalmon:[233,150,122],
153
+ darkviolet:[148,0,211],
154
+ fuchsia:[255,0,255],
155
+ gold:[255,215,0],
156
+ green:[0,128,0],
157
+ indigo:[75,0,130],
158
+ khaki:[240,230,140],
159
+ lightblue:[173,216,230],
160
+ lightcyan:[224,255,255],
161
+ lightgreen:[144,238,144],
162
+ lightgrey:[211,211,211],
163
+ lightpink:[255,182,193],
164
+ lightyellow:[255,255,224],
165
+ lime:[0,255,0],
166
+ magenta:[255,0,255],
167
+ maroon:[128,0,0],
168
+ navy:[0,0,128],
169
+ olive:[128,128,0],
170
+ orange:[255,165,0],
171
+ pink:[255,192,203],
172
+ purple:[128,0,128],
173
+ violet:[128,0,128],
174
+ red:[255,0,0],
175
+ silver:[192,192,192],
176
+ white:[255,255,255],
177
+ yellow:[255,255,0]
178
+ };
179
+ })(jQuery);
@@ -0,0 +1,21 @@
1
+ /* Plugin for jQuery for working with colors.
2
+ *
3
+ * Version 1.1.
4
+ *
5
+ * Inspiration from jQuery color animation plugin by John Resig.
6
+ *
7
+ * Released under the MIT license by Ole Laursen, October 2009.
8
+ *
9
+ * Examples:
10
+ *
11
+ * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
12
+ * var c = $.color.extract($("#mydiv"), 'background-color');
13
+ * console.log(c.r, c.g, c.b, c.a);
14
+ * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
15
+ *
16
+ * Note that .scale() and .add() return the same modified object
17
+ * instead of making a new one.
18
+ *
19
+ * V. 1.1: Fix error handling so e.g. parsing an empty string does
20
+ * produce a color rather than just crashing.
21
+ */(function(e){e.color={},e.color.make=function(t,n,r,i){var s={};return s.r=t||0,s.g=n||0,s.b=r||0,s.a=i!=null?i:1,s.add=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]+=t;return s.normalize()},s.scale=function(e,t){for(var n=0;n<e.length;++n)s[e.charAt(n)]*=t;return s.normalize()},s.toString=function(){return s.a>=1?"rgb("+[s.r,s.g,s.b].join(",")+")":"rgba("+[s.r,s.g,s.b,s.a].join(",")+")"},s.normalize=function(){function e(e,t,n){return t<e?e:t>n?n:t}return s.r=e(0,parseInt(s.r),255),s.g=e(0,parseInt(s.g),255),s.b=e(0,parseInt(s.b),255),s.a=e(0,s.a,1),s},s.clone=function(){return e.color.make(s.r,s.b,s.g,s.a)},s.normalize()},e.color.extract=function(t,n){var r;do{r=t.css(n).toLowerCase();if(r!=""&&r!="transparent")break;t=t.parent()}while(!e.nodeName(t.get(0),"body"));return r=="rgba(0, 0, 0, 0)"&&(r="transparent"),e.color.parse(r)},e.color.parse=function(n){var r,i=e.color.make;if(r=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10));if(r=/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(n))return i(parseInt(r[1],10),parseInt(r[2],10),parseInt(r[3],10),parseFloat(r[4]));if(r=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55);if(r=/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(n))return i(parseFloat(r[1])*2.55,parseFloat(r[2])*2.55,parseFloat(r[3])*2.55,parseFloat(r[4]));if(r=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(n))return i(parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16));if(r=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(n))return i(parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16),parseInt(r[3]+r[3],16));var s=e.trim(n).toLowerCase();return s=="transparent"?i(255,255,255,0):(r=t[s]||[0,0,0],i(r[0],r[1],r[2]))};var t={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]}})(jQuery);
@@ -0,0 +1,345 @@
1
+ /* Flot plugin for drawing all elements of a plot on the canvas.
2
+
3
+ Copyright (c) 2007-2013 IOLA and Ole Laursen.
4
+ Licensed under the MIT license.
5
+
6
+ Flot normally produces certain elements, like axis labels and the legend, using
7
+ HTML elements. This permits greater interactivity and customization, and often
8
+ looks better, due to cross-browser canvas text inconsistencies and limitations.
9
+
10
+ It can also be desirable to render the plot entirely in canvas, particularly
11
+ if the goal is to save it as an image, or if Flot is being used in a context
12
+ where the HTML DOM does not exist, as is the case within Node.js. This plugin
13
+ switches out Flot's standard drawing operations for canvas-only replacements.
14
+
15
+ Currently the plugin supports only axis labels, but it will eventually allow
16
+ every element of the plot to be rendered directly to canvas.
17
+
18
+ The plugin supports these options:
19
+
20
+ {
21
+ canvas: boolean
22
+ }
23
+
24
+ The "canvas" option controls whether full canvas drawing is enabled, making it
25
+ possible to toggle on and off. This is useful when a plot uses HTML text in the
26
+ browser, but needs to redraw with canvas text when exporting as an image.
27
+
28
+ */
29
+
30
+ (function($) {
31
+
32
+ var options = {
33
+ canvas: true
34
+ };
35
+
36
+ var render, getTextInfo, addText;
37
+
38
+ // Cache the prototype hasOwnProperty for faster access
39
+
40
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
41
+
42
+ function init(plot, classes) {
43
+
44
+ var Canvas = classes.Canvas;
45
+
46
+ // We only want to replace the functions once; the second time around
47
+ // we would just get our new function back. This whole replacing of
48
+ // prototype functions is a disaster, and needs to be changed ASAP.
49
+
50
+ if (render == null) {
51
+ getTextInfo = Canvas.prototype.getTextInfo,
52
+ addText = Canvas.prototype.addText,
53
+ render = Canvas.prototype.render;
54
+ }
55
+
56
+ // Finishes rendering the canvas, including overlaid text
57
+
58
+ Canvas.prototype.render = function() {
59
+
60
+ if (!plot.getOptions().canvas) {
61
+ return render.call(this);
62
+ }
63
+
64
+ var context = this.context,
65
+ cache = this._textCache;
66
+
67
+ // For each text layer, render elements marked as active
68
+
69
+ context.save();
70
+ context.textBaseline = "middle";
71
+
72
+ for (var layerKey in cache) {
73
+ if (hasOwnProperty.call(cache, layerKey)) {
74
+ var layerCache = cache[layerKey];
75
+ for (var styleKey in layerCache) {
76
+ if (hasOwnProperty.call(layerCache, styleKey)) {
77
+ var styleCache = layerCache[styleKey],
78
+ updateStyles = true;
79
+ for (var key in styleCache) {
80
+ if (hasOwnProperty.call(styleCache, key)) {
81
+
82
+ var info = styleCache[key],
83
+ positions = info.positions,
84
+ lines = info.lines;
85
+
86
+ // Since every element at this level of the cache have the
87
+ // same font and fill styles, we can just change them once
88
+ // using the values from the first element.
89
+
90
+ if (updateStyles) {
91
+ context.fillStyle = info.font.color;
92
+ context.font = info.font.definition;
93
+ updateStyles = false;
94
+ }
95
+
96
+ for (var i = 0, position; position = positions[i]; i++) {
97
+ if (position.active) {
98
+ for (var j = 0, line; line = position.lines[j]; j++) {
99
+ context.fillText(lines[j].text, line[0], line[1]);
100
+ }
101
+ } else {
102
+ positions.splice(i--, 1);
103
+ }
104
+ }
105
+
106
+ if (positions.length == 0) {
107
+ delete styleCache[key];
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ context.restore();
117
+ };
118
+
119
+ // Creates (if necessary) and returns a text info object.
120
+ //
121
+ // When the canvas option is set, the object looks like this:
122
+ //
123
+ // {
124
+ // width: Width of the text's bounding box.
125
+ // height: Height of the text's bounding box.
126
+ // positions: Array of positions at which this text is drawn.
127
+ // lines: [{
128
+ // height: Height of this line.
129
+ // widths: Width of this line.
130
+ // text: Text on this line.
131
+ // }],
132
+ // font: {
133
+ // definition: Canvas font property string.
134
+ // color: Color of the text.
135
+ // },
136
+ // }
137
+ //
138
+ // The positions array contains objects that look like this:
139
+ //
140
+ // {
141
+ // active: Flag indicating whether the text should be visible.
142
+ // lines: Array of [x, y] coordinates at which to draw the line.
143
+ // x: X coordinate at which to draw the text.
144
+ // y: Y coordinate at which to draw the text.
145
+ // }
146
+
147
+ Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) {
148
+
149
+ if (!plot.getOptions().canvas) {
150
+ return getTextInfo.call(this, layer, text, font, angle, width);
151
+ }
152
+
153
+ var textStyle, layerCache, styleCache, info;
154
+
155
+ // Cast the value to a string, in case we were given a number
156
+
157
+ text = "" + text;
158
+
159
+ // If the font is a font-spec object, generate a CSS definition
160
+
161
+ if (typeof font === "object") {
162
+ textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family;
163
+ } else {
164
+ textStyle = font;
165
+ }
166
+
167
+ // Retrieve (or create) the cache for the text's layer and styles
168
+
169
+ layerCache = this._textCache[layer];
170
+
171
+ if (layerCache == null) {
172
+ layerCache = this._textCache[layer] = {};
173
+ }
174
+
175
+ styleCache = layerCache[textStyle];
176
+
177
+ if (styleCache == null) {
178
+ styleCache = layerCache[textStyle] = {};
179
+ }
180
+
181
+ info = styleCache[text];
182
+
183
+ if (info == null) {
184
+
185
+ var context = this.context;
186
+
187
+ // If the font was provided as CSS, create a div with those
188
+ // classes and examine it to generate a canvas font spec.
189
+
190
+ if (typeof font !== "object") {
191
+
192
+ var element = $("<div>&nbsp;</div>")
193
+ .css("position", "absolute")
194
+ .addClass(typeof font === "string" ? font : null)
195
+ .appendTo(this.getTextLayer(layer));
196
+
197
+ font = {
198
+ lineHeight: element.height(),
199
+ style: element.css("font-style"),
200
+ variant: element.css("font-variant"),
201
+ weight: element.css("font-weight"),
202
+ family: element.css("font-family"),
203
+ color: element.css("color")
204
+ };
205
+
206
+ // Setting line-height to 1, without units, sets it equal
207
+ // to the font-size, even if the font-size is abstract,
208
+ // like 'smaller'. This enables us to read the real size
209
+ // via the element's height, working around browsers that
210
+ // return the literal 'smaller' value.
211
+
212
+ font.size = element.css("line-height", 1).height();
213
+
214
+ element.remove();
215
+ }
216
+
217
+ textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family;
218
+
219
+ // Create a new info object, initializing the dimensions to
220
+ // zero so we can count them up line-by-line.
221
+
222
+ info = styleCache[text] = {
223
+ width: 0,
224
+ height: 0,
225
+ positions: [],
226
+ lines: [],
227
+ font: {
228
+ definition: textStyle,
229
+ color: font.color
230
+ }
231
+ };
232
+
233
+ context.save();
234
+ context.font = textStyle;
235
+
236
+ // Canvas can't handle multi-line strings; break on various
237
+ // newlines, including HTML brs, to build a list of lines.
238
+ // Note that we could split directly on regexps, but IE < 9 is
239
+ // broken; revisit when we drop IE 7/8 support.
240
+
241
+ var lines = (text + "").replace(/<br ?\/?>|\r\n|\r/g, "\n").split("\n");
242
+
243
+ for (var i = 0; i < lines.length; ++i) {
244
+
245
+ var lineText = lines[i],
246
+ measured = context.measureText(lineText);
247
+
248
+ info.width = Math.max(measured.width, info.width);
249
+ info.height += font.lineHeight;
250
+
251
+ info.lines.push({
252
+ text: lineText,
253
+ width: measured.width,
254
+ height: font.lineHeight
255
+ });
256
+ }
257
+
258
+ context.restore();
259
+ }
260
+
261
+ return info;
262
+ };
263
+
264
+ // Adds a text string to the canvas text overlay.
265
+
266
+ Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) {
267
+
268
+ if (!plot.getOptions().canvas) {
269
+ return addText.call(this, layer, x, y, text, font, angle, width, halign, valign);
270
+ }
271
+
272
+ var info = this.getTextInfo(layer, text, font, angle, width),
273
+ positions = info.positions,
274
+ lines = info.lines;
275
+
276
+ // Text is drawn with baseline 'middle', which we need to account
277
+ // for by adding half a line's height to the y position.
278
+
279
+ y += info.height / lines.length / 2;
280
+
281
+ // Tweak the initial y-position to match vertical alignment
282
+
283
+ if (valign == "middle") {
284
+ y = Math.round(y - info.height / 2);
285
+ } else if (valign == "bottom") {
286
+ y = Math.round(y - info.height);
287
+ } else {
288
+ y = Math.round(y);
289
+ }
290
+
291
+ // FIXME: LEGACY BROWSER FIX
292
+ // AFFECTS: Opera < 12.00
293
+
294
+ // Offset the y coordinate, since Opera is off pretty
295
+ // consistently compared to the other browsers.
296
+
297
+ if (!!(window.opera && window.opera.version().split(".")[0] < 12)) {
298
+ y -= 2;
299
+ }
300
+
301
+ // Determine whether this text already exists at this position.
302
+ // If so, mark it for inclusion in the next render pass.
303
+
304
+ for (var i = 0, position; position = positions[i]; i++) {
305
+ if (position.x == x && position.y == y) {
306
+ position.active = true;
307
+ return;
308
+ }
309
+ }
310
+
311
+ // If the text doesn't exist at this position, create a new entry
312
+
313
+ position = {
314
+ active: true,
315
+ lines: [],
316
+ x: x,
317
+ y: y
318
+ };
319
+
320
+ positions.push(position);
321
+
322
+ // Fill in the x & y positions of each line, adjusting them
323
+ // individually for horizontal alignment.
324
+
325
+ for (var i = 0, line; line = lines[i]; i++) {
326
+ if (halign == "center") {
327
+ position.lines.push([Math.round(x - line.width / 2), y]);
328
+ } else if (halign == "right") {
329
+ position.lines.push([Math.round(x - line.width), y]);
330
+ } else {
331
+ position.lines.push([Math.round(x), y]);
332
+ }
333
+ y += line.height;
334
+ }
335
+ };
336
+ }
337
+
338
+ $.plot.plugins.push({
339
+ init: init,
340
+ options: options,
341
+ name: "canvas",
342
+ version: "1.0"
343
+ });
344
+
345
+ })(jQuery);