fnordmetric 0.7.5 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. data/doc/V1.0-ROADMAP +97 -0
  2. data/doc/full_example.rb +95 -511
  3. data/doc/legacy_example.rb +640 -0
  4. data/doc/minimal_example.rb +26 -0
  5. data/doc/preview3.png +0 -0
  6. data/fnordmetric.gemspec +3 -2
  7. data/lib/fnordmetric/acceptors/acceptor.rb +29 -0
  8. data/lib/fnordmetric/{inbound_stream.rb → acceptors/tcp_acceptor.rb} +8 -5
  9. data/lib/fnordmetric/{inbound_datagram.rb → acceptors/udp_acceptor.rb} +9 -8
  10. data/lib/fnordmetric/api.rb +2 -2
  11. data/lib/fnordmetric/context.rb +37 -18
  12. data/lib/fnordmetric/defaults.rb +9 -0
  13. data/lib/fnordmetric/ext.rb +72 -0
  14. data/lib/fnordmetric/gauge.rb +37 -10
  15. data/lib/fnordmetric/gauge_calculations.rb +38 -16
  16. data/lib/fnordmetric/gauge_modifiers.rb +67 -0
  17. data/lib/fnordmetric/gauge_rendering.rb +40 -0
  18. data/lib/fnordmetric/gauge_validations.rb +15 -0
  19. data/lib/fnordmetric/gauges/distribution_gauge.rb +85 -0
  20. data/lib/fnordmetric/gauges/timeseries_gauge.rb +143 -0
  21. data/lib/fnordmetric/gauges/toplist_gauge.rb +44 -0
  22. data/lib/fnordmetric/histogram.rb +57 -0
  23. data/lib/fnordmetric/logger.rb +42 -36
  24. data/lib/fnordmetric/namespace.rb +47 -23
  25. data/lib/fnordmetric/session.rb +6 -6
  26. data/lib/fnordmetric/standalone.rb +15 -35
  27. data/lib/fnordmetric/timeseries.rb +79 -0
  28. data/lib/fnordmetric/toplist.rb +61 -0
  29. data/lib/fnordmetric/version.rb +1 -1
  30. data/lib/fnordmetric/web/app.rb +122 -0
  31. data/lib/fnordmetric/web/app_helpers.rb +42 -0
  32. data/lib/fnordmetric/{dashboard.rb → web/dashboard.rb} +4 -0
  33. data/lib/fnordmetric/{event.rb → web/event.rb} +7 -2
  34. data/lib/fnordmetric/web/reactor.rb +87 -0
  35. data/lib/fnordmetric/web/web.rb +53 -0
  36. data/lib/fnordmetric/web/websocket.rb +38 -0
  37. data/lib/fnordmetric/widgets/bars_widget.rb +44 -0
  38. data/lib/fnordmetric/{html_widget.rb → widgets/html_widget.rb} +0 -0
  39. data/lib/fnordmetric/widgets/numbers_widget.rb +56 -0
  40. data/lib/fnordmetric/{pie_widget.rb → widgets/pie_widget.rb} +0 -0
  41. data/lib/fnordmetric/widgets/timeseries_widget.rb +55 -0
  42. data/lib/fnordmetric/widgets/toplist_widget.rb +64 -0
  43. data/lib/fnordmetric/worker.rb +26 -25
  44. data/lib/fnordmetric.rb +85 -115
  45. data/readme.md +362 -0
  46. data/spec/gauge_like_shared.rb +54 -0
  47. data/spec/gauge_spec.rb +2 -36
  48. data/spec/namespace_spec.rb +25 -11
  49. data/spec/spec_helper.rb +4 -0
  50. data/spec/{inbound_stream_spec.rb → tcp_acceptor_spec.rb} +3 -3
  51. data/spec/timeseries_gauge_spec.rb +54 -0
  52. data/spec/{inbound_datagram_spec.rb → udp_acceptor_spec.rb} +3 -3
  53. data/web/fnordmetric.css +786 -0
  54. data/web/haml/app.haml +38 -0
  55. data/web/haml/distribution_gauge.haml +118 -0
  56. data/web/haml/timeseries_gauge.haml +80 -0
  57. data/web/haml/toplist_gauge.haml +194 -0
  58. data/web/img/head.png +0 -0
  59. data/web/img/list.png +0 -0
  60. data/web/img/list_active.png +0 -0
  61. data/web/img/list_hover.png +0 -0
  62. data/web/img/loader_white.gif +0 -0
  63. data/web/img/navbar.png +0 -0
  64. data/web/img/navbar_btn.png +0 -0
  65. data/web/img/picto_gauge.png +0 -0
  66. data/web/js/fnordmetric.bars_widget.js +178 -0
  67. data/web/js/fnordmetric.dashboard_view.js +99 -0
  68. data/web/js/fnordmetric.gauge_view.js +260 -0
  69. data/web/js/fnordmetric.html_widget.js +21 -0
  70. data/web/js/fnordmetric.js +255 -0
  71. data/web/js/fnordmetric.numbers_widget.js +121 -0
  72. data/web/js/fnordmetric.overview_view.js +35 -0
  73. data/web/js/fnordmetric.pie_widget.js +118 -0
  74. data/web/js/fnordmetric.realtime_timeline_widget.js +175 -0
  75. data/web/js/fnordmetric.session_view.js +343 -0
  76. data/web/js/fnordmetric.timeline_widget.js +333 -0
  77. data/web/js/fnordmetric.timeseries_widget.js +388 -0
  78. data/web/js/fnordmetric.toplist_widget.js +112 -0
  79. data/web/js/fnordmetric.ui.js +91 -0
  80. data/web/js/fnordmetric.util.js +244 -0
  81. data/{pub → web}/loader.gif +0 -0
  82. data/web/vendor/d3.v2.js +9382 -0
  83. data/web/vendor/font-awesome/css/font-awesome.css +239 -0
  84. data/web/vendor/font-awesome/font/fontawesome-webfont.eot +0 -0
  85. data/web/vendor/font-awesome/font/fontawesome-webfont.svg +175 -0
  86. data/web/vendor/font-awesome/font/fontawesome-webfont.svgz +0 -0
  87. data/web/vendor/font-awesome/font/fontawesome-webfont.ttf +0 -0
  88. data/web/vendor/font-awesome/font/fontawesome-webfont.woff +0 -0
  89. data/web/vendor/jquery-1.6.2.min.js +18 -0
  90. data/web/vendor/jquery-ui.min.js +413 -0
  91. data/web/vendor/jquery.maskedinput.js +252 -0
  92. data/web/vendor/rickshaw.css +286 -0
  93. data/web/vendor/rickshaw.fnordmetric.js +2676 -0
  94. metadata +129 -79
  95. data/Gemfile +0 -6
  96. data/README.md +0 -404
  97. data/Rakefile +0 -6
  98. data/doc/version +0 -1
  99. data/haml/app.haml +0 -79
  100. data/haml/widget.haml +0 -9
  101. data/lib/fnordmetric/app.rb +0 -163
  102. data/lib/fnordmetric/average_metric.rb +0 -7
  103. data/lib/fnordmetric/bars_widget.rb +0 -26
  104. data/lib/fnordmetric/combine_metric.rb +0 -7
  105. data/lib/fnordmetric/count_metric.rb +0 -13
  106. data/lib/fnordmetric/funnel_widget.rb +0 -2
  107. data/lib/fnordmetric/metric.rb +0 -80
  108. data/lib/fnordmetric/metric_api.rb +0 -37
  109. data/lib/fnordmetric/numbers_widget.rb +0 -26
  110. data/lib/fnordmetric/report.rb +0 -29
  111. data/lib/fnordmetric/sum_metric.rb +0 -13
  112. data/lib/fnordmetric/timeline_widget.rb +0 -30
  113. data/lib/fnordmetric/toplist_widget.rb +0 -25
  114. data/pub/fnordmetric.css +0 -145
  115. data/pub/fnordmetric.js +0 -1179
  116. data/pub/vendor/highcharts.js +0 -170
  117. data/pub/vendor/jquery-1.6.1.min.js +0 -18
@@ -0,0 +1,343 @@
1
+ FnordMetric.views.sessionView = (function(){
2
+
3
+ var listElem = $('<ul class="session_list"></ul>');
4
+ var feedInnerElem = $('<ul class="feed_inner"></ul>');
5
+ var typeListElem = $('<ul class="event_type_list"></ul>');
6
+
7
+ var filterElem = $('<div class="events_sidebar"></div>')
8
+ .append("<div class='headbar small'>Event Types</div>")
9
+ .append(typeListElem);
10
+
11
+ var feedElem = $('<div class="sessions_feed"></div>').append(feedInnerElem);
12
+ var sideElem = $('<div class="sessions_sidebar"></div>').append(listElem);
13
+
14
+ var eventsPolledUntil = false;
15
+ var eventsFilter = {uncheckedTypes: [], checkedSessions: []};
16
+ var sessionData = {};
17
+ var pollRunning = true;
18
+ var first_poll = true;
19
+
20
+ function load(elem){
21
+ eventsPolledUntil = parseInt(new Date().getTime()/10000);
22
+
23
+ elem.html('')
24
+ .append('<div class="navbar"></div>');
25
+
26
+ FnordMetric.ui.navbar($('.navbar', viewport), {
27
+ breadcrumb: [
28
+ ["Fnord", "/group/fnord"],
29
+ ["Active Users", "/active_users"]
30
+ ],
31
+ buttons: [
32
+ ["<i class='icon-refresh'></i>Refresh", function(){ }]
33
+ ]
34
+ });
35
+
36
+ elem
37
+ .append(filterElem.css('marginTop', '47px'))
38
+ .append(feedElem)
39
+ .append(sideElem);
40
+
41
+ startPoll();
42
+ loadEventTypes();
43
+ };
44
+
45
+ function resize(_width, _height){
46
+ $('.sessions_feed').width(_width-452);
47
+ };
48
+
49
+ function startPoll(){
50
+ (doSessionPoll())();
51
+ (doEventsPoll())();
52
+ FnordMetric.views.sessionView.session_poll = window.setInterval(doSessionPoll(), 1000);
53
+ };
54
+
55
+ function stopPoll(){
56
+ pollRunning = false;
57
+ window.clearInterval(FnordMetric.views.sessionView.session_poll);
58
+ }
59
+
60
+ function doSessionPoll(){
61
+ return (function(){
62
+ /*$.ajax({
63
+ url: FnordMetric.p + '/' + FnordMetric.currentNamespace+'/sessions',
64
+ success: callbackSessionPoll()
65
+ });*/
66
+ sortSessions();
67
+ });
68
+ };
69
+
70
+ function loadEventHistory(params){
71
+ feedInnerElem.html('');
72
+ /*$.ajax({
73
+ url: FnordMetric.p + '/' + FnordMetric.currentNamespace+'/events',
74
+ data: params,
75
+ success: function(_data, _status){
76
+ var data = JSON.parse(_data).events;
77
+ for(var n=data.length; n >= 0; n--){
78
+ if(data[n]){ renderEvent(data[n]); }
79
+ }
80
+ }
81
+ });*/
82
+ }
83
+
84
+ function callbackSessionPoll(){
85
+ return (function(_data, _status){
86
+ $.each(JSON.parse(_data).sessions, function(i,v){
87
+ updateSession(v);
88
+ });
89
+ sortSessions();
90
+ });
91
+ };
92
+
93
+ function loadEventTypes(data){
94
+ typeListElem.html('');
95
+ $(data).each(function(i,v){
96
+ if((v.length > 0) && (v.slice(0,5)!='_set_')){
97
+ addEventType(v,v);
98
+ }
99
+ });
100
+ };
101
+
102
+ function setCheckboxesCheckedState(types_state, sessions_state) {
103
+ $('.event_type_list .event_type input').attr('checked', types_state);
104
+ $('.session_list .session input').attr('checked', sessions_state);
105
+ }
106
+
107
+ function addEventType(type, display){
108
+ typeListElem.append(
109
+ $('<li class="event_type"></li>').append(
110
+ $('<span class="history"></span>').html('history')
111
+ .click(function(){
112
+ setCheckboxesCheckedState(true, true);
113
+ $('input', $(this).parent()).attr('checked', true);
114
+ updateEventFilter(); loadEventHistory({type: type});
115
+ })
116
+ ).append(
117
+ $('<input type="checkbox" />').attr('checked', true)
118
+ .click(function(){ updateEventFilter(); })
119
+ ).append(
120
+ $('<span></span>').html(display)
121
+ ).attr('rel', type)
122
+ );
123
+ }
124
+
125
+ function updateEventFilter(){
126
+ var _unchecked_types = [];
127
+ $('ul.event_type_list li.event_type').each(function(i,v){
128
+ if(!$('input', v).attr('checked')){
129
+ _unchecked_types.push($(v).attr('rel'));
130
+ }
131
+ });
132
+ eventsFilter.uncheckedTypes = _unchecked_types;
133
+
134
+ var _checked_sessions = [];
135
+ $('ul.session_list li.session').each(function(i,v){
136
+ if($('input', v).attr('checked')){
137
+ _checked_sessions.push($(v).data().session);
138
+ }
139
+ });
140
+ eventsFilter.checkedSessions = _checked_sessions;
141
+ }
142
+
143
+ function doEventsPoll(){
144
+ return (function(){
145
+
146
+ /*$.ajax({
147
+ url: FnordMetric.p + '/' + FnordMetric.currentNamespace+'/events?since='+eventsPolledUntil,
148
+ success: callbackEventsPoll()
149
+ });*/
150
+
151
+ FnordMetric.publish({
152
+ "type": "active_users_request",
153
+ "first_poll": first_poll,
154
+ "since": eventsPolledUntil
155
+ });
156
+
157
+ first_poll = false;
158
+
159
+ });
160
+ };
161
+
162
+ function announce(evt){
163
+ if (evt.type == "active_users_response"){
164
+ callbackEventsPoll(evt);
165
+ $(evt.sessions).each(function(){ updateSession(this); });
166
+ if(evt.types.length > 0){ loadEventTypes(evt.types); }
167
+ }
168
+ }
169
+
170
+ function callbackEventsPoll(data){
171
+ var events = data.events;
172
+ var timeout = 1000;
173
+ var maxevents = 200;
174
+ var passesFiltering = function(event_data) {
175
+ var passes_type_filtering = false;
176
+ var passes_session_filtering = false;
177
+ if(eventsFilter.uncheckedTypes.indexOf(event_data._type) == -1) {
178
+ if(parseInt(v._time)<=eventsPolledUntil) {
179
+ passes_type_filtering = true;
180
+ }
181
+ }
182
+ if(!passes_type_filtering) return false;
183
+
184
+ if(eventsFilter.checkedSessions.length == 0){
185
+ return true; // No filter set - show all events
186
+ } else {
187
+ if(event_data._session_key){
188
+ if(eventsFilter.checkedSessions.indexOf(event_data._session_key) >= 0){
189
+ return true; // Filter set and match
190
+ } else {
191
+ return false; // Filter set but no match
192
+ }
193
+ } else {
194
+ return false; // Filter set but event is not associated with session
195
+ }
196
+ }
197
+ }
198
+
199
+ if(events.length > 0){
200
+ eventsPolledUntil = parseInt(events[0]._time)-1;
201
+ }
202
+ for(var n=events.length-1; n >= 0; n--){
203
+ var v = events[n];
204
+ if(passesFiltering(v)) {
205
+ renderEvent(v);
206
+ }
207
+ };
208
+ var elems = $("p", feedInnerElem);
209
+ for(var n=maxevents; n < elems.length; n++){
210
+ $(elems[n]).remove();
211
+ }
212
+ if(pollRunning){
213
+ window.setTimeout(doEventsPoll(), timeout);
214
+ }
215
+ };
216
+
217
+ function updateSession(session_data){
218
+ var session_key = session_data.session_key;
219
+ if(!sessionData[session_key]){
220
+ updateEventFilter()
221
+ }
222
+ sessionData[session_key] = session_data;
223
+ renderSession(session_data);
224
+ }
225
+
226
+ function sortSessions(){
227
+ console.log("fixpaul: sort and splice to 100");
228
+ }
229
+
230
+ function renderSession(session_data){
231
+
232
+ var session_name = session_data["_name"];
233
+ var session_time = FnordMetric.util.formatTimeSince(session_data["_updated_at"]);
234
+ var session_elem = $('li[data-session='+session_data["session_key"]+']:first');
235
+
236
+ if(session_elem.length>0){
237
+
238
+ if(session_data["_picture"] && (session_data["_picture"].length > 1)){
239
+ $('.picture img', session_elem).attr('src', session_data["_picture"])
240
+ }
241
+
242
+ if(session_name){
243
+ $('.name', session_elem).html(session_name);
244
+ }
245
+
246
+ $('.time', session_elem).html(session_time);
247
+
248
+ } else {
249
+
250
+ var session_picture = $('<img width="25" />');
251
+
252
+ if(!session_name){
253
+ session_name = session_data["session_key"].substr(0,15)
254
+ };
255
+
256
+ if(session_data["_picture"]){
257
+ session_picture.attr('src', session_data["_picture"]);
258
+ };
259
+
260
+ listElem.append(
261
+ $('<li class="session"></li>').append(
262
+ $('<div class="picture"></div>').html(session_picture)
263
+ ).append(
264
+ $('<span class="name"></span>').html(session_name)
265
+ ).append(
266
+ $('<span class="time"></span>').html(session_time)
267
+ ).append(
268
+ $('<span class="history"></span>').html('history')
269
+ .click(function(){
270
+ setCheckboxesCheckedState(true, false);
271
+ updateEventFilter();
272
+ loadEventHistory({session_key: session_data["session_key"]});
273
+ })
274
+ ).attr('data-session', session_data["session_key"])
275
+ );
276
+
277
+ }
278
+ };
279
+
280
+ function renderEvent(event_data){
281
+ var event_time = $('<span class="time"></span>');
282
+ var event_message = $('<span class="message"></span>');
283
+ var event_props = $('<span class="properties"></span>');
284
+ var event_picture = $('<div class="picture"></picture>');
285
+
286
+ var event_type = event_data._type;
287
+
288
+ if(!event_type){ return true; }
289
+
290
+ if(event_data._message){
291
+ event_message.html(event_data._message);
292
+ } else if(event_type=="_pageview"){
293
+ event_message.html("Pageview: " + event_data.url);
294
+ } else if(event_type.substr(0,5) == '_set_'){
295
+ return true; /* dont render */
296
+ } else {
297
+ event_message.html(event_type);
298
+ }
299
+
300
+ event_time.html(FnordMetric.util.formatTimeOfDay(event_data._time));
301
+
302
+ if(event_data._session_key && event_data._session_key.length > 0){
303
+ var __session_key = event_data._session_key;
304
+ var load_usersession = (function(){
305
+ loadEventHistory({session_key: __session_key});
306
+ });
307
+ if(session_data=sessionData[event_data._session_key]){
308
+ if(session_data._name){
309
+ event_props.append(
310
+ $('<strong></strong>').html(session_data._name).css({
311
+ 'cursor': 'pointer'
312
+ }).click(load_usersession)
313
+ );
314
+ }
315
+ if(session_data._picture){
316
+ event_picture.append(
317
+ $('<img width="40" />').attr('src', session_data._picture)
318
+ ).click(load_usersession);
319
+ }
320
+ }
321
+ }
322
+
323
+ feedInnerElem.prepend(
324
+ $('<li class="feed_event"></li>')
325
+ .append(event_time)
326
+ .append(event_picture)
327
+ .append(event_message)
328
+ .append(event_props)
329
+ );
330
+ }
331
+
332
+ function close(){
333
+ stopPoll();
334
+ };
335
+
336
+ return {
337
+ load: load,
338
+ resize: resize,
339
+ announce: announce,
340
+ close: close
341
+ };
342
+
343
+ });
@@ -0,0 +1,333 @@
1
+ FnordMetric.widgets.timelineWidget = function(){
2
+
3
+ var widget_uid = FnordMetric.util.getNextWidgetUID();
4
+ var width, height, canvas, series, opts, xtick;
5
+ var xticks = 100;
6
+ var running_request = false;
7
+
8
+ var series_paths = {};
9
+ var series_values = {};
10
+
11
+ var xpadding = 30;
12
+
13
+ function render(_opts){
14
+ opts = _opts;
15
+ //if(!silent){ $(opts.elem).css('opacity', 0.5); }
16
+
17
+ if(opts.xticks){ xticks = opts.xticks; }
18
+ if(!opts.draw_points){ opts.draw_points = true; }
19
+ if(!opts.draw_path){ opts.draw_path = true; }
20
+ if(!opts.draw_ygrid){ opts.draw_ygrid = true; }
21
+ if(!opts.draw_xgrid){ opts.draw_xgrid = true; }
22
+
23
+ drawLayout(opts);
24
+
25
+ width = opts.elem.width() - (xpadding * 2) - 15;
26
+ height = opts.height || 240;
27
+ xtick = width / (xticks - 1);
28
+
29
+ canvas = d3.select('#container-'+widget_uid)
30
+ .append("svg:svg")
31
+ .attr("width", width+(2*xpadding))
32
+ .attr("height", height+30);
33
+
34
+ canvas.selectAll("*").remove();
35
+
36
+ for (ind in opts.series){
37
+ series_values[opts.series[ind]] = {};
38
+ }
39
+
40
+ updateRange();
41
+ updateChart();
42
+
43
+ if(opts.autoupdate){
44
+ var secs = parseInt(opts.autoupdate);
45
+ if(secs > 0){
46
+ var autoupdate_interval = window.setInterval(function(){
47
+ updateRange(true); updateChart(); // FIXPAUL: only update if not scrolled
48
+ }, secs*1000);
49
+
50
+ $('body').bind('fm_dashboard_close', function(){
51
+ window.clearInterval(autoupdate_interval);
52
+ });
53
+ }
54
+ }
55
+
56
+ //canvas.drawGrid(0, 0, width+(2*xpadding), height, 1, 6, "#ececec");
57
+ }
58
+
59
+ function announce(evt){
60
+ if(evt.widget_key == opts.widget_key){
61
+ if((evt._class == "widget_response") && (evt.cmd == "values_at")){
62
+ running_request = false;
63
+ updateSeriesData(evt.tick, evt.values);
64
+ updateChart();
65
+ }
66
+ }
67
+ }
68
+
69
+ function requestValues(_tick, times){
70
+ if(times.length > 0){
71
+ if(!running_request){
72
+ running_request = (new Date).getTime();
73
+ requestValuesAsync(_tick, times);
74
+ }
75
+ }
76
+ }
77
+
78
+ function requestValuesAsync(_tick, times){
79
+ FnordMetric.publish({
80
+ "_class": "widget_request",
81
+ "_channel": opts.channel,
82
+ "cmd": "values_at",
83
+ "tick": _tick,
84
+ "ticks": times,
85
+ "widget_key": opts.widget_key
86
+ })
87
+ }
88
+
89
+
90
+ function changeTick(){
91
+ opts.tick = parseInt($(this).attr('data-tick'));
92
+ opts.start_timestamp = null;
93
+ opts.end_timestamp = null;
94
+ updateRange();
95
+ redrawDatepicker();
96
+ updateChart();
97
+ }
98
+
99
+ function updateSeriesData(_tick, values){
100
+ for(_series in values){
101
+ for(_t in values[_series]){
102
+ series_values[_series][_tick+"+"+_t] = values[_series][_t];
103
+ }
104
+ }
105
+ }
106
+
107
+ function updateChart(){
108
+ var _ticks = [];
109
+ var _miss = [];
110
+ var _max = [];
111
+ var _rndr = [];
112
+
113
+ for(sind in opts.series){
114
+ var _last = opts.start_timestamp + opts.tick;
115
+ var _delta = (opts.end_timestamp - _last) / xticks;
116
+ var _sdata = [];
117
+
118
+ for(var n=0; n < xticks; n++){
119
+ var _t = (parseInt(_last / opts.tick) * opts.tick);
120
+ var _v = series_values[opts.series[sind]][opts.tick+"+"+_t];
121
+ if((_v === undefined) && (_miss.indexOf(_t) == -1)){ _miss.push(_t); }
122
+ _sdata.push(_v || 0);
123
+ _last += _delta;
124
+ }
125
+
126
+ _max.push(Math.max.apply(Math, _sdata));
127
+ _rndr.push([opts.series[sind], _sdata]);
128
+ }
129
+
130
+ _max = Math.max.apply(Math, _max)*1.1;
131
+ if(_max == 0){ _max = 1; }
132
+
133
+ for(rind in _rndr){
134
+ drawSeries(_rndr[rind][0], _rndr[rind][1], _max);
135
+ }
136
+
137
+ if(_miss.length > 0)
138
+ requestValues(opts.tick, _miss);
139
+
140
+ redrawDatepicker();
141
+ }
142
+
143
+ function refreshChart(){
144
+ for(_series in series_values){
145
+ series_values[_series] = {};
146
+ }
147
+
148
+ updateRange();
149
+ updateChart();
150
+ }
151
+
152
+ function drawSeries(series, series_data, _max){
153
+
154
+ //var path_string = "M0,"+height;
155
+ var path_string = "";
156
+ var _color = '0066CC';
157
+
158
+ if (series_paths[series]){
159
+ for(ind in series_paths[series]){
160
+ series_paths[series][ind].remove();
161
+ }
162
+ }
163
+
164
+ series_paths[series] = [];
165
+
166
+ if(!_max){ _max = Math.max.apply(Math, series_data)*1.1; }
167
+ if(_max < 1){ _max = 1; }
168
+
169
+ $(series_data).each(function(i,v){
170
+
171
+ var p_x = xpadding+(i*xtick);
172
+ var p_y = (height-((v/_max)*height));
173
+
174
+ path_string += ( ( i == 0 ? "M" : "L" ) + p_x + ',' + p_y );
175
+
176
+ // if(i%label_mod==0){
177
+ // canvas.text(p_x, height+10, labels[i]).attr({
178
+ // font: '10px Helvetica, Arial',
179
+ // fill: "#777"
180
+ // });
181
+ // }
182
+
183
+ if(opts.draw_points){
184
+ series_paths[series].push(canvas
185
+ .append("svg:circle")
186
+ .attr("cx", p_x)
187
+ .attr("cy", p_y)
188
+ .attr("stroke-width", "1px")
189
+ .attr("stroke", "#fff")
190
+ .attr("fill", _color)
191
+ .attr("fill-opacity", 1)
192
+ .attr("r", 4)
193
+ );
194
+ }
195
+
196
+ // var htrgt = canvas.rect(p_x - 20, p_y - 20, 40, 40).attr({
197
+ // stroke: "none",
198
+ // fill: "#fff",
199
+ // opacity: 0
200
+ // }).toFront();
201
+
202
+ //series_paths[series].push(htrgt);
203
+
204
+ // (function(htrgt, series_paths){
205
+
206
+ // var t_y = p_y + 9;
207
+ // var ttt = canvas.text(p_x, t_y+10, v).attr({
208
+ // font: '12px Helvetica, Arial',
209
+ // fill: "#fff",
210
+ // opacity: 0
211
+ // });
212
+
213
+ // var tttb = ttt.getBBox();
214
+ // var ttw = tttb.width+20;
215
+ // var tt = canvas.rect(p_x-(ttw/2), t_y, ttw, 22, 5).attr({
216
+ // stroke: "none",
217
+ // fill: "#000",
218
+ // opacity: 0
219
+ // }).toBack();
220
+
221
+ // series_paths[series].push(tt);
222
+ // series_paths[series].push(ttt);
223
+
224
+ // $(htrgt[0]).hover(function(){
225
+ // tt.animate({ opacity: 0.8 }, 300);
226
+ // ttt.animate({ opacity: 0.8 }, 300);
227
+ // }, function(){
228
+ // tt.animate({ opacity: 0 }, 300);
229
+ // ttt.animate({ opacity: 0 }, 300);
230
+ // });
231
+
232
+ // })(htrgt, series_paths);
233
+
234
+ });
235
+
236
+ if(opts.draw_path){
237
+ series_paths[series].push(canvas.append("svg:path")
238
+ .attr("fill", "none")
239
+ .attr("stroke", "steelblue")
240
+ .attr("stroke-width", 3)
241
+ .attr("d", path_string)
242
+ );
243
+ }
244
+
245
+ if(opts.draw_area){
246
+ // path_string += "L"+(width+xpadding)+","+height+" L"+xpadding+","+height+" Z";
247
+
248
+ // series_paths[series].push(
249
+ // canvas.path(path_string).attr({
250
+ // stroke: "none",
251
+ // fill: _color,
252
+ // opacity: 0.1
253
+ // }).toBack()
254
+ // );
255
+ }
256
+ }
257
+
258
+ function drawLayout(opts){
259
+ $(opts.elem).append( $('<div></div>').attr('class', 'headbar').append(
260
+ $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('refresh')).click(
261
+ function(){ refreshChart(); }
262
+ )
263
+ ).append(
264
+ $('<div></div>').attr('class', 'button mr').append($('<span></span>').html('&rarr;')).click(
265
+ function(){ moveRange(1); }
266
+ )
267
+ ).append(
268
+ $('<div></div>').attr('class', 'datepicker')
269
+ ).append(
270
+ $('<div></div>').attr('class', 'button ml').append($('<span></span>').html('&larr;')).click(
271
+ function(){ moveRange(-1); }
272
+ )
273
+ ).append(
274
+ $('<h2></h2>').html(opts.title)
275
+ ) ).append(
276
+ $('<div></div>').attr('id', 'container-'+widget_uid).css({
277
+ height: opts.height + 6,
278
+ marginBottom: 20,
279
+ overflow: 'hidden'
280
+ })
281
+ );
282
+
283
+ if(opts.ticks){
284
+ $('.headbar', opts.elem).append('<div class="tick_btns btn_group"></div>');
285
+ for(__tick in opts.ticks){
286
+ var _tick = opts.ticks[__tick];
287
+ $('.tick_btns', opts.elem).append(
288
+ $('<div></div>').attr('class', 'button tick').append($('<span></span>')
289
+ .html(FnordMetric.util.formatTimeRange(_tick)))
290
+ .attr('data-tick', _tick)
291
+ .click(changeTick)
292
+ );
293
+ }
294
+ }
295
+ }
296
+
297
+ function redrawDatepicker(){
298
+ $('.datepicker', opts.elem).html(
299
+ Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(opts.start_timestamp)*1000) +
300
+ '&nbsp;&dash;&nbsp;' +
301
+ Highcharts.dateFormat('%d.%m.%y %H:%M', parseInt(opts.end_timestamp)*1000)
302
+ );
303
+ }
304
+
305
+ function updateRange(force){
306
+ if(!opts.tick){
307
+ opts.tick = opts.ticks[0];
308
+ }
309
+
310
+ if(!opts.start_timestamp || !opts.end_timestamp || !!force){
311
+ opts.end_timestamp = parseInt(new Date().getTime() / 1000);
312
+ opts.start_timestamp = opts.end_timestamp - (opts.tick * xticks);
313
+ }
314
+ }
315
+
316
+ function moveRange(direction){
317
+ v = opts.tick*direction*8;
318
+
319
+ if(((opts.end_timestamp + v)*1000) < new Date().getTime()){
320
+ opts.start_timestamp += v;
321
+ opts.end_timestamp += v;
322
+ }
323
+
324
+ updateChart();
325
+ }
326
+
327
+
328
+ return {
329
+ render: render,
330
+ announce: announce
331
+ }
332
+
333
+ };