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.
- data/lib/riemann/dash/public/clock.js +12 -8
- data/lib/riemann/dash/public/strings.js +8 -2
- data/lib/riemann/dash/public/subs.js +8 -4
- data/lib/riemann/dash/public/util.js +19 -5
- data/lib/riemann/dash/public/vendor/flot/jquery.colorhelpers.js +179 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.colorhelpers.min.js +21 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.canvas.js +345 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.canvas.min.js +28 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.categories.js +190 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.categories.min.js +44 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.crosshair.js +176 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.crosshair.min.js +59 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.errorbars.js +353 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.errorbars.min.js +63 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.fillbetween.js +226 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.fillbetween.min.js +30 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.image.js +241 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.image.min.js +53 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.js +3061 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.min.js +29 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.navigate.js +346 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.navigate.min.js +86 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.pie.js +817 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.pie.min.js +56 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.resize.js +60 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.resize.min.js +19 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.selection.js +360 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.selection.min.js +79 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.stack.js +188 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.stack.min.js +36 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.symbol.js +71 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.symbol.min.js +14 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.threshold.js +142 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.threshold.min.js +43 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.time.js +431 -0
- data/lib/riemann/dash/public/vendor/flot/jquery.flot.time.min.js +9 -0
- data/lib/riemann/dash/public/view.js +11 -7
- data/lib/riemann/dash/public/views/flot.js +248 -0
- data/lib/riemann/dash/public/views/grid.js +1 -5
- data/lib/riemann/dash/version.rb +1 -1
- data/lib/riemann/dash/views/css.scss +24 -0
- data/lib/riemann/dash/views/index.erubis +6 -0
- 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
|
-
|
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
|
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
|
26
|
-
callbacks
|
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(
|
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
|
-
'
|
43
|
-
'
|
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
|
31
|
-
|
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=" +
|
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
|
-
|
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
|
-
|
97
|
-
|
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> </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);
|