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,2676 @@
1
+ Rickshaw = {
2
+
3
+ namespace: function(namespace, obj) {
4
+
5
+ var parts = namespace.split('.');
6
+
7
+ // for rudimentary compatibility w/ node
8
+ var root = typeof global != 'undefined' ? global : window;
9
+
10
+ var parent = root.Rickshaw;
11
+
12
+ for(var i = 1, length = parts.length; i < length; i++) {
13
+ currentPart = parts[i];
14
+ parent[currentPart] = parent[currentPart] || {};
15
+ parent = parent[currentPart];
16
+ }
17
+ return parent;
18
+ },
19
+
20
+ keys: function(obj) {
21
+ var keys = [];
22
+ for (var key in obj) keys.push(key);
23
+ return keys;
24
+ },
25
+
26
+ extend: function(destination, source) {
27
+
28
+ for (var property in source) {
29
+ destination[property] = source[property];
30
+ }
31
+ return destination;
32
+ }
33
+ };
34
+
35
+ /* Adapted from https://github.com/Jakobo/PTClass */
36
+
37
+ /*
38
+ Copyright (c) 2005-2010 Sam Stephenson
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining a copy
41
+ of this software and associated documentation files (the "Software"), to deal
42
+ in the Software without restriction, including without limitation the rights
43
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
44
+ copies of the Software, and to permit persons to whom the Software is
45
+ furnished to do so, subject to the following conditions:
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
53
+ SOFTWARE.
54
+ */
55
+ /* Based on Alex Arnell's inheritance implementation. */
56
+ /** section: Language
57
+ * class Class
58
+ *
59
+ * Manages Prototype's class-based OOP system.
60
+ *
61
+ * Refer to Prototype's web site for a [tutorial on classes and
62
+ * inheritance](http://prototypejs.org/learn/class-inheritance).
63
+ **/
64
+ (function(globalContext) {
65
+ /* ------------------------------------ */
66
+ /* Import from object.js */
67
+ /* ------------------------------------ */
68
+ var _toString = Object.prototype.toString,
69
+ NULL_TYPE = 'Null',
70
+ UNDEFINED_TYPE = 'Undefined',
71
+ BOOLEAN_TYPE = 'Boolean',
72
+ NUMBER_TYPE = 'Number',
73
+ STRING_TYPE = 'String',
74
+ OBJECT_TYPE = 'Object',
75
+ FUNCTION_CLASS = '[object Function]';
76
+ function isFunction(object) {
77
+ return _toString.call(object) === FUNCTION_CLASS;
78
+ }
79
+ function extend(destination, source) {
80
+ for (var property in source) if (source.hasOwnProperty(property)) // modify protect primitive slaughter
81
+ destination[property] = source[property];
82
+ return destination;
83
+ }
84
+ function keys(object) {
85
+ if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
86
+ var results = [];
87
+ for (var property in object) {
88
+ if (object.hasOwnProperty(property)) {
89
+ results.push(property);
90
+ }
91
+ }
92
+ return results;
93
+ }
94
+ function Type(o) {
95
+ switch(o) {
96
+ case null: return NULL_TYPE;
97
+ case (void 0): return UNDEFINED_TYPE;
98
+ }
99
+ var type = typeof o;
100
+ switch(type) {
101
+ case 'boolean': return BOOLEAN_TYPE;
102
+ case 'number': return NUMBER_TYPE;
103
+ case 'string': return STRING_TYPE;
104
+ }
105
+ return OBJECT_TYPE;
106
+ }
107
+ function isUndefined(object) {
108
+ return typeof object === "undefined";
109
+ }
110
+ /* ------------------------------------ */
111
+ /* Import from Function.js */
112
+ /* ------------------------------------ */
113
+ var slice = Array.prototype.slice;
114
+ function argumentNames(fn) {
115
+ var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
116
+ .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
117
+ .replace(/\s+/g, '').split(',');
118
+ return names.length == 1 && !names[0] ? [] : names;
119
+ }
120
+ function wrap(fn, wrapper) {
121
+ var __method = fn;
122
+ return function() {
123
+ var a = update([bind(__method, this)], arguments);
124
+ return wrapper.apply(this, a);
125
+ }
126
+ }
127
+ function update(array, args) {
128
+ var arrayLength = array.length, length = args.length;
129
+ while (length--) array[arrayLength + length] = args[length];
130
+ return array;
131
+ }
132
+ function merge(array, args) {
133
+ array = slice.call(array, 0);
134
+ return update(array, args);
135
+ }
136
+ function bind(fn, context) {
137
+ if (arguments.length < 2 && isUndefined(arguments[0])) return this;
138
+ var __method = fn, args = slice.call(arguments, 2);
139
+ return function() {
140
+ var a = merge(args, arguments);
141
+ return __method.apply(context, a);
142
+ }
143
+ }
144
+
145
+ /* ------------------------------------ */
146
+ /* Import from Prototype.js */
147
+ /* ------------------------------------ */
148
+ var emptyFunction = function(){};
149
+
150
+ var Class = (function() {
151
+
152
+ // Some versions of JScript fail to enumerate over properties, names of which
153
+ // correspond to non-enumerable properties in the prototype chain
154
+ var IS_DONTENUM_BUGGY = (function(){
155
+ for (var p in { toString: 1 }) {
156
+ // check actual property name, so that it works with augmented Object.prototype
157
+ if (p === 'toString') return false;
158
+ }
159
+ return true;
160
+ })();
161
+
162
+ function subclass() {};
163
+ function create() {
164
+ var parent = null, properties = [].slice.apply(arguments);
165
+ if (isFunction(properties[0]))
166
+ parent = properties.shift();
167
+
168
+ function klass() {
169
+ this.initialize.apply(this, arguments);
170
+ }
171
+
172
+ extend(klass, Class.Methods);
173
+ klass.superclass = parent;
174
+ klass.subclasses = [];
175
+
176
+ if (parent) {
177
+ subclass.prototype = parent.prototype;
178
+ klass.prototype = new subclass;
179
+ try { parent.subclasses.push(klass) } catch(e) {}
180
+ }
181
+
182
+ for (var i = 0, length = properties.length; i < length; i++)
183
+ klass.addMethods(properties[i]);
184
+
185
+ if (!klass.prototype.initialize)
186
+ klass.prototype.initialize = emptyFunction;
187
+
188
+ klass.prototype.constructor = klass;
189
+ return klass;
190
+ }
191
+
192
+ function addMethods(source) {
193
+ var ancestor = this.superclass && this.superclass.prototype,
194
+ properties = keys(source);
195
+
196
+ // IE6 doesn't enumerate `toString` and `valueOf` (among other built-in `Object.prototype`) properties,
197
+ // Force copy if they're not Object.prototype ones.
198
+ // Do not copy other Object.prototype.* for performance reasons
199
+ if (IS_DONTENUM_BUGGY) {
200
+ if (source.toString != Object.prototype.toString)
201
+ properties.push("toString");
202
+ if (source.valueOf != Object.prototype.valueOf)
203
+ properties.push("valueOf");
204
+ }
205
+
206
+ for (var i = 0, length = properties.length; i < length; i++) {
207
+ var property = properties[i], value = source[property];
208
+ if (ancestor && isFunction(value) &&
209
+ argumentNames(value)[0] == "$super") {
210
+ var method = value;
211
+ value = wrap((function(m) {
212
+ return function() { return ancestor[m].apply(this, arguments); };
213
+ })(property), method);
214
+
215
+ value.valueOf = bind(method.valueOf, method);
216
+ value.toString = bind(method.toString, method);
217
+ }
218
+ this.prototype[property] = value;
219
+ }
220
+
221
+ return this;
222
+ }
223
+
224
+ return {
225
+ create: create,
226
+ Methods: {
227
+ addMethods: addMethods
228
+ }
229
+ };
230
+ })();
231
+
232
+ if (globalContext.exports) {
233
+ globalContext.exports.Class = Class;
234
+ }
235
+ else {
236
+ globalContext.Class = Class;
237
+ }
238
+ })(Rickshaw);
239
+ Rickshaw.namespace('Rickshaw.Compat.ClassList');
240
+
241
+ Rickshaw.Compat.ClassList = function() {
242
+
243
+ /* adapted from http://purl.eligrey.com/github/classList.js/blob/master/classList.js */
244
+
245
+ if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {
246
+
247
+ (function (view) {
248
+
249
+ "use strict";
250
+
251
+ var
252
+ classListProp = "classList"
253
+ , protoProp = "prototype"
254
+ , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
255
+ , objCtr = Object
256
+ , strTrim = String[protoProp].trim || function () {
257
+ return this.replace(/^\s+|\s+$/g, "");
258
+ }
259
+ , arrIndexOf = Array[protoProp].indexOf || function (item) {
260
+ var
261
+ i = 0
262
+ , len = this.length
263
+ ;
264
+ for (; i < len; i++) {
265
+ if (i in this && this[i] === item) {
266
+ return i;
267
+ }
268
+ }
269
+ return -1;
270
+ }
271
+ // Vendors: please allow content code to instantiate DOMExceptions
272
+ , DOMEx = function (type, message) {
273
+ this.name = type;
274
+ this.code = DOMException[type];
275
+ this.message = message;
276
+ }
277
+ , checkTokenAndGetIndex = function (classList, token) {
278
+ if (token === "") {
279
+ throw new DOMEx(
280
+ "SYNTAX_ERR"
281
+ , "An invalid or illegal string was specified"
282
+ );
283
+ }
284
+ if (/\s/.test(token)) {
285
+ throw new DOMEx(
286
+ "INVALID_CHARACTER_ERR"
287
+ , "String contains an invalid character"
288
+ );
289
+ }
290
+ return arrIndexOf.call(classList, token);
291
+ }
292
+ , ClassList = function (elem) {
293
+ var
294
+ trimmedClasses = strTrim.call(elem.className)
295
+ , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
296
+ , i = 0
297
+ , len = classes.length
298
+ ;
299
+ for (; i < len; i++) {
300
+ this.push(classes[i]);
301
+ }
302
+ this._updateClassName = function () {
303
+ elem.className = this.toString();
304
+ };
305
+ }
306
+ , classListProto = ClassList[protoProp] = []
307
+ , classListGetter = function () {
308
+ return new ClassList(this);
309
+ }
310
+ ;
311
+ // Most DOMException implementations don't allow calling DOMException's toString()
312
+ // on non-DOMExceptions. Error's toString() is sufficient here.
313
+ DOMEx[protoProp] = Error[protoProp];
314
+ classListProto.item = function (i) {
315
+ return this[i] || null;
316
+ };
317
+ classListProto.contains = function (token) {
318
+ token += "";
319
+ return checkTokenAndGetIndex(this, token) !== -1;
320
+ };
321
+ classListProto.add = function (token) {
322
+ token += "";
323
+ if (checkTokenAndGetIndex(this, token) === -1) {
324
+ this.push(token);
325
+ this._updateClassName();
326
+ }
327
+ };
328
+ classListProto.remove = function (token) {
329
+ token += "";
330
+ var index = checkTokenAndGetIndex(this, token);
331
+ if (index !== -1) {
332
+ this.splice(index, 1);
333
+ this._updateClassName();
334
+ }
335
+ };
336
+ classListProto.toggle = function (token) {
337
+ token += "";
338
+ if (checkTokenAndGetIndex(this, token) === -1) {
339
+ this.add(token);
340
+ } else {
341
+ this.remove(token);
342
+ }
343
+ };
344
+ classListProto.toString = function () {
345
+ return this.join(" ");
346
+ };
347
+
348
+ if (objCtr.defineProperty) {
349
+ var classListPropDesc = {
350
+ get: classListGetter
351
+ , enumerable: true
352
+ , configurable: true
353
+ };
354
+ try {
355
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
356
+ } catch (ex) { // IE 8 doesn't support enumerable:true
357
+ if (ex.number === -0x7FF5EC54) {
358
+ classListPropDesc.enumerable = false;
359
+ objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
360
+ }
361
+ }
362
+ } else if (objCtr[protoProp].__defineGetter__) {
363
+ elemCtrProto.__defineGetter__(classListProp, classListGetter);
364
+ }
365
+
366
+ }(self));
367
+
368
+ }
369
+ };
370
+
371
+ if ( (typeof RICKSHAW_NO_COMPAT !== "undefined" && !RICKSHAW_NO_COMPAT) || typeof RICKSHAW_NO_COMPAT === "undefined") {
372
+ new Rickshaw.Compat.ClassList();
373
+ }
374
+ Rickshaw.namespace('Rickshaw.Graph');
375
+
376
+ Rickshaw.Graph = function(args) {
377
+
378
+ this.element = args.element;
379
+ this.series = args.series;
380
+
381
+ this.defaults = {
382
+ interpolation: 'cardinal',
383
+ offset: 'zero',
384
+ min: undefined,
385
+ max: undefined,
386
+ };
387
+
388
+ Rickshaw.keys(this.defaults).forEach( function(k) {
389
+ this[k] = args[k] || this.defaults[k];
390
+ }, this );
391
+
392
+ this.window = {};
393
+
394
+ this.updateCallbacks = [];
395
+
396
+ var self = this;
397
+
398
+ this.initialize = function(args) {
399
+
400
+ this.validateSeries(args.series);
401
+
402
+ this.series.active = function() { return self.series.filter( function(s) { return !s.disabled } ) };
403
+
404
+ this.setSize({ width: args.width, height: args.height });
405
+
406
+ this.element.classList.add('rickshaw_graph');
407
+ this.vis = d3.select(this.element)
408
+ .append("svg:svg")
409
+ .attr('width', this.width)
410
+ .attr('height', this.height);
411
+
412
+ var renderers = [
413
+ Rickshaw.Graph.Renderer.Stack,
414
+ Rickshaw.Graph.Renderer.Line,
415
+ Rickshaw.Graph.Renderer.Bar,
416
+ Rickshaw.Graph.Renderer.Area,
417
+ Rickshaw.Graph.Renderer.ScatterPlot
418
+ ];
419
+
420
+ renderers.forEach( function(r) {
421
+ if (!r) return;
422
+ self.registerRenderer(new r( { graph: self } ));
423
+ } );
424
+
425
+ this.setRenderer(args.renderer || 'stack', args);
426
+ this.discoverRange();
427
+ };
428
+
429
+ this.validateSeries = function(series) {
430
+
431
+ if (!(series instanceof Array) && !(series instanceof Rickshaw.Series)) {
432
+ var seriesSignature = Object.prototype.toString.apply(series);
433
+ throw "series is not an array: " + seriesSignature;
434
+ }
435
+
436
+ var pointsCount;
437
+
438
+ series.forEach( function(s) {
439
+
440
+ if (!(s instanceof Object)) {
441
+ throw "series element is not an object: " + s;
442
+ }
443
+ if (!(s.data)) {
444
+ throw "series has no data: " + JSON.stringify(s);
445
+ }
446
+ if (!(s.data instanceof Array)) {
447
+ throw "series data is not an array: " + JSON.stringify(s.data);
448
+ }
449
+
450
+ pointsCount = pointsCount || s.data.length;
451
+
452
+ if (pointsCount && s.data.length != pointsCount) {
453
+ throw "series cannot have differing numbers of points: " +
454
+ pointsCount + " vs " + s.data.length + "; see Rickshaw.Series.zeroFill()";
455
+ }
456
+
457
+ var dataTypeX = typeof s.data[0].x;
458
+ var dataTypeY = typeof s.data[0].y;
459
+
460
+ if (dataTypeX != 'number' || dataTypeY != 'number') {
461
+ throw "x and y properties of points should be numbers instead of " +
462
+ dataTypeX + " and " + dataTypeY;
463
+ }
464
+ } );
465
+ };
466
+
467
+ this.dataDomain = function() {
468
+
469
+ // take from the first series
470
+ var data = this.series[0].data;
471
+
472
+ return [ data[0].x, data.slice(-1).shift().x ];
473
+
474
+ };
475
+
476
+ this.discoverRange = function() {
477
+
478
+ var domain = this.renderer.domain();
479
+
480
+ this.x = d3.scale.linear().domain(domain.x).range([0, this.width]);
481
+
482
+ this.y = d3.scale.linear().domain(domain.y).range([this.height, 0]);
483
+ this.y.magnitude = d3.scale.linear().domain(domain.y).range([0, this.height]);
484
+
485
+ };
486
+
487
+ this.render = function() {
488
+
489
+ var stackedData = this.stackData();
490
+ this.discoverRange();
491
+
492
+ this.renderer.render();
493
+
494
+ this.updateCallbacks.forEach( function(callback) {
495
+ callback();
496
+ } );
497
+ };
498
+
499
+ this.update = this.render;
500
+
501
+ this.stackData = function() {
502
+
503
+ var data = this.series.active()
504
+ .map( function(d) { return d.data } )
505
+ .map( function(d) { return d.filter( function(d) { return this._slice(d) }, this ) }, this);
506
+
507
+ this.stackData.hooks.data.forEach( function(entry) {
508
+ data = entry.f.apply(self, [data]);
509
+ } );
510
+
511
+ var layout = d3.layout.stack();
512
+ layout.offset( self.offset );
513
+
514
+ var stackedData = layout(data);
515
+
516
+ this.stackData.hooks.after.forEach( function(entry) {
517
+ stackedData = entry.f.apply(self, [data]);
518
+ } );
519
+
520
+ var i = 0;
521
+ this.series.forEach( function(series) {
522
+ if (series.disabled) return;
523
+ series.stack = stackedData[i++];
524
+ } );
525
+
526
+ this.stackedData = stackedData;
527
+ return stackedData;
528
+ };
529
+
530
+ this.stackData.hooks = { data: [], after: [] };
531
+
532
+ this._slice = function(d) {
533
+
534
+ if (this.window.xMin || this.window.xMax) {
535
+
536
+ var isInRange = true;
537
+
538
+ if (this.window.xMin && d.x < this.window.xMin) isInRange = false;
539
+ if (this.window.xMax && d.x > this.window.xMax) isInRange = false;
540
+
541
+ return isInRange;
542
+ }
543
+
544
+ return true;
545
+ };
546
+
547
+ this.onUpdate = function(callback) {
548
+ this.updateCallbacks.push(callback);
549
+ };
550
+
551
+ this.registerRenderer = function(renderer) {
552
+ this._renderers = this._renderers || {};
553
+ this._renderers[renderer.name] = renderer;
554
+ };
555
+
556
+ this.configure = function(args) {
557
+
558
+ if (args.width || args.height) {
559
+ this.setSize(args);
560
+ }
561
+
562
+ Rickshaw.keys(this.defaults).forEach( function(k) {
563
+ this[k] = args[k] || this.defaults[k];
564
+ }, this );
565
+
566
+ this.setRenderer(args.renderer || graph.renderer.name, args);
567
+ };
568
+
569
+ this.setRenderer = function(name, args) {
570
+
571
+ if (!this._renderers[name]) {
572
+ throw "couldn't find renderer " + name;
573
+ }
574
+ this.renderer = this._renderers[name];
575
+
576
+ if (typeof args == 'object') {
577
+ this.renderer.configure(args);
578
+ }
579
+ };
580
+
581
+ this.setSize = function(args) {
582
+
583
+ args = args || {};
584
+
585
+ if (typeof window !== undefined) {
586
+ var style = window.getComputedStyle(this.element, null);
587
+ var elementWidth = parseInt(style.getPropertyValue('width'));
588
+ var elementHeight = parseInt(style.getPropertyValue('height'));
589
+ }
590
+
591
+ this.width = args.width || elementWidth || 400;
592
+ this.height = args.height || elementHeight || 250;
593
+
594
+ this.vis && this.vis
595
+ .attr('width', this.width)
596
+ .attr('height', this.height);
597
+ }
598
+
599
+ this.initialize(args);
600
+ };
601
+ Rickshaw.namespace('Rickshaw.Fixtures.Color');
602
+
603
+ Rickshaw.Fixtures.Color = function() {
604
+
605
+ this.schemes = {};
606
+
607
+ this.schemes.spectrum14 = [
608
+ '#ecb796',
609
+ '#dc8f70',
610
+ '#b2a470',
611
+ '#92875a',
612
+ '#716c49',
613
+ '#d2ed82',
614
+ '#bbe468',
615
+ '#a1d05d',
616
+ '#e7cbe6',
617
+ '#d8aad6',
618
+ '#a888c2',
619
+ '#9dc2d3',
620
+ '#649eb9',
621
+ '#387aa3'
622
+ ].reverse();
623
+
624
+ this.schemes.spectrum2000 = [
625
+ '#57306f',
626
+ '#514c76',
627
+ '#646583',
628
+ '#738394',
629
+ '#6b9c7d',
630
+ '#84b665',
631
+ '#a7ca50',
632
+ '#bfe746',
633
+ '#e2f528',
634
+ '#fff726',
635
+ '#ecdd00',
636
+ '#d4b11d',
637
+ '#de8800',
638
+ '#de4800',
639
+ '#c91515',
640
+ '#9a0000',
641
+ '#7b0429',
642
+ '#580839',
643
+ '#31082b'
644
+ ];
645
+
646
+ this.schemes.spectrum2001 = [
647
+ '#2f243f',
648
+ '#3c2c55',
649
+ '#4a3768',
650
+ '#565270',
651
+ '#6b6b7c',
652
+ '#72957f',
653
+ '#86ad6e',
654
+ '#a1bc5e',
655
+ '#b8d954',
656
+ '#d3e04e',
657
+ '#ccad2a',
658
+ '#cc8412',
659
+ '#c1521d',
660
+ '#ad3821',
661
+ '#8a1010',
662
+ '#681717',
663
+ '#531e1e',
664
+ '#3d1818',
665
+ '#320a1b'
666
+ ];
667
+
668
+ this.schemes.classic9 = [
669
+ '#423d4f',
670
+ '#4a6860',
671
+ '#848f39',
672
+ '#a2b73c',
673
+ '#ddcb53',
674
+ '#c5a32f',
675
+ '#7d5836',
676
+ '#963b20',
677
+ '#7c2626',
678
+ '#491d37',
679
+ '#2f254a'
680
+ ].reverse();
681
+
682
+ this.schemes.httpStatus = {
683
+ 503: '#ea5029',
684
+ 502: '#d23f14',
685
+ 500: '#bf3613',
686
+ 410: '#efacea',
687
+ 409: '#e291dc',
688
+ 403: '#f457e8',
689
+ 408: '#e121d2',
690
+ 401: '#b92dae',
691
+ 405: '#f47ceb',
692
+ 404: '#a82a9f',
693
+ 400: '#b263c6',
694
+ 301: '#6fa024',
695
+ 302: '#87c32b',
696
+ 307: '#a0d84c',
697
+ 304: '#28b55c',
698
+ 200: '#1a4f74',
699
+ 206: '#27839f',
700
+ 201: '#52adc9',
701
+ 202: '#7c979f',
702
+ 203: '#a5b8bd',
703
+ 204: '#c1cdd1'
704
+ };
705
+
706
+ this.schemes.colorwheel = [
707
+ '#b5b6a9',
708
+ '#858772',
709
+ '#785f43',
710
+ '#96557e',
711
+ '#4682b4',
712
+ '#65b9ac',
713
+ '#73c03a',
714
+ '#cb513a'
715
+ ].reverse();
716
+
717
+ this.schemes.cool = [
718
+ '#5e9d2f',
719
+ '#73c03a',
720
+ '#4682b4',
721
+ '#7bc3b8',
722
+ '#a9884e',
723
+ '#c1b266',
724
+ '#a47493',
725
+ '#c09fb5'
726
+ ];
727
+
728
+ this.schemes.munin = [
729
+ '#00cc00',
730
+ '#0066b3',
731
+ '#ff8000',
732
+ '#ffcc00',
733
+ '#330099',
734
+ '#990099',
735
+ '#ccff00',
736
+ '#ff0000',
737
+ '#808080',
738
+ '#008f00',
739
+ '#00487d',
740
+ '#b35a00',
741
+ '#b38f00',
742
+ '#6b006b',
743
+ '#8fb300',
744
+ '#b30000',
745
+ '#bebebe',
746
+ '#80ff80',
747
+ '#80c9ff',
748
+ '#ffc080',
749
+ '#ffe680',
750
+ '#aa80ff',
751
+ '#ee00cc',
752
+ '#ff8080',
753
+ '#666600',
754
+ '#ffbfff',
755
+ '#00ffcc',
756
+ '#cc6699',
757
+ '#999900'
758
+ ];
759
+ };
760
+ Rickshaw.namespace('Rickshaw.Fixtures.RandomData');
761
+
762
+ Rickshaw.Fixtures.RandomData = function(timeInterval) {
763
+
764
+ var addData;
765
+ timeInterval = timeInterval || 1;
766
+
767
+ var lastRandomValue = 200;
768
+
769
+ var timeBase = Math.floor(new Date().getTime() / 1000);
770
+
771
+ this.addData = function(data) {
772
+
773
+ var randomValue = Math.random() * 100 + 15 + lastRandomValue;
774
+ var index = data[0].length;
775
+
776
+ var counter = 1;
777
+
778
+ data.forEach( function(series) {
779
+ var randomVariance = Math.random() * 20;
780
+ var v = randomValue / 25 + counter++
781
+ + (Math.cos((index * counter * 11) / 960) + 2) * 15
782
+ + (Math.cos(index / 7) + 2) * 7
783
+ + (Math.cos(index / 17) + 2) * 1;
784
+
785
+ series.push( { x: (index * timeInterval) + timeBase, y: v + randomVariance } );
786
+ } );
787
+
788
+ lastRandomValue = randomValue * .85;
789
+ }
790
+ };
791
+
792
+ Rickshaw.namespace('Rickshaw.Fixtures.Time');
793
+
794
+ Rickshaw.Fixtures.Time = function() {
795
+
796
+ var tzOffset = new Date().getTimezoneOffset() * 60;
797
+
798
+ var self = this;
799
+
800
+ this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
801
+
802
+ this.units = [
803
+ {
804
+ name: 'decade',
805
+ seconds: 86400 * 365.25 * 10,
806
+ formatter: function(d) { return (parseInt(d.getUTCFullYear() / 10) * 10) }
807
+ }, {
808
+ name: 'year',
809
+ seconds: 86400 * 365.25,
810
+ formatter: function(d) { return d.getUTCFullYear() }
811
+ }, {
812
+ name: 'month',
813
+ seconds: 86400 * 30.5,
814
+ formatter: function(d) { return self.months[d.getUTCMonth()] }
815
+ }, {
816
+ name: 'week',
817
+ seconds: 86400 * 7,
818
+ formatter: function(d) { return self.formatDate(d) }
819
+ }, {
820
+ name: 'day',
821
+ seconds: 86400,
822
+ formatter: function(d) { return d.getUTCDate() }
823
+ }, {
824
+ name: '6 hour',
825
+ seconds: 3600 * 6,
826
+ formatter: function(d) { return self.formatTime(d) }
827
+ }, {
828
+ name: 'hour',
829
+ seconds: 3600,
830
+ formatter: function(d) { return self.formatTime(d) }
831
+ }, {
832
+ name: '15 minute',
833
+ seconds: 60 * 15,
834
+ formatter: function(d) { return self.formatTime(d) }
835
+ }, {
836
+ name: 'minute',
837
+ seconds: 60,
838
+ formatter: function(d) { return d.getUTCMinutes() }
839
+ }, {
840
+ name: '15 second',
841
+ seconds: 15,
842
+ formatter: function(d) { return d.getUTCSeconds() + 's' }
843
+ }, {
844
+ name: 'second',
845
+ seconds: 1,
846
+ formatter: function(d) { return d.getUTCSeconds() + 's' }
847
+ }
848
+ ];
849
+
850
+ this.unit = function(unitName) {
851
+ return this.units.filter( function(unit) { return unitName == unit.name } ).shift();
852
+ };
853
+
854
+ this.formatDate = function(d) {
855
+ var exp = FnordMetric.util.dateFormat(d.getTime()).match(/^([0-9]+\.[0-9]+)/);
856
+
857
+ if (exp)
858
+ return exp[1];
859
+
860
+ return 0;
861
+ };
862
+
863
+ this.formatTime = function(d) {
864
+ return d.toLocaleString().match(/(\d+:\d+):/)[1];
865
+ };
866
+
867
+ this.ceil = function(time, unit) {
868
+
869
+ if (unit.name == 'month') {
870
+ var nearFuture = new Date((time + unit.seconds - 1) * 1000);
871
+ return new Date(nearFuture.getUTCFullYear(), nearFuture.getUTCMonth() + 1, 1, 0, 0, 0, 0).getTime() / 1000;
872
+ }
873
+
874
+ if (unit.name == 'year') {
875
+ var nearFuture = new Date((time + unit.seconds - 1) * 1000);
876
+ return new Date(nearFuture.getUTCFullYear(), 1, 1, 0, 0, 0, 0).getTime() / 1000;
877
+ }
878
+
879
+ return Math.ceil(time / unit.seconds) * unit.seconds;
880
+ };
881
+ };
882
+ Rickshaw.namespace('Rickshaw.Fixtures.Number');
883
+
884
+ Rickshaw.Fixtures.Number.formatKMBT = function(y) {
885
+ if (y >= 1000000000000) { return y / 1000000000000 + "T" }
886
+ else if (y >= 1000000000) { return y / 1000000000 + "B" }
887
+ else if (y >= 1000000) { return y / 1000000 + "M" }
888
+ else if (y >= 1000) { return y / 1000 + "K" }
889
+ else if (y < 1 && y > 0) { return y.toFixed(2) }
890
+ else if (y == 0) { return '' }
891
+ else { return y }
892
+ };
893
+
894
+ Rickshaw.Fixtures.Number.formatBase1024KMGTP = function(y) {
895
+ if (y >= 1125899906842624) { return y / 1125899906842624 + "P" }
896
+ else if (y >= 1099511627776){ return y / 1099511627776 + "T" }
897
+ else if (y >= 1073741824) { return y / 1073741824 + "G" }
898
+ else if (y >= 1048576) { return y / 1048576 + "M" }
899
+ else if (y >= 1024) { return y / 1024 + "K" }
900
+ else if (y < 1 && y > 0) { return y.toFixed(2) }
901
+ else if (y == 0) { return '' }
902
+ else { return y }
903
+ };
904
+ Rickshaw.namespace("Rickshaw.Color.Palette");
905
+
906
+ Rickshaw.Color.Palette = function(args) {
907
+
908
+ var color = new Rickshaw.Fixtures.Color();
909
+
910
+ args = args || {};
911
+ this.schemes = {};
912
+
913
+ this.scheme = color.schemes[args.scheme] || args.scheme || color.schemes.colorwheel;
914
+ this.runningIndex = 0;
915
+
916
+ this.color = function(key) {
917
+ return this.scheme[key] || this.scheme[this.runningIndex++] || '#808080';
918
+ };
919
+ };
920
+ Rickshaw.namespace('Graph.Ajax');
921
+
922
+ Rickshaw.Graph.Ajax = function(args) {
923
+
924
+ var self = this;
925
+ this.dataURL = args.dataURL;
926
+
927
+ $.ajax( {
928
+ url: this.dataURL,
929
+ complete: function(response, status) {
930
+
931
+ if (status === 'error') {
932
+ console.log("error loading dataURL: " + this.dataURL);
933
+ }
934
+
935
+ var data = JSON.parse(response.responseText);
936
+
937
+ if (typeof args.onData === 'function') {
938
+ var processedData = args.onData(data);
939
+ data = processedData;
940
+ }
941
+
942
+ if (args.series) {
943
+
944
+ args.series.forEach( function(s) {
945
+
946
+ var seriesKey = s.key || s.name;
947
+ if (!seriesKey) throw "series needs a key or a name";
948
+
949
+ data.forEach( function(d) {
950
+
951
+ var dataKey = d.key || d.name;
952
+ if (!dataKey) throw "data needs a key or a name";
953
+
954
+ if (seriesKey == dataKey) {
955
+ var properties = ['color', 'name', 'data'];
956
+ properties.forEach( function(p) {
957
+ s[p] = s[p] || d[p];
958
+ } );
959
+ }
960
+ } );
961
+ } );
962
+
963
+ } else {
964
+ args.series = data;
965
+ }
966
+
967
+ self.graph = new Rickshaw.Graph(args);
968
+ self.graph.render();
969
+
970
+ if (typeof args.onComplete == 'function') {
971
+ args.onComplete(self);
972
+ }
973
+ }
974
+ } );
975
+ };
976
+
977
+ Rickshaw.namespace('Rickshaw.Graph.Annotate');
978
+
979
+ Rickshaw.Graph.Annotate = function(args) {
980
+
981
+ var graph = this.graph = args.graph;
982
+ this.elements = { timeline: args.element };
983
+
984
+ var self = this;
985
+
986
+ this.data = {};
987
+
988
+ this.elements.timeline.classList.add('rickshaw_annotation_timeline');
989
+
990
+ this.add = function(time, content) {
991
+ self.data[time] = self.data[time] || {'boxes': []};
992
+ self.data[time].boxes.push({content: content});
993
+ };
994
+
995
+ this.update = function() {
996
+
997
+ Rickshaw.keys(self.data).forEach( function(time) {
998
+
999
+ var annotation = self.data[time];
1000
+ var left = self.graph.x(time);
1001
+
1002
+ if (left < 0 || left > self.graph.x.range()[1]) {
1003
+ if (annotation.element) {
1004
+ annotation.element.style.display = 'none';
1005
+ }
1006
+ return;
1007
+ }
1008
+
1009
+ if (!annotation.element) {
1010
+ var element = annotation.element = document.createElement('div');
1011
+ element.classList.add('annotation');
1012
+ this.elements.timeline.appendChild(element);
1013
+ element.addEventListener('click', function(e) {
1014
+ element.classList.toggle('active');
1015
+ annotation.line.classList.toggle('active');
1016
+ }, false);
1017
+
1018
+ }
1019
+
1020
+ annotation.element.style.left = left + 'px';
1021
+ annotation.element.style.display = 'block';
1022
+
1023
+ annotation.boxes.forEach( function(box) {
1024
+
1025
+ var element = box.element;
1026
+
1027
+ if (!element) {
1028
+ element = box.element = document.createElement('div');
1029
+ element.classList.add('content');
1030
+ element.innerHTML = box.content;
1031
+ annotation.element.appendChild(element);
1032
+
1033
+ annotation.line = document.createElement('div');
1034
+ annotation.line.classList.add('annotation_line');
1035
+ self.graph.element.appendChild(annotation.line);
1036
+ }
1037
+
1038
+ annotation.line.style.left = left + 'px';
1039
+ } );
1040
+ }, this );
1041
+ };
1042
+
1043
+ this.graph.onUpdate( function() { self.update() } );
1044
+ };
1045
+ Rickshaw.namespace('Rickshaw.Graph.Axis.Time');
1046
+
1047
+ Rickshaw.Graph.Axis.Time = function(args) {
1048
+
1049
+ var self = this;
1050
+
1051
+ this.graph = args.graph;
1052
+ this.elements = [];
1053
+ this.ticksTreatment = args.ticksTreatment || 'plain';
1054
+ this.fixedTimeUnit = args.timeUnit;
1055
+
1056
+ var time = new Rickshaw.Fixtures.Time();
1057
+
1058
+ this.appropriateTimeUnit = function() {
1059
+
1060
+ var unit;
1061
+ var units = time.units;
1062
+
1063
+ var domain = this.graph.x.domain();
1064
+ var rangeSeconds = domain[1] - domain[0];
1065
+
1066
+ units.forEach( function(u) {
1067
+ if (Math.floor(rangeSeconds / u.seconds) >= 2) {
1068
+ unit = unit || u;
1069
+ }
1070
+ } );
1071
+
1072
+ return (unit || time.units[time.units.length - 1]);
1073
+ };
1074
+
1075
+ this.tickOffsets = function() {
1076
+
1077
+ var domain = this.graph.x.domain();
1078
+
1079
+ var unit = this.fixedTimeUnit || this.appropriateTimeUnit();
1080
+ var count = Math.ceil((domain[1] - domain[0]) / unit.seconds);
1081
+
1082
+ var runningTick = domain[0];
1083
+
1084
+ var offsets = [];
1085
+
1086
+ for (var i = 0; i < count; i++) {
1087
+
1088
+ tickValue = time.ceil(runningTick, unit);
1089
+ runningTick = tickValue + unit.seconds / 2;
1090
+
1091
+ offsets.push( { value: tickValue, unit: unit } );
1092
+ }
1093
+
1094
+ return offsets;
1095
+ };
1096
+
1097
+ this.render = function() {
1098
+
1099
+ this.elements.forEach( function(e) {
1100
+ e.parentNode.removeChild(e);
1101
+ } );
1102
+
1103
+ this.elements = [];
1104
+
1105
+ var offsets = this.tickOffsets();
1106
+
1107
+ offsets.forEach( function(o) {
1108
+
1109
+ if (self.graph.x(o.value) > self.graph.x.range()[1]) return;
1110
+
1111
+ var element = document.createElement('div');
1112
+ element.style.left = self.graph.x(o.value) + 'px';
1113
+ element.classList.add('x_tick');
1114
+ element.classList.add(self.ticksTreatment);
1115
+
1116
+ var title = document.createElement('div');
1117
+ title.classList.add('title');
1118
+ title.innerHTML = o.unit.formatter(new Date(o.value * 1000));
1119
+ element.appendChild(title);
1120
+
1121
+ self.graph.element.appendChild(element);
1122
+ self.elements.push(element);
1123
+
1124
+ } );
1125
+ };
1126
+
1127
+ this.graph.onUpdate( function() { self.render() } );
1128
+ };
1129
+
1130
+ Rickshaw.namespace('Rickshaw.Graph.Axis.Y');
1131
+
1132
+ Rickshaw.Graph.Axis.Y = function(args) {
1133
+
1134
+ var self = this;
1135
+ var berthRate = 0.10;
1136
+
1137
+ this.initialize = function(args) {
1138
+
1139
+ this.graph = args.graph;
1140
+ this.orientation = args.orientation || 'right';
1141
+
1142
+ var pixelsPerTick = 60;
1143
+
1144
+ if(Math.floor(this.graph.height / pixelsPerTick) > 6){
1145
+ pixelsPerTick = Math.floor(this.graph.height / 6);
1146
+ }
1147
+
1148
+ this.ticks = args.ticks || Math.floor(this.graph.height / pixelsPerTick);
1149
+ this.tickSize = args.tickSize || 4;
1150
+ this.ticksTreatment = args.ticksTreatment || 'plain';
1151
+
1152
+ if (args.element) {
1153
+
1154
+ this.element = args.element;
1155
+ this.vis = d3.select(args.element)
1156
+ .append("svg:svg")
1157
+ .attr('class', 'rickshaw_graph y_axis');
1158
+
1159
+ this.element = this.vis[0][0];
1160
+ this.element.style.position = 'relative';
1161
+
1162
+ this.setSize({ width: args.width, height: args.height });
1163
+
1164
+ } else {
1165
+ this.vis = this.graph.vis;
1166
+ }
1167
+
1168
+ this.graph.onUpdate( function() { self.render() } );
1169
+ };
1170
+
1171
+ this.setSize = function(args) {
1172
+
1173
+ args = args || {};
1174
+
1175
+ if (!this.element) return;
1176
+
1177
+ if (typeof window !== undefined) {
1178
+
1179
+ var style = window.getComputedStyle(this.element, null);
1180
+ var elementWidth = parseInt(style.getPropertyValue('width'));
1181
+
1182
+ if (!args.auto) {
1183
+ var elementHeight = parseInt(style.getPropertyValue('height'));
1184
+ }
1185
+ }
1186
+
1187
+ this.width = args.width || elementWidth || this.graph.width * berthRate;
1188
+ this.height = args.height || elementHeight || this.graph.height;
1189
+
1190
+ this.vis
1191
+ .attr('width', this.width)
1192
+ .attr('height', this.height * (1 + berthRate));
1193
+
1194
+ var berth = this.height * berthRate;
1195
+ this.element.style.top = -1 * berth + 'px';
1196
+ this.element.style.paddingTop = berth + 'px';
1197
+ };
1198
+
1199
+ this.render = function() {
1200
+
1201
+ if (this.graph.height !== this._renderHeight) this.setSize({ auto: true });
1202
+
1203
+ var axis = d3.svg.axis().scale(this.graph.y).orient(this.orientation);
1204
+ axis.tickFormat( args.tickFormat || function(y) { return y } );
1205
+
1206
+ if (this.orientation == 'left') {
1207
+ var transform = 'translate(' + this.width + ', 0)';
1208
+ }
1209
+
1210
+ if (this.element) {
1211
+ this.vis.selectAll('*').remove();
1212
+ }
1213
+
1214
+ this.vis
1215
+ .append("svg:g")
1216
+ .attr("class", ["y_ticks", this.ticksTreatment].join(" "))
1217
+ .attr("transform", transform)
1218
+ .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize))
1219
+
1220
+ var gridSize = (this.orientation == 'right' ? 1 : -1) * this.graph.width;
1221
+
1222
+ this.graph.vis
1223
+ .append("svg:g")
1224
+ .attr("class", "y_grid")
1225
+ .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize));
1226
+
1227
+ this._renderHeight = this.graph.height;
1228
+ };
1229
+
1230
+ this.initialize(args);
1231
+ };
1232
+
1233
+ Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Highlight');
1234
+
1235
+ Rickshaw.Graph.Behavior.Series.Highlight = function(args) {
1236
+
1237
+ this.graph = args.graph;
1238
+ this.legend = args.legend;
1239
+
1240
+ var self = this;
1241
+
1242
+ var colorSafe = {};
1243
+
1244
+ this.addHighlightEvents = function (l) {
1245
+ l.element.addEventListener( 'mouseover', function(e) {
1246
+
1247
+ self.legend.lines.forEach( function(line) {
1248
+ if (l === line) return;
1249
+ colorSafe[line.series.name] = colorSafe[line.series.name] || line.series.color;
1250
+ line.series.color = d3.interpolateRgb(line.series.color, d3.rgb('#d8d8d8'))(0.8).toString();
1251
+ } );
1252
+
1253
+ self.graph.update();
1254
+
1255
+ }, false );
1256
+
1257
+ l.element.addEventListener( 'mouseout', function(e) {
1258
+
1259
+ self.legend.lines.forEach( function(line) {
1260
+ if (colorSafe[line.series.name]) {
1261
+ line.series.color = colorSafe[line.series.name];
1262
+ }
1263
+ } );
1264
+
1265
+ self.graph.update();
1266
+
1267
+ }, false );
1268
+ };
1269
+
1270
+ if (this.legend) {
1271
+ this.legend.lines.forEach( function(l) {
1272
+ self.addHighlightEvents(l);
1273
+ } );
1274
+ }
1275
+
1276
+ };
1277
+ Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Order');
1278
+
1279
+ Rickshaw.Graph.Behavior.Series.Order = function(args) {
1280
+
1281
+ this.graph = args.graph;
1282
+ this.legend = args.legend;
1283
+
1284
+ var self = this;
1285
+
1286
+ $(function() {
1287
+ $(self.legend.list).sortable( {
1288
+ containment: 'parent',
1289
+ tolerance: 'pointer',
1290
+ update: function( event, ui ) {
1291
+ var series = [];
1292
+ $(self.legend.list).find('li').each( function(index, item) {
1293
+ if (!item.series) return;
1294
+ series.push(item.series);
1295
+ } );
1296
+
1297
+ for (var i = self.graph.series.length - 1; i >= 0; i--) {
1298
+ self.graph.series[i] = series.shift();
1299
+ }
1300
+
1301
+ self.graph.update();
1302
+ }
1303
+ } );
1304
+ $(self.legend.list).disableSelection();
1305
+ });
1306
+
1307
+ //hack to make jquery-ui sortable behave
1308
+ this.graph.onUpdate( function() {
1309
+ var h = window.getComputedStyle(self.legend.element).height;
1310
+ self.legend.element.style.height = h;
1311
+ } );
1312
+ };
1313
+ Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Toggle');
1314
+
1315
+ Rickshaw.Graph.Behavior.Series.Toggle = function(args) {
1316
+
1317
+ this.graph = args.graph;
1318
+ this.legend = args.legend;
1319
+
1320
+ var self = this;
1321
+
1322
+ this.addAnchor = function(line) {
1323
+ var anchor = document.createElement('a');
1324
+ anchor.innerHTML = '&#10004;';
1325
+ anchor.classList.add('action');
1326
+ line.element.insertBefore(anchor, line.element.firstChild);
1327
+
1328
+ anchor.onclick = function(e) {
1329
+ if (line.series.disabled) {
1330
+ line.series.enable();
1331
+ line.element.classList.remove('disabled');
1332
+ } else {
1333
+ line.series.disable();
1334
+ line.element.classList.add('disabled');
1335
+ }
1336
+ }
1337
+
1338
+ var label = line.element.getElementsByTagName('span')[0];
1339
+ label.onclick = function(e){
1340
+
1341
+ var disableAllOtherLines = line.series.disabled;
1342
+ if ( ! disableAllOtherLines ) {
1343
+ for ( var i = 0; i < self.legend.lines.length; i++ ) {
1344
+ var l = self.legend.lines[i];
1345
+ if ( line.series === l.series ) {
1346
+ // noop
1347
+ } else if ( l.series.disabled ) {
1348
+ // noop
1349
+ } else {
1350
+ disableAllOtherLines = true;
1351
+ break;
1352
+ }
1353
+ }
1354
+ }
1355
+
1356
+ // show all or none
1357
+ if ( disableAllOtherLines ) {
1358
+
1359
+ // these must happen first or else we try ( and probably fail ) to make a no line graph
1360
+ line.series.enable();
1361
+ line.element.classList.remove('disabled');
1362
+
1363
+ self.legend.lines.forEach(function(l){
1364
+ if ( line.series === l.series ) {
1365
+ // noop
1366
+ } else {
1367
+ l.series.disable();
1368
+ l.element.classList.add('disabled');
1369
+ }
1370
+ });
1371
+
1372
+ } else {
1373
+
1374
+ self.legend.lines.forEach(function(l){
1375
+ l.series.enable();
1376
+ l.element.classList.remove('disabled');
1377
+ });
1378
+
1379
+ }
1380
+
1381
+ };
1382
+
1383
+ };
1384
+
1385
+ if (this.legend) {
1386
+
1387
+ $(this.legend.list).sortable( {
1388
+ start: function(event, ui) {
1389
+ ui.item.bind('no.onclick',
1390
+ function(event) {
1391
+ event.preventDefault();
1392
+ }
1393
+ );
1394
+ },
1395
+ stop: function(event, ui) {
1396
+ setTimeout(function(){
1397
+ ui.item.unbind('no.onclick');
1398
+ }, 250);
1399
+ }
1400
+ })
1401
+
1402
+ this.legend.lines.forEach( function(l) {
1403
+ self.addAnchor(l);
1404
+ } );
1405
+ }
1406
+
1407
+ this._addBehavior = function() {
1408
+
1409
+ this.graph.series.forEach( function(s) {
1410
+
1411
+ s.disable = function() {
1412
+
1413
+ if (self.graph.series.length <= 1) {
1414
+ throw('only one series left');
1415
+ }
1416
+
1417
+ s.disabled = true;
1418
+ self.graph.update();
1419
+ };
1420
+
1421
+ s.enable = function() {
1422
+ s.disabled = false;
1423
+ self.graph.update();
1424
+ };
1425
+ } );
1426
+ };
1427
+ this._addBehavior();
1428
+
1429
+ this.updateBehaviour = function () { this._addBehavior() };
1430
+
1431
+ };
1432
+ Rickshaw.namespace('Rickshaw.Graph.HoverDetail');
1433
+
1434
+ Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
1435
+
1436
+ initialize: function(args) {
1437
+
1438
+ var graph = this.graph = args.graph;
1439
+
1440
+ this.xFormatter = args.xFormatter || function(x) {
1441
+ return FnordMetric.util.dateFormat(x);
1442
+ };
1443
+
1444
+ this.yFormatter = args.yFormatter || function(y) {
1445
+ return y.toFixed(2);
1446
+ };
1447
+
1448
+ var element = this.element = document.createElement('div');
1449
+ element.className = 'detail';
1450
+
1451
+ if(args.no_detail){
1452
+ element.className = 'detail no_detail';
1453
+ }
1454
+
1455
+ this.visible = true;
1456
+ graph.element.appendChild(element);
1457
+
1458
+ this.lastEvent = null;
1459
+ this._addListeners();
1460
+
1461
+ this.onShow = args.onShow;
1462
+ this.onHide = args.onHide;
1463
+ this.onRender = args.onRender;
1464
+
1465
+ this.formatter = args.formatter || this.formatter;
1466
+ },
1467
+
1468
+ formatter: function(series, x, y, formattedX, formattedY) {
1469
+ return series.name + ':&nbsp;' + formattedY;
1470
+ },
1471
+
1472
+ update: function(e) {
1473
+
1474
+ e = e || this.lastEvent;
1475
+ if (!e) return;
1476
+ this.lastEvent = e;
1477
+
1478
+ if (e.target.nodeName != 'path' && e.target.nodeName != 'svg') return;
1479
+
1480
+ var graph = this.graph;
1481
+
1482
+ var eventX = e.offsetX || e.layerX;
1483
+ var eventY = e.offsetY || e.layerY;
1484
+
1485
+ var domainX = graph.x.invert(eventX);
1486
+ var stackedData = graph.stackedData;
1487
+
1488
+ var topSeriesData = stackedData.slice(-1).shift();
1489
+
1490
+ var domainIndexScale = d3.scale.linear()
1491
+ .domain([topSeriesData[0].x, topSeriesData.slice(-1).shift().x])
1492
+ .range([0, topSeriesData.length]);
1493
+
1494
+ var approximateIndex = Math.floor(domainIndexScale(domainX));
1495
+ var dataIndex = approximateIndex || 0;
1496
+
1497
+ for (var i = approximateIndex; i < stackedData[0].length - 1;) {
1498
+
1499
+ if (!stackedData[0][i] || !stackedData[0][i + 1]) {
1500
+ break;
1501
+ }
1502
+
1503
+ if (stackedData[0][i].x <= domainX && stackedData[0][i + 1].x > domainX) {
1504
+ dataIndex = i;
1505
+ break;
1506
+ }
1507
+ if (stackedData[0][i + 1] < domainX) { i++ } else { i-- }
1508
+ }
1509
+
1510
+ var domainX = stackedData[0][dataIndex].x;
1511
+ var formattedXValue = this.xFormatter(domainX);
1512
+ var graphX = graph.x(domainX);
1513
+ var order = 0;
1514
+
1515
+ var detail = graph.series.active()
1516
+ .map( function(s) { return { order: order++, series: s, name: s.name, value: s.stack[dataIndex] } } );
1517
+
1518
+ var activeItem;
1519
+
1520
+ var sortFn = function(a, b) {
1521
+ return (a.value.y0 + a.value.y) - (b.value.y0 + b.value.y);
1522
+ };
1523
+
1524
+ var domainMouseY = graph.y.magnitude.invert(graph.element.offsetHeight - eventY);
1525
+
1526
+ detail.sort(sortFn).forEach( function(d) {
1527
+
1528
+ d.formattedYValue = (this.yFormatter.constructor == Array) ?
1529
+ this.yFormatter[detail.indexOf(d)](d.value.y) :
1530
+ this.yFormatter(d.value.y);
1531
+
1532
+ d.graphX = graphX;
1533
+ d.graphY = graph.y(d.value.y0 + d.value.y);
1534
+
1535
+ if (domainMouseY > d.value.y0 && domainMouseY < d.value.y0 + d.value.y && !activeItem) {
1536
+ activeItem = d;
1537
+ d.active = true;
1538
+ }
1539
+
1540
+ }, this );
1541
+
1542
+ this.element.innerHTML = '';
1543
+ this.element.style.left = graph.x(domainX) + 'px';
1544
+
1545
+ if (this.visible) {
1546
+ this.render( {
1547
+ detail: detail,
1548
+ domainX: domainX,
1549
+ formattedXValue: formattedXValue,
1550
+ mouseX: eventX,
1551
+ mouseY: eventY
1552
+ } );
1553
+ }
1554
+ },
1555
+
1556
+ hide: function() {
1557
+ this.visible = false;
1558
+ this.element.classList.add('inactive');
1559
+
1560
+ if (typeof this.onHide == 'function') {
1561
+ this.onHide();
1562
+ }
1563
+ },
1564
+
1565
+ show: function() {
1566
+ this.visible = true;
1567
+ this.element.classList.remove('inactive');
1568
+
1569
+ if (typeof this.onShow == 'function') {
1570
+ this.onShow();
1571
+ }
1572
+ },
1573
+
1574
+ render: function(args) {
1575
+
1576
+ var detail = args.detail;
1577
+ var domainX = args.domainX;
1578
+
1579
+ var mouseX = args.mouseX;
1580
+ var mouseY = args.mouseY;
1581
+
1582
+ var formattedXValue = args.formattedXValue;
1583
+
1584
+ var xLabel = document.createElement('div');
1585
+ xLabel.className = 'x_label';
1586
+ xLabel.innerHTML = formattedXValue;
1587
+ this.element.appendChild(xLabel);
1588
+
1589
+ detail.forEach( function(d) {
1590
+
1591
+ var item = document.createElement('div');
1592
+ item.className = 'item';
1593
+ item.innerHTML = this.formatter(d.series, domainX, d.value.y, formattedXValue, d.formattedYValue);
1594
+ item.style.top = this.graph.y(d.value.y0 + d.value.y) + 'px';
1595
+
1596
+ this.element.appendChild(item);
1597
+
1598
+ var dot = document.createElement('div');
1599
+ dot.className = 'dot';
1600
+ dot.style.top = item.style.top;
1601
+ dot.style.borderColor = d.series.color;
1602
+
1603
+ this.element.appendChild(dot);
1604
+
1605
+ if (d.active) {
1606
+ item.className = 'item active';
1607
+ dot.className = 'dot active';
1608
+ }
1609
+
1610
+ }, this );
1611
+
1612
+ this.show();
1613
+
1614
+ if (typeof this.onRender == 'function') {
1615
+ this.onRender(args);
1616
+ }
1617
+ },
1618
+
1619
+ _addListeners: function() {
1620
+
1621
+ this.graph.element.addEventListener(
1622
+ 'mousemove',
1623
+ function(e) {
1624
+ this.visible = true;
1625
+ this.update(e)
1626
+ }.bind(this),
1627
+ false
1628
+ );
1629
+
1630
+ this.graph.onUpdate( function() { this.update() }.bind(this) );
1631
+
1632
+ this.graph.element.addEventListener(
1633
+ 'mouseout',
1634
+ function(e) {
1635
+ if (e.relatedTarget && !(e.relatedTarget.compareDocumentPosition(this.graph.element) & Node.DOCUMENT_POSITION_CONTAINS)) {
1636
+ this.hide();
1637
+ }
1638
+ }.bind(this),
1639
+ false
1640
+ );
1641
+ }
1642
+ });
1643
+
1644
+ Rickshaw.namespace('Rickshaw.Graph.JSONP');
1645
+
1646
+ Rickshaw.Graph.JSONP = function(args) {
1647
+
1648
+ var self = this;
1649
+ this.dataURL = args.dataURL;
1650
+
1651
+ $.ajax( {
1652
+ url: this.dataURL,
1653
+ dataType: 'jsonp',
1654
+ success: function(data, status, response) {
1655
+
1656
+ if (status === 'error') {
1657
+ console.log("error loading dataURL: " + this.dataURL);
1658
+ }
1659
+
1660
+ if (typeof args.onData === 'function') {
1661
+ var processedData = args.onData(data);
1662
+ data = processedData;
1663
+ }
1664
+
1665
+ if (args.series) {
1666
+
1667
+ args.series.forEach( function(s) {
1668
+
1669
+ var seriesKey = s.key || s.name;
1670
+ if (!seriesKey) throw "series needs a key or a name";
1671
+
1672
+ data.forEach( function(d) {
1673
+
1674
+ var dataKey = d.key || d.name;
1675
+ if (!dataKey) throw "data needs a key or a name";
1676
+
1677
+ if (seriesKey == dataKey) {
1678
+ var properties = ['color', 'name', 'data'];
1679
+ properties.forEach( function(p) {
1680
+ s[p] = s[p] || d[p];
1681
+ } );
1682
+ }
1683
+ } );
1684
+ } );
1685
+
1686
+ } else {
1687
+ args.series = data;
1688
+ }
1689
+
1690
+ self.graph = new Rickshaw.Graph(args);
1691
+ self.graph.render();
1692
+
1693
+ if (typeof args.onComplete == 'function') {
1694
+ args.onComplete(self);
1695
+ }
1696
+ }
1697
+ } );
1698
+ };
1699
+
1700
+ Rickshaw.namespace('Rickshaw.Graph.Legend');
1701
+
1702
+ Rickshaw.Graph.Legend = function(args) {
1703
+
1704
+ var element = this.element = args.element;
1705
+ var graph = this.graph = args.graph;
1706
+
1707
+ var self = this;
1708
+
1709
+ element.classList.add('rickshaw_legend');
1710
+
1711
+ var list = this.list = document.createElement('ul');
1712
+ element.appendChild(list);
1713
+
1714
+ var series = graph.series
1715
+ .map( function(s) { return s } )
1716
+ .reverse();
1717
+
1718
+ this.lines = [];
1719
+
1720
+ this.addLine = function (series) {
1721
+ var line = document.createElement('li');
1722
+ line.className = 'line';
1723
+
1724
+ var swatch = document.createElement('div');
1725
+ swatch.className = 'swatch';
1726
+ swatch.style.backgroundColor = series.color;
1727
+
1728
+ line.appendChild(swatch);
1729
+
1730
+ var label = document.createElement('span');
1731
+ label.className = 'label';
1732
+ label.innerHTML = series.name;
1733
+
1734
+ line.appendChild(label);
1735
+ list.appendChild(line);
1736
+
1737
+ line.series = series;
1738
+
1739
+ if (series.noLegend) {
1740
+ line.style.display = 'none';
1741
+ }
1742
+
1743
+ var _line = { element: line, series: series };
1744
+ if (self.shelving) {
1745
+ self.shelving.addAnchor(_line);
1746
+ self.shelving.updateBehaviour();
1747
+ }
1748
+ if (self.highlighter) {
1749
+ self.highlighter.addHighlightEvents(_line);
1750
+ }
1751
+ self.lines.push(_line);
1752
+ };
1753
+
1754
+ series.forEach( function(s) {
1755
+ self.addLine(s);
1756
+ } );
1757
+
1758
+ graph.onUpdate( function() {
1759
+
1760
+ } );
1761
+ };
1762
+ Rickshaw.namespace('Rickshaw.Graph.RangeSlider');
1763
+
1764
+ Rickshaw.Graph.RangeSlider = function(args) {
1765
+
1766
+ var element = this.element = args.element;
1767
+ var graph = this.graph = args.graph;
1768
+
1769
+ $( function() {
1770
+ $(element).slider( {
1771
+
1772
+ range: true,
1773
+ min: graph.dataDomain()[0],
1774
+ max: graph.dataDomain()[1],
1775
+ values: [
1776
+ graph.dataDomain()[0],
1777
+ graph.dataDomain()[1],
1778
+ ],
1779
+ slide: function( event, ui ) {
1780
+
1781
+ graph.window.xMin = ui.values[0];
1782
+ graph.window.xMax = ui.values[1];
1783
+ graph.update();
1784
+
1785
+ // if we're at an extreme, stick there
1786
+ if (graph.dataDomain()[0] == ui.values[0]) {
1787
+ graph.window.xMin = undefined;
1788
+ }
1789
+ if (graph.dataDomain()[1] == ui.values[1]) {
1790
+ graph.window.xMax = undefined;
1791
+ }
1792
+ }
1793
+ } );
1794
+ } );
1795
+
1796
+ element[0].style.width = graph.width + 'px';
1797
+
1798
+ graph.onUpdate( function() {
1799
+
1800
+ var values = $(element).slider('option', 'values');
1801
+
1802
+ $(element).slider('option', 'min', graph.dataDomain()[0]);
1803
+ $(element).slider('option', 'max', graph.dataDomain()[1]);
1804
+
1805
+ if (graph.window.xMin == undefined) {
1806
+ values[0] = graph.dataDomain()[0];
1807
+ }
1808
+ if (graph.window.xMax == undefined) {
1809
+ values[1] = graph.dataDomain()[1];
1810
+ }
1811
+
1812
+ $(element).slider('option', 'values', values);
1813
+
1814
+ } );
1815
+ };
1816
+
1817
+ Rickshaw.namespace("Rickshaw.Graph.Renderer");
1818
+
1819
+ Rickshaw.Graph.Renderer = Rickshaw.Class.create( {
1820
+
1821
+ initialize: function(args) {
1822
+ this.graph = args.graph;
1823
+ this.tension = args.tension || this.tension;
1824
+ this.graph.unstacker = this.graph.unstacker || new Rickshaw.Graph.Unstacker( { graph: this.graph } );
1825
+ this.configure(args);
1826
+ },
1827
+
1828
+ seriesPathFactory: function() {
1829
+ //implement in subclass
1830
+ },
1831
+
1832
+ seriesStrokeFactory: function() {
1833
+ // implement in subclass
1834
+ },
1835
+
1836
+ defaults: function() {
1837
+ return {
1838
+ tension: 0.8,
1839
+ strokeWidth: 2,
1840
+ unstack: true,
1841
+ padding: { top: 0.01, right: 0, bottom: 0.01, left: 0 },
1842
+ stroke: false,
1843
+ fill: false
1844
+ };
1845
+ },
1846
+
1847
+ domain: function() {
1848
+
1849
+ var values = [];
1850
+ var stackedData = this.graph.stackedData || this.graph.stackData();
1851
+
1852
+ var topSeriesData = this.unstack ? stackedData : [ stackedData.slice(-1).shift() ];
1853
+
1854
+ topSeriesData.forEach( function(series) {
1855
+ series.forEach( function(d) {
1856
+ values.push( d.y + d.y0 );
1857
+ } );
1858
+ } );
1859
+
1860
+ var xMin = stackedData[0][0].x;
1861
+ var xMax = stackedData[0][ stackedData[0].length - 1 ].x;
1862
+
1863
+ xMin -= (xMax - xMin) * this.padding.left;
1864
+ xMax += (xMax - xMin) * this.padding.right;
1865
+
1866
+ var yMin = this.graph.min === 'auto' ? d3.min( values ) : this.graph.min || 0;
1867
+ var yMax = this.graph.max || d3.max( values );
1868
+
1869
+ if (this.graph.min === 'auto' || yMin < 0) {
1870
+ yMin -= (yMax - yMin) * this.padding.bottom;
1871
+ }
1872
+
1873
+ if (this.graph.max === undefined) {
1874
+ yMax += (yMax - yMin) * this.padding.top;
1875
+ }
1876
+
1877
+ return { x: [xMin, xMax], y: [yMin, yMax] };
1878
+ },
1879
+
1880
+ render: function() {
1881
+
1882
+ var graph = this.graph;
1883
+
1884
+ graph.vis.selectAll('*').remove();
1885
+
1886
+ var nodes = graph.vis.selectAll("path")
1887
+ .data(this.graph.stackedData)
1888
+ .enter().append("svg:path")
1889
+ .attr("d", this.seriesPathFactory());
1890
+
1891
+ var i = 0;
1892
+ graph.series.forEach( function(series) {
1893
+ if (series.disabled) return;
1894
+ series.path = nodes[0][i++];
1895
+ this._styleSeries(series);
1896
+ }, this );
1897
+ },
1898
+
1899
+ _styleSeries: function(series, fm_opts) {
1900
+
1901
+ var fill = this.fill ? series.color : 'none';
1902
+ var stroke = this.stroke ? series.color : 'none';
1903
+
1904
+ series.path.setAttribute('fill', fill);
1905
+ series.path.setAttribute('stroke', stroke);
1906
+ if (fm_opts){
1907
+ series.path.setAttribute('stroke-width', fm_opts.stroke_width);
1908
+ } else {
1909
+ series.path.setAttribute('stroke-width', this.strokeWidth);
1910
+ }
1911
+ series.path.setAttribute('class', series.className);
1912
+ },
1913
+
1914
+ configure: function(args) {
1915
+
1916
+ args = args || {};
1917
+
1918
+ Rickshaw.keys(this.defaults()).forEach( function(key) {
1919
+
1920
+ if (!args.hasOwnProperty(key)) {
1921
+ this[key] = this[key] || this.graph[key] || this.defaults()[key];
1922
+ return;
1923
+ }
1924
+
1925
+ if (typeof this.defaults()[key] == 'object') {
1926
+
1927
+ Rickshaw.keys(this.defaults()[key]).forEach( function(k) {
1928
+
1929
+ this[key][k] =
1930
+ args[key][k] !== undefined ? args[key][k] :
1931
+ this[key][k] !== undefined ? this[key][k] :
1932
+ this.defaults()[key][k];
1933
+ }, this );
1934
+
1935
+ } else {
1936
+ this[key] =
1937
+ args[key] !== undefined ? args[key] :
1938
+ this[key] !== undefined ? this[key] :
1939
+ this.graph[key] !== undefined ? this.graph[key] :
1940
+ this.defaults()[key];
1941
+ }
1942
+
1943
+ }, this );
1944
+ },
1945
+
1946
+ setStrokeWidth: function(strokeWidth) {
1947
+ if (strokeWidth !== undefined) {
1948
+ this.strokeWidth = strokeWidth;
1949
+ }
1950
+ },
1951
+
1952
+ setTension: function(tension) {
1953
+ if (tension !== undefined) {
1954
+ this.tension = tension;
1955
+ }
1956
+ }
1957
+ } );
1958
+
1959
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Line');
1960
+
1961
+ Rickshaw.Graph.Renderer.Line = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
1962
+
1963
+ name: 'line',
1964
+
1965
+ defaults: function($super) {
1966
+
1967
+ return Rickshaw.extend( $super(), {
1968
+ unstack: true,
1969
+ fill: false,
1970
+ stroke: true
1971
+ } );
1972
+ },
1973
+
1974
+ seriesPathFactory: function() {
1975
+
1976
+ var graph = this.graph;
1977
+
1978
+ return d3.svg.line()
1979
+ .x( function(d) { return graph.x(d.x) } )
1980
+ .y( function(d) { return graph.y(d.y) } )
1981
+ .interpolate(this.graph.interpolation).tension(this.tension);
1982
+ },
1983
+
1984
+
1985
+ render: function() {
1986
+
1987
+ if(this.graph.stackedData[0].length < 42){
1988
+ var fm_opts = { stroke_width: 3, draw_points: true };
1989
+ } else if(this.graph.stackedData[0].length < 99){
1990
+ var fm_opts = { stroke_width: 2, draw_points: false };
1991
+ } else {
1992
+ var fm_opts = { stroke_width: 1, draw_points: false };
1993
+ }
1994
+
1995
+ var graph = this.graph;
1996
+
1997
+ graph.vis.selectAll('*').remove();
1998
+
1999
+ var nodes = graph.vis.selectAll("path")
2000
+ .data(this.graph.stackedData)
2001
+ .enter().append("svg:path")
2002
+ .attr("d", this.seriesPathFactory());
2003
+
2004
+ if(fm_opts.draw_points){
2005
+ console.log("FIXPAUL: timeseries widget -° draw points!");
2006
+ }
2007
+
2008
+ var i = 0;
2009
+ graph.series.forEach( function(series) {
2010
+ if (series.disabled) return;
2011
+ series.path = nodes[0][i++];
2012
+ this._styleSeries(series, fm_opts);
2013
+ }, this );
2014
+
2015
+ },
2016
+
2017
+ } );
2018
+
2019
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Stack');
2020
+
2021
+ Rickshaw.Graph.Renderer.Stack = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2022
+
2023
+ name: 'stack',
2024
+
2025
+ defaults: function($super) {
2026
+
2027
+ return Rickshaw.extend( $super(), {
2028
+ fill: true,
2029
+ stroke: false,
2030
+ unstack: false,
2031
+ } );
2032
+ },
2033
+
2034
+ seriesPathFactory: function() {
2035
+
2036
+ var graph = this.graph;
2037
+
2038
+ return d3.svg.area()
2039
+ .x( function(d) { return graph.x(d.x) } )
2040
+ .y0( function(d) { return graph.y(d.y0) } )
2041
+ .y1( function(d) { return graph.y(d.y + d.y0) } )
2042
+ .interpolate(this.graph.interpolation).tension(this.tension);
2043
+ },
2044
+
2045
+ render: function() {
2046
+ var graph = this.graph;
2047
+
2048
+ graph.vis.selectAll('*').remove();
2049
+
2050
+ var nodes = graph.vis.selectAll("path")
2051
+ .data(this.graph.stackedData)
2052
+ .enter().append("svg:path")
2053
+ .attr("d", this.seriesPathFactory());
2054
+
2055
+ var i = 0;
2056
+ graph.series.forEach( function(series) {
2057
+ if (series.disabled) return;
2058
+ series.path = nodes[0][i++];
2059
+ this._styleSeries(series);
2060
+ }, this );
2061
+ },
2062
+
2063
+ _styleSeries: function(series, fm_opts) {
2064
+
2065
+ var fill = this.fill ? series.color : 'none';
2066
+ var stroke = this.stroke ? series.color : 'none';
2067
+
2068
+ series.path.setAttribute('fill', d3.interpolateRgb(fill, 'white')(0.125))
2069
+ series.path.setAttribute('stroke', stroke);
2070
+ if (fm_opts){
2071
+ series.path.setAttribute('stroke-width', fm_opts.stroke_width);
2072
+ } else {
2073
+ series.path.setAttribute('stroke-width', this.strokeWidth);
2074
+ }
2075
+ series.path.setAttribute('class', series.className);
2076
+ },
2077
+
2078
+
2079
+ } );
2080
+
2081
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Bar');
2082
+
2083
+ Rickshaw.Graph.Renderer.Bar = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2084
+
2085
+ name: 'bar',
2086
+
2087
+ defaults: function($super) {
2088
+
2089
+ var defaults = Rickshaw.extend( $super(), {
2090
+ gapSize: 0.10,
2091
+ unstack: false,
2092
+ } );
2093
+
2094
+ delete defaults.tension;
2095
+ return defaults;
2096
+ },
2097
+
2098
+ initialize: function($super, args) {
2099
+ args = args || {};
2100
+ this.gapSize = args.gapSize || this.gapSize;
2101
+ this.xPadding = args.xPadding || 50;
2102
+ $super(args);
2103
+ },
2104
+
2105
+ domain: function($super) {
2106
+
2107
+ var domain = $super();
2108
+
2109
+ var frequentInterval = this._frequentInterval();
2110
+ domain.x[1] += parseInt(frequentInterval.magnitude);
2111
+
2112
+ return domain;
2113
+ },
2114
+
2115
+ barWidth: function() {
2116
+ var stackedData = this.graph.stackedData || this.graph.stackData();
2117
+ var data = stackedData.slice(-1).shift();
2118
+
2119
+ var frequentInterval = this._frequentInterval();
2120
+ var barWidth = this.graph.x(data[0].x + frequentInterval.magnitude * (1 - this.gapSize));
2121
+
2122
+ return ((this.graph.width - (this.xPadding * 2)) / data.length);
2123
+ },
2124
+
2125
+ render: function() {
2126
+
2127
+ var graph = this.graph;
2128
+
2129
+ graph.vis.selectAll('*').remove();
2130
+
2131
+ var barWidth = this.barWidth();
2132
+ var barXOffset = 0;
2133
+
2134
+ var activeSeriesCount = graph.series.filter( function(s) { return !s.disabled; } ).length;
2135
+ var seriesBarWidth = this.unstack ? barWidth / activeSeriesCount : barWidth;
2136
+
2137
+ graph.series.forEach( function(series) {
2138
+
2139
+ if (series.disabled) return;
2140
+
2141
+ var xpad = this.xPadding;
2142
+
2143
+ var seriesBarDrawWidth = Math.min(60,
2144
+ parseInt(seriesBarWidth * (1 - this.gapSize)));
2145
+
2146
+ if(parseInt(seriesBarWidth) == seriesBarDrawWidth){
2147
+ seriesBarDrawWidth -= 1;
2148
+ }
2149
+
2150
+ var seriesBarDrawPadding = (seriesBarWidth - seriesBarDrawWidth) / 2;
2151
+
2152
+ var nodes = graph.vis.selectAll("path")
2153
+ .data(series.stack)
2154
+ .enter().append("svg:rect")
2155
+ .attr("x", function(d) { return xpad + (d.x * seriesBarWidth) + seriesBarDrawPadding })
2156
+ .attr("y", function(d) { return graph.y(d.y0 + d.y) })
2157
+ .attr("width", seriesBarDrawWidth)
2158
+ .attr("stroke", "#000")
2159
+ .attr("stroke-width", "1px")
2160
+ .attr("stroke-opacity", "0.6")
2161
+ .attr("height", function(d) { return graph.y.magnitude(d.y) });
2162
+
2163
+
2164
+ var sdata = series.stack;
2165
+ for(var ind=0; ind < sdata.length; ind++){
2166
+ $(graph.element).append(
2167
+ $("<div>")
2168
+ .css("position", "absolute")
2169
+ .css("color", "#666")
2170
+ .css("width", seriesBarWidth)
2171
+ .css("textAlign", "center")
2172
+ .css("marginTop", "5px")
2173
+ .css("marginLeft", xpad + (sdata[ind].x * seriesBarWidth))
2174
+ .css("y", graph.height)
2175
+ .html(sdata[ind].label)
2176
+ );
2177
+ //
2178
+ }
2179
+
2180
+ Array.prototype.forEach.call(nodes[0], function(n) {
2181
+ n.setAttribute('fill', series.color);
2182
+ } );
2183
+
2184
+ }, this );
2185
+ },
2186
+
2187
+ _frequentInterval: function() {
2188
+
2189
+ var stackedData = this.graph.stackedData || this.graph.stackData();
2190
+ var data = stackedData.slice(-1).shift();
2191
+
2192
+ var intervalCounts = {};
2193
+
2194
+ for (var i = 0; i < data.length - 1; i++) {
2195
+ var interval = data[i + 1].x - data[i].x;
2196
+ intervalCounts[interval] = intervalCounts[interval] || 0;
2197
+ intervalCounts[interval]++;
2198
+ }
2199
+
2200
+ var frequentInterval = { count: 0 };
2201
+
2202
+ Rickshaw.keys(intervalCounts).forEach( function(i) {
2203
+ if (frequentInterval.count < intervalCounts[i]) {
2204
+
2205
+ frequentInterval = {
2206
+ count: intervalCounts[i],
2207
+ magnitude: i
2208
+ };
2209
+ }
2210
+ } );
2211
+
2212
+ this._frequentInterval = function() { return frequentInterval };
2213
+
2214
+ return frequentInterval;
2215
+ }
2216
+ } );
2217
+
2218
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Area');
2219
+
2220
+ Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2221
+
2222
+ name: 'area',
2223
+
2224
+ defaults: function($super) {
2225
+
2226
+ return Rickshaw.extend( $super(), {
2227
+ unstack: false,
2228
+ fill: false,
2229
+ stroke: false
2230
+ } );
2231
+ },
2232
+
2233
+ seriesPathFactory: function() {
2234
+
2235
+ var graph = this.graph;
2236
+
2237
+ return d3.svg.area()
2238
+ .x( function(d) { return graph.x(d.x) } )
2239
+ .y0( function(d) { return graph.y(d.y0) } )
2240
+ .y1( function(d) { return graph.y(d.y + d.y0) } )
2241
+ .interpolate(graph.interpolation).tension(this.tension);
2242
+ },
2243
+
2244
+ seriesStrokeFactory: function() {
2245
+
2246
+ var graph = this.graph;
2247
+
2248
+ return d3.svg.line()
2249
+ .x( function(d) { return graph.x(d.x) } )
2250
+ .y( function(d) { return graph.y(d.y + d.y0) } )
2251
+ .interpolate(graph.interpolation).tension(this.tension);
2252
+ },
2253
+
2254
+ render: function() {
2255
+
2256
+ var graph = this.graph;
2257
+
2258
+ graph.vis.selectAll('*').remove();
2259
+
2260
+ if(this.graph.stackedData[0].length < 42){
2261
+ var fm_opts = { stroke_width: 3 };
2262
+ } else {
2263
+ var fm_opts = { stroke_width: 1 };
2264
+ }
2265
+
2266
+ var nodes = graph.vis.selectAll("path")
2267
+ .data(this.graph.stackedData)
2268
+ .enter().insert("svg:g", 'g');
2269
+
2270
+ nodes.append("svg:path")
2271
+ .attr("d", this.seriesPathFactory())
2272
+ .attr("class", 'area');
2273
+
2274
+ if (this.stroke) {
2275
+ nodes.append("svg:path")
2276
+ .attr("d", this.seriesStrokeFactory())
2277
+ .attr("class", 'line');
2278
+ }
2279
+
2280
+ var i = 0;
2281
+ graph.series.forEach( function(series) {
2282
+ if (series.disabled) return;
2283
+ series.path = nodes[0][i++];
2284
+ this._styleSeries(series, fm_opts);
2285
+ }, this );
2286
+ },
2287
+
2288
+ _styleSeries: function(series, fm_opts) {
2289
+
2290
+ if (!series.path) return;
2291
+
2292
+ d3.select(series.path).select('.area')
2293
+ .attr('opacity', '0.65')
2294
+ .attr('fill', series.color);
2295
+
2296
+ d3.select(series.path).select('.line')
2297
+ .attr('fill', 'none')
2298
+ .attr('stroke', d3.interpolateRgb(series.color, 'white')(0.125))
2299
+ .attr('stroke-width', fm_opts.stroke_width);
2300
+
2301
+ if (series.className) {
2302
+ series.path.setAttribute('class', series.className);
2303
+ }
2304
+ }
2305
+ } );
2306
+
2307
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.ScatterPlot');
2308
+
2309
+ Rickshaw.Graph.Renderer.ScatterPlot = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2310
+
2311
+ name: 'scatterplot',
2312
+
2313
+ defaults: function($super) {
2314
+
2315
+ return Rickshaw.extend( $super(), {
2316
+ unstack: true,
2317
+ fill: true,
2318
+ stroke: false,
2319
+ padding:{ top: 0.01, right: 0.01, bottom: 0.01, left: 0.01 },
2320
+ dotSize: 4
2321
+ } );
2322
+ },
2323
+
2324
+ initialize: function($super, args) {
2325
+ $super(args);
2326
+ },
2327
+
2328
+ render: function() {
2329
+
2330
+ var graph = this.graph;
2331
+
2332
+ graph.vis.selectAll('*').remove();
2333
+
2334
+ graph.series.forEach( function(series) {
2335
+
2336
+ if (series.disabled) return;
2337
+
2338
+ var nodes = graph.vis.selectAll("path")
2339
+ .data(series.stack)
2340
+ .enter().append("svg:circle")
2341
+ .attr("cx", function(d) { return graph.x(d.x) })
2342
+ .attr("cy", function(d) { return graph.y(d.y) })
2343
+ .attr("r", this.dotSize);
2344
+
2345
+ Array.prototype.forEach.call(nodes[0], function(n) {
2346
+ n.setAttribute('fill', series.color);
2347
+ } );
2348
+
2349
+ }, this );
2350
+ }
2351
+ } );
2352
+ Rickshaw.namespace('Rickshaw.Graph.Smoother');
2353
+
2354
+ Rickshaw.Graph.Smoother = function(args) {
2355
+
2356
+ this.graph = args.graph;
2357
+ this.element = args.element;
2358
+
2359
+ var self = this;
2360
+
2361
+ this.aggregationScale = 1;
2362
+
2363
+ if (this.element) {
2364
+
2365
+ $( function() {
2366
+ $(self.element).slider( {
2367
+ min: 1,
2368
+ max: 100,
2369
+ slide: function( event, ui ) {
2370
+ self.setScale(ui.value);
2371
+ self.graph.update();
2372
+ }
2373
+ } );
2374
+ } );
2375
+ }
2376
+
2377
+ self.graph.stackData.hooks.data.push( {
2378
+ name: 'smoother',
2379
+ orderPosition: 50,
2380
+ f: function(data) {
2381
+
2382
+ var aggregatedData = [];
2383
+
2384
+ data.forEach( function(seriesData) {
2385
+
2386
+ var aggregatedSeriesData = [];
2387
+
2388
+ while (seriesData.length) {
2389
+
2390
+ var avgX = 0, avgY = 0;
2391
+ var slice = seriesData.splice(0, self.aggregationScale);
2392
+
2393
+ slice.forEach( function(d) {
2394
+ avgX += d.x / slice.length;
2395
+ avgY += d.y / slice.length;
2396
+ } );
2397
+
2398
+ aggregatedSeriesData.push( { x: avgX, y: avgY } );
2399
+ }
2400
+
2401
+ aggregatedData.push(aggregatedSeriesData);
2402
+ } );
2403
+
2404
+ return aggregatedData;
2405
+ }
2406
+ } );
2407
+
2408
+ this.setScale = function(scale) {
2409
+
2410
+ if (scale < 1) {
2411
+ throw "scale out of range: " + scale;
2412
+ }
2413
+
2414
+ this.aggregationScale = scale;
2415
+ this.graph.update();
2416
+ }
2417
+ };
2418
+
2419
+ Rickshaw.namespace('Rickshaw.Graph.Unstacker');
2420
+
2421
+ Rickshaw.Graph.Unstacker = function(args) {
2422
+
2423
+ this.graph = args.graph;
2424
+ var self = this;
2425
+
2426
+ this.graph.stackData.hooks.after.push( {
2427
+ name: 'unstacker',
2428
+ f: function(data) {
2429
+
2430
+ if (!self.graph.renderer.unstack) return data;
2431
+
2432
+ data.forEach( function(seriesData) {
2433
+ seriesData.forEach( function(d) {
2434
+ d.y0 = 0;
2435
+ } );
2436
+ } );
2437
+
2438
+ return data;
2439
+ }
2440
+ } );
2441
+ };
2442
+
2443
+ Rickshaw.namespace('Rickshaw.Series');
2444
+
2445
+ Rickshaw.Series = Rickshaw.Class.create( Array, {
2446
+
2447
+ initialize: function (data, palette, options) {
2448
+
2449
+ options = options || {}
2450
+
2451
+ this.palette = new Rickshaw.Color.Palette(palette);
2452
+
2453
+ this.timeBase = typeof(options.timeBase) === 'undefined' ?
2454
+ Math.floor(new Date().getTime() / 1000) :
2455
+ options.timeBase;
2456
+
2457
+ if (data && (typeof(data) == "object") && (data instanceof Array)) {
2458
+ data.forEach( function(item) { this.addItem(item) }, this );
2459
+ }
2460
+ },
2461
+
2462
+ addItem: function(item) {
2463
+
2464
+ if (typeof(item.name) === 'undefined') {
2465
+ throw('addItem() needs a name');
2466
+ }
2467
+
2468
+ item.color = (item.color || this.palette.color(item.name));
2469
+ item.data = (item.data || []);
2470
+
2471
+ // backfill, if necessary
2472
+ if ((item.data.length == 0) && this.length && (this.getIndex() > 0)) {
2473
+ this[0].data.forEach( function(plot) {
2474
+ item.data.push({ x: plot.x, y: 0 });
2475
+ } );
2476
+ } else if (item.data.length == 0) {
2477
+ item.data.push({ x: this.timeBase - (this.timeInterval || 0), y: 0 });
2478
+ }
2479
+
2480
+ this.push(item);
2481
+
2482
+ if (this.legend) {
2483
+ this.legend.addLine(this.itemByName(item.name));
2484
+ }
2485
+ },
2486
+
2487
+ addData: function(data) {
2488
+
2489
+ var index = this.getIndex();
2490
+
2491
+ Rickshaw.keys(data).forEach( function(name) {
2492
+ if (! this.itemByName(name)) {
2493
+ this.addItem({ name: name });
2494
+ }
2495
+ }, this );
2496
+
2497
+ this.forEach( function(item) {
2498
+ item.data.push({
2499
+ x: (index * this.timeInterval || 1) + this.timeBase,
2500
+ y: (data[item.name] || 0)
2501
+ });
2502
+ }, this );
2503
+ },
2504
+
2505
+ getIndex: function () {
2506
+ return (this[0] && this[0].data && this[0].data.length) ? this[0].data.length : 0;
2507
+ },
2508
+
2509
+ itemByName: function(name) {
2510
+
2511
+ for (var i = 0; i < this.length; i++) {
2512
+ if (this[i].name == name)
2513
+ return this[i];
2514
+ }
2515
+ },
2516
+
2517
+ setTimeInterval: function(iv) {
2518
+ this.timeInterval = iv / 1000;
2519
+ },
2520
+
2521
+ setTimeBase: function (t) {
2522
+ this.timeBase = t;
2523
+ },
2524
+
2525
+ dump: function() {
2526
+
2527
+ var data = {
2528
+ timeBase: this.timeBase,
2529
+ timeInterval: this.timeInterval,
2530
+ items: [],
2531
+ };
2532
+
2533
+ this.forEach( function(item) {
2534
+
2535
+ var newItem = {
2536
+ color: item.color,
2537
+ name: item.name,
2538
+ data: []
2539
+ };
2540
+
2541
+ item.data.forEach( function(plot) {
2542
+ newItem.data.push({ x: plot.x, y: plot.y });
2543
+ } );
2544
+
2545
+ data.items.push(newItem);
2546
+ } );
2547
+
2548
+ return data;
2549
+ },
2550
+
2551
+ load: function(data) {
2552
+
2553
+ if (data.timeInterval) {
2554
+ this.timeInterval = data.timeInterval;
2555
+ }
2556
+
2557
+ if (data.timeBase) {
2558
+ this.timeBase = data.timeBase;
2559
+ }
2560
+
2561
+ if (data.items) {
2562
+ data.items.forEach( function(item) {
2563
+ this.push(item);
2564
+ if (this.legend) {
2565
+ this.legend.addLine(this.itemByName(item.name));
2566
+ }
2567
+
2568
+ }, this );
2569
+ }
2570
+ }
2571
+ } );
2572
+
2573
+ Rickshaw.Series.zeroFill = function(series) {
2574
+
2575
+ var x;
2576
+ var i = 0;
2577
+
2578
+ var data = series.map( function(s) { return s.data } );
2579
+
2580
+ while ( i < Math.max.apply(null, data.map( function(d) { return d.length } )) ) {
2581
+
2582
+ x = Math.min.apply( null,
2583
+ data
2584
+ .filter(function(d) { return d[i] })
2585
+ .map(function(d) { return d[i].x })
2586
+ );
2587
+
2588
+ data.forEach( function(d) {
2589
+ if (!d[i] || d[i].x != x) {
2590
+ d.splice(i, 0, { x: x, y: 0 });
2591
+ }
2592
+ } );
2593
+
2594
+ i++;
2595
+ }
2596
+ };
2597
+ Rickshaw.namespace('Rickshaw.Series.FixedDuration');
2598
+
2599
+ Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, {
2600
+
2601
+ initialize: function (data, palette, options) {
2602
+
2603
+ var options = options || {}
2604
+
2605
+ if (typeof(options.timeInterval) === 'undefined') {
2606
+ throw new Error('FixedDuration series requires timeInterval');
2607
+ }
2608
+
2609
+ if (typeof(options.maxDataPoints) === 'undefined') {
2610
+ throw new Error('FixedDuration series requires maxDataPoints');
2611
+ }
2612
+
2613
+ this.palette = new Rickshaw.Color.Palette(palette);
2614
+ this.timeBase = typeof(options.timeBase) === 'undefined' ? Math.floor(new Date().getTime() / 1000) : options.timeBase;
2615
+ this.setTimeInterval(options.timeInterval);
2616
+
2617
+ if (this[0] && this[0].data && this[0].data.length) {
2618
+ this.currentSize = this[0].data.length;
2619
+ this.currentIndex = this[0].data.length;
2620
+ } else {
2621
+ this.currentSize = 0;
2622
+ this.currentIndex = 0;
2623
+ }
2624
+
2625
+ this.maxDataPoints = options.maxDataPoints;
2626
+
2627
+
2628
+ if (data && (typeof(data) == "object") && (data instanceof Array)) {
2629
+ data.forEach( function (item) { this.addItem(item) }, this );
2630
+ this.currentSize += 1;
2631
+ this.currentIndex += 1;
2632
+ }
2633
+
2634
+ // reset timeBase for zero-filled values if needed
2635
+ this.timeBase -= (this.maxDataPoints - this.currentSize) * this.timeInterval;
2636
+
2637
+ // zero-fill up to maxDataPoints size if we don't have that much data yet
2638
+ if ((typeof(this.maxDataPoints) !== 'undefined') && (this.currentSize < this.maxDataPoints)) {
2639
+ for (var i = this.maxDataPoints - this.currentSize - 1; i > 0; i--) {
2640
+ this.currentSize += 1;
2641
+ this.currentIndex += 1;
2642
+ this.forEach( function (item) {
2643
+ item.data.unshift({ x: ((i-1) * this.timeInterval || 1) + this.timeBase, y: 0, i: i });
2644
+ }, this );
2645
+ }
2646
+ }
2647
+ },
2648
+
2649
+ addData: function($super, data) {
2650
+
2651
+ $super(data)
2652
+
2653
+ this.currentSize += 1;
2654
+ this.currentIndex += 1;
2655
+
2656
+ if (this.maxDataPoints !== undefined) {
2657
+ while (this.currentSize > this.maxDataPoints) {
2658
+ this.dropData();
2659
+ }
2660
+ }
2661
+ },
2662
+
2663
+ dropData: function() {
2664
+
2665
+ this.forEach(function(item) {
2666
+ item.data.splice(0, 1);
2667
+ } );
2668
+
2669
+ this.currentSize -= 1;
2670
+ },
2671
+
2672
+ getIndex: function () {
2673
+ return this.currentIndex;
2674
+ }
2675
+ } );
2676
+