rickshaw_rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2555 @@
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
+ return d.toUTCString().match(/, (\w+ \w+ \w+)/)[1];
856
+ };
857
+
858
+ this.formatTime = function(d) {
859
+ return d.toUTCString().match(/(\d+:\d+):/)[1];
860
+ };
861
+
862
+ this.ceil = function(time, unit) {
863
+
864
+ if (unit.name == 'month') {
865
+ var nearFuture = new Date((time + unit.seconds - 1) * 1000);
866
+ return new Date(nearFuture.getUTCFullYear(), nearFuture.getUTCMonth() + 1, 1, 0, 0, 0, 0).getTime() / 1000;
867
+ }
868
+
869
+ if (unit.name == 'year') {
870
+ var nearFuture = new Date((time + unit.seconds - 1) * 1000);
871
+ return new Date(nearFuture.getUTCFullYear(), 1, 1, 0, 0, 0, 0).getTime() / 1000;
872
+ }
873
+
874
+ return Math.ceil(time / unit.seconds) * unit.seconds;
875
+ };
876
+ };
877
+ Rickshaw.namespace('Rickshaw.Fixtures.Number');
878
+
879
+ Rickshaw.Fixtures.Number.formatKMBT = function(y) {
880
+ if (y >= 1000000000000) { return y / 1000000000000 + "T" }
881
+ else if (y >= 1000000000) { return y / 1000000000 + "B" }
882
+ else if (y >= 1000000) { return y / 1000000 + "M" }
883
+ else if (y >= 1000) { return y / 1000 + "K" }
884
+ else if (y < 1 && y > 0) { return y.toFixed(2) }
885
+ else if (y == 0) { return '' }
886
+ else { return y }
887
+ };
888
+
889
+ Rickshaw.Fixtures.Number.formatBase1024KMGTP = function(y) {
890
+ if (y >= 1125899906842624) { return y / 1125899906842624 + "P" }
891
+ else if (y >= 1099511627776){ return y / 1099511627776 + "T" }
892
+ else if (y >= 1073741824) { return y / 1073741824 + "G" }
893
+ else if (y >= 1048576) { return y / 1048576 + "M" }
894
+ else if (y >= 1024) { return y / 1024 + "K" }
895
+ else if (y < 1 && y > 0) { return y.toFixed(2) }
896
+ else if (y == 0) { return '' }
897
+ else { return y }
898
+ };
899
+ Rickshaw.namespace("Rickshaw.Color.Palette");
900
+
901
+ Rickshaw.Color.Palette = function(args) {
902
+
903
+ var color = new Rickshaw.Fixtures.Color();
904
+
905
+ args = args || {};
906
+ this.schemes = {};
907
+
908
+ this.scheme = color.schemes[args.scheme] || args.scheme || color.schemes.colorwheel;
909
+ this.runningIndex = 0;
910
+
911
+ this.color = function(key) {
912
+ return this.scheme[key] || this.scheme[this.runningIndex++] || '#808080';
913
+ };
914
+ };
915
+ Rickshaw.namespace('Graph.Ajax');
916
+
917
+ Rickshaw.Graph.Ajax = function(args) {
918
+
919
+ var self = this;
920
+ this.dataURL = args.dataURL;
921
+
922
+ $.ajax( {
923
+ url: this.dataURL,
924
+ complete: function(response, status) {
925
+
926
+ if (status === 'error') {
927
+ console.log("error loading dataURL: " + this.dataURL);
928
+ }
929
+
930
+ var data = JSON.parse(response.responseText);
931
+
932
+ if (typeof args.onData === 'function') {
933
+ var processedData = args.onData(data);
934
+ data = processedData;
935
+ }
936
+
937
+ if (args.series) {
938
+
939
+ args.series.forEach( function(s) {
940
+
941
+ var seriesKey = s.key || s.name;
942
+ if (!seriesKey) throw "series needs a key or a name";
943
+
944
+ data.forEach( function(d) {
945
+
946
+ var dataKey = d.key || d.name;
947
+ if (!dataKey) throw "data needs a key or a name";
948
+
949
+ if (seriesKey == dataKey) {
950
+ var properties = ['color', 'name', 'data'];
951
+ properties.forEach( function(p) {
952
+ s[p] = s[p] || d[p];
953
+ } );
954
+ }
955
+ } );
956
+ } );
957
+
958
+ } else {
959
+ args.series = data;
960
+ }
961
+
962
+ self.graph = new Rickshaw.Graph(args);
963
+ self.graph.render();
964
+
965
+ if (typeof args.onComplete == 'function') {
966
+ args.onComplete(self);
967
+ }
968
+ }
969
+ } );
970
+ };
971
+
972
+ Rickshaw.namespace('Rickshaw.Graph.Annotate');
973
+
974
+ Rickshaw.Graph.Annotate = function(args) {
975
+
976
+ var graph = this.graph = args.graph;
977
+ this.elements = { timeline: args.element };
978
+
979
+ var self = this;
980
+
981
+ this.data = {};
982
+
983
+ this.elements.timeline.classList.add('rickshaw_annotation_timeline');
984
+
985
+ this.add = function(time, content) {
986
+ self.data[time] = self.data[time] || {'boxes': []};
987
+ self.data[time].boxes.push({content: content});
988
+ };
989
+
990
+ this.update = function() {
991
+
992
+ Rickshaw.keys(self.data).forEach( function(time) {
993
+
994
+ var annotation = self.data[time];
995
+ var left = self.graph.x(time);
996
+
997
+ if (left < 0 || left > self.graph.x.range()[1]) {
998
+ if (annotation.element) {
999
+ annotation.element.style.display = 'none';
1000
+ }
1001
+ return;
1002
+ }
1003
+
1004
+ if (!annotation.element) {
1005
+ var element = annotation.element = document.createElement('div');
1006
+ element.classList.add('annotation');
1007
+ this.elements.timeline.appendChild(element);
1008
+ element.addEventListener('click', function(e) {
1009
+ element.classList.toggle('active');
1010
+ annotation.line.classList.toggle('active');
1011
+ }, false);
1012
+
1013
+ }
1014
+
1015
+ annotation.element.style.left = left + 'px';
1016
+ annotation.element.style.display = 'block';
1017
+
1018
+ annotation.boxes.forEach( function(box) {
1019
+
1020
+ var element = box.element;
1021
+
1022
+ if (!element) {
1023
+ element = box.element = document.createElement('div');
1024
+ element.classList.add('content');
1025
+ element.innerHTML = box.content;
1026
+ annotation.element.appendChild(element);
1027
+
1028
+ annotation.line = document.createElement('div');
1029
+ annotation.line.classList.add('annotation_line');
1030
+ self.graph.element.appendChild(annotation.line);
1031
+ }
1032
+
1033
+ annotation.line.style.left = left + 'px';
1034
+ } );
1035
+ }, this );
1036
+ };
1037
+
1038
+ this.graph.onUpdate( function() { self.update() } );
1039
+ };
1040
+ Rickshaw.namespace('Rickshaw.Graph.Axis.Time');
1041
+
1042
+ Rickshaw.Graph.Axis.Time = function(args) {
1043
+
1044
+ var self = this;
1045
+
1046
+ this.graph = args.graph;
1047
+ this.elements = [];
1048
+ this.ticksTreatment = args.ticksTreatment || 'plain';
1049
+ this.fixedTimeUnit = args.timeUnit;
1050
+
1051
+ var time = new Rickshaw.Fixtures.Time();
1052
+
1053
+ this.appropriateTimeUnit = function() {
1054
+
1055
+ var unit;
1056
+ var units = time.units;
1057
+
1058
+ var domain = this.graph.x.domain();
1059
+ var rangeSeconds = domain[1] - domain[0];
1060
+
1061
+ units.forEach( function(u) {
1062
+ if (Math.floor(rangeSeconds / u.seconds) >= 2) {
1063
+ unit = unit || u;
1064
+ }
1065
+ } );
1066
+
1067
+ return (unit || time.units[time.units.length - 1]);
1068
+ };
1069
+
1070
+ this.tickOffsets = function() {
1071
+
1072
+ var domain = this.graph.x.domain();
1073
+
1074
+ var unit = this.fixedTimeUnit || this.appropriateTimeUnit();
1075
+ var count = Math.ceil((domain[1] - domain[0]) / unit.seconds);
1076
+
1077
+ var runningTick = domain[0];
1078
+
1079
+ var offsets = [];
1080
+
1081
+ for (var i = 0; i < count; i++) {
1082
+
1083
+ tickValue = time.ceil(runningTick, unit);
1084
+ runningTick = tickValue + unit.seconds / 2;
1085
+
1086
+ offsets.push( { value: tickValue, unit: unit } );
1087
+ }
1088
+
1089
+ return offsets;
1090
+ };
1091
+
1092
+ this.render = function() {
1093
+
1094
+ this.elements.forEach( function(e) {
1095
+ e.parentNode.removeChild(e);
1096
+ } );
1097
+
1098
+ this.elements = [];
1099
+
1100
+ var offsets = this.tickOffsets();
1101
+
1102
+ offsets.forEach( function(o) {
1103
+
1104
+ if (self.graph.x(o.value) > self.graph.x.range()[1]) return;
1105
+
1106
+ var element = document.createElement('div');
1107
+ element.style.left = self.graph.x(o.value) + 'px';
1108
+ element.classList.add('x_tick');
1109
+ element.classList.add(self.ticksTreatment);
1110
+
1111
+ var title = document.createElement('div');
1112
+ title.classList.add('title');
1113
+ title.innerHTML = o.unit.formatter(new Date(o.value * 1000));
1114
+ element.appendChild(title);
1115
+
1116
+ self.graph.element.appendChild(element);
1117
+ self.elements.push(element);
1118
+
1119
+ } );
1120
+ };
1121
+
1122
+ this.graph.onUpdate( function() { self.render() } );
1123
+ };
1124
+
1125
+ Rickshaw.namespace('Rickshaw.Graph.Axis.Y');
1126
+
1127
+ Rickshaw.Graph.Axis.Y = function(args) {
1128
+
1129
+ var self = this;
1130
+ var berthRate = 0.10;
1131
+
1132
+ this.initialize = function(args) {
1133
+
1134
+ this.graph = args.graph;
1135
+ this.orientation = args.orientation || 'right';
1136
+
1137
+ var pixelsPerTick = 75;
1138
+ this.ticks = args.ticks || Math.floor(this.graph.height / pixelsPerTick);
1139
+ this.tickSize = args.tickSize || 4;
1140
+ this.ticksTreatment = args.ticksTreatment || 'plain';
1141
+
1142
+ if (args.element) {
1143
+
1144
+ this.element = args.element;
1145
+ this.vis = d3.select(args.element)
1146
+ .append("svg:svg")
1147
+ .attr('class', 'rickshaw_graph y_axis');
1148
+
1149
+ this.element = this.vis[0][0];
1150
+ this.element.style.position = 'relative';
1151
+
1152
+ this.setSize({ width: args.width, height: args.height });
1153
+
1154
+ } else {
1155
+ this.vis = this.graph.vis;
1156
+ }
1157
+
1158
+ this.graph.onUpdate( function() { self.render() } );
1159
+ };
1160
+
1161
+ this.setSize = function(args) {
1162
+
1163
+ args = args || {};
1164
+
1165
+ if (!this.element) return;
1166
+
1167
+ if (typeof window !== undefined) {
1168
+
1169
+ var style = window.getComputedStyle(this.element, null);
1170
+ var elementWidth = parseInt(style.getPropertyValue('width'));
1171
+
1172
+ if (!args.auto) {
1173
+ var elementHeight = parseInt(style.getPropertyValue('height'));
1174
+ }
1175
+ }
1176
+
1177
+ this.width = args.width || elementWidth || this.graph.width * berthRate;
1178
+ this.height = args.height || elementHeight || this.graph.height;
1179
+
1180
+ this.vis
1181
+ .attr('width', this.width)
1182
+ .attr('height', this.height * (1 + berthRate));
1183
+
1184
+ var berth = this.height * berthRate;
1185
+ this.element.style.top = -1 * berth + 'px';
1186
+ this.element.style.paddingTop = berth + 'px';
1187
+ };
1188
+
1189
+ this.render = function() {
1190
+
1191
+ if (this.graph.height !== this._renderHeight) this.setSize({ auto: true });
1192
+
1193
+ var axis = d3.svg.axis().scale(this.graph.y).orient(this.orientation);
1194
+ axis.tickFormat( args.tickFormat || function(y) { return y } );
1195
+
1196
+ if (this.orientation == 'left') {
1197
+ var transform = 'translate(' + this.width + ', 0)';
1198
+ }
1199
+
1200
+ if (this.element) {
1201
+ this.vis.selectAll('*').remove();
1202
+ }
1203
+
1204
+ this.vis
1205
+ .append("svg:g")
1206
+ .attr("class", ["y_ticks", this.ticksTreatment].join(" "))
1207
+ .attr("transform", transform)
1208
+ .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize))
1209
+
1210
+ var gridSize = (this.orientation == 'right' ? 1 : -1) * this.graph.width;
1211
+
1212
+ this.graph.vis
1213
+ .append("svg:g")
1214
+ .attr("class", "y_grid")
1215
+ .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize));
1216
+
1217
+ this._renderHeight = this.graph.height;
1218
+ };
1219
+
1220
+ this.initialize(args);
1221
+ };
1222
+
1223
+ Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Highlight');
1224
+
1225
+ Rickshaw.Graph.Behavior.Series.Highlight = function(args) {
1226
+
1227
+ this.graph = args.graph;
1228
+ this.legend = args.legend;
1229
+
1230
+ var self = this;
1231
+
1232
+ var colorSafe = {};
1233
+
1234
+ this.addHighlightEvents = function (l) {
1235
+ l.element.addEventListener( 'mouseover', function(e) {
1236
+
1237
+ self.legend.lines.forEach( function(line) {
1238
+ if (l === line) return;
1239
+ colorSafe[line.series.name] = colorSafe[line.series.name] || line.series.color;
1240
+ line.series.color = d3.interpolateRgb(line.series.color, d3.rgb('#d8d8d8'))(0.8).toString();
1241
+ } );
1242
+
1243
+ self.graph.update();
1244
+
1245
+ }, false );
1246
+
1247
+ l.element.addEventListener( 'mouseout', function(e) {
1248
+
1249
+ self.legend.lines.forEach( function(line) {
1250
+ if (colorSafe[line.series.name]) {
1251
+ line.series.color = colorSafe[line.series.name];
1252
+ }
1253
+ } );
1254
+
1255
+ self.graph.update();
1256
+
1257
+ }, false );
1258
+ };
1259
+
1260
+ if (this.legend) {
1261
+ this.legend.lines.forEach( function(l) {
1262
+ self.addHighlightEvents(l);
1263
+ } );
1264
+ }
1265
+
1266
+ };
1267
+ Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Order');
1268
+
1269
+ Rickshaw.Graph.Behavior.Series.Order = function(args) {
1270
+
1271
+ this.graph = args.graph;
1272
+ this.legend = args.legend;
1273
+
1274
+ var self = this;
1275
+
1276
+ $(function() {
1277
+ $(self.legend.list).sortable( {
1278
+ containment: 'parent',
1279
+ tolerance: 'pointer',
1280
+ update: function( event, ui ) {
1281
+ var series = [];
1282
+ $(self.legend.list).find('li').each( function(index, item) {
1283
+ if (!item.series) return;
1284
+ series.push(item.series);
1285
+ } );
1286
+
1287
+ for (var i = self.graph.series.length - 1; i >= 0; i--) {
1288
+ self.graph.series[i] = series.shift();
1289
+ }
1290
+
1291
+ self.graph.update();
1292
+ }
1293
+ } );
1294
+ $(self.legend.list).disableSelection();
1295
+ });
1296
+
1297
+ //hack to make jquery-ui sortable behave
1298
+ this.graph.onUpdate( function() {
1299
+ var h = window.getComputedStyle(self.legend.element).height;
1300
+ self.legend.element.style.height = h;
1301
+ } );
1302
+ };
1303
+ Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Toggle');
1304
+
1305
+ Rickshaw.Graph.Behavior.Series.Toggle = function(args) {
1306
+
1307
+ this.graph = args.graph;
1308
+ this.legend = args.legend;
1309
+
1310
+ var self = this;
1311
+
1312
+ this.addAnchor = function(line) {
1313
+ var anchor = document.createElement('a');
1314
+ anchor.innerHTML = '&#10004;';
1315
+ anchor.classList.add('action');
1316
+ line.element.insertBefore(anchor, line.element.firstChild);
1317
+
1318
+ anchor.onclick = function(e) {
1319
+ if (line.series.disabled) {
1320
+ line.series.enable();
1321
+ line.element.classList.remove('disabled');
1322
+ } else {
1323
+ line.series.disable();
1324
+ line.element.classList.add('disabled');
1325
+ }
1326
+ }
1327
+
1328
+ var label = line.element.getElementsByTagName('span')[0];
1329
+ label.onclick = function(e){
1330
+
1331
+ var disableAllOtherLines = line.series.disabled;
1332
+ if ( ! disableAllOtherLines ) {
1333
+ for ( var i = 0; i < self.legend.lines.length; i++ ) {
1334
+ var l = self.legend.lines[i];
1335
+ if ( line.series === l.series ) {
1336
+ // noop
1337
+ } else if ( l.series.disabled ) {
1338
+ // noop
1339
+ } else {
1340
+ disableAllOtherLines = true;
1341
+ break;
1342
+ }
1343
+ }
1344
+ }
1345
+
1346
+ // show all or none
1347
+ if ( disableAllOtherLines ) {
1348
+
1349
+ // these must happen first or else we try ( and probably fail ) to make a no line graph
1350
+ line.series.enable();
1351
+ line.element.classList.remove('disabled');
1352
+
1353
+ self.legend.lines.forEach(function(l){
1354
+ if ( line.series === l.series ) {
1355
+ // noop
1356
+ } else {
1357
+ l.series.disable();
1358
+ l.element.classList.add('disabled');
1359
+ }
1360
+ });
1361
+
1362
+ } else {
1363
+
1364
+ self.legend.lines.forEach(function(l){
1365
+ l.series.enable();
1366
+ l.element.classList.remove('disabled');
1367
+ });
1368
+
1369
+ }
1370
+
1371
+ };
1372
+
1373
+ };
1374
+
1375
+ if (this.legend) {
1376
+
1377
+ $(this.legend.list).sortable( {
1378
+ start: function(event, ui) {
1379
+ ui.item.bind('no.onclick',
1380
+ function(event) {
1381
+ event.preventDefault();
1382
+ }
1383
+ );
1384
+ },
1385
+ stop: function(event, ui) {
1386
+ setTimeout(function(){
1387
+ ui.item.unbind('no.onclick');
1388
+ }, 250);
1389
+ }
1390
+ })
1391
+
1392
+ this.legend.lines.forEach( function(l) {
1393
+ self.addAnchor(l);
1394
+ } );
1395
+ }
1396
+
1397
+ this._addBehavior = function() {
1398
+
1399
+ this.graph.series.forEach( function(s) {
1400
+
1401
+ s.disable = function() {
1402
+
1403
+ if (self.graph.series.length <= 1) {
1404
+ throw('only one series left');
1405
+ }
1406
+
1407
+ s.disabled = true;
1408
+ self.graph.update();
1409
+ };
1410
+
1411
+ s.enable = function() {
1412
+ s.disabled = false;
1413
+ self.graph.update();
1414
+ };
1415
+ } );
1416
+ };
1417
+ this._addBehavior();
1418
+
1419
+ this.updateBehaviour = function () { this._addBehavior() };
1420
+
1421
+ };
1422
+ Rickshaw.namespace('Rickshaw.Graph.HoverDetail');
1423
+
1424
+ Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({
1425
+
1426
+ initialize: function(args) {
1427
+
1428
+ var graph = this.graph = args.graph;
1429
+
1430
+ this.xFormatter = args.xFormatter || function(x) {
1431
+ return new Date( x * 1000 ).toUTCString();
1432
+ };
1433
+
1434
+ this.yFormatter = args.yFormatter || function(y) {
1435
+ return y.toFixed(2);
1436
+ };
1437
+
1438
+ var element = this.element = document.createElement('div');
1439
+ element.className = 'detail';
1440
+
1441
+ this.visible = true;
1442
+ graph.element.appendChild(element);
1443
+
1444
+ this.lastEvent = null;
1445
+ this._addListeners();
1446
+
1447
+ this.onShow = args.onShow;
1448
+ this.onHide = args.onHide;
1449
+ this.onRender = args.onRender;
1450
+
1451
+ this.formatter = args.formatter || this.formatter;
1452
+ },
1453
+
1454
+ formatter: function(series, x, y, formattedX, formattedY) {
1455
+ return series.name + ':&nbsp;' + formattedY;
1456
+ },
1457
+
1458
+ update: function(e) {
1459
+
1460
+ e = e || this.lastEvent;
1461
+ if (!e) return;
1462
+ this.lastEvent = e;
1463
+
1464
+ if (e.target.nodeName != 'path' && e.target.nodeName != 'svg') return;
1465
+
1466
+ var graph = this.graph;
1467
+
1468
+ var eventX = e.offsetX || e.layerX;
1469
+ var eventY = e.offsetY || e.layerY;
1470
+
1471
+ var domainX = graph.x.invert(eventX);
1472
+ var stackedData = graph.stackedData;
1473
+
1474
+ var topSeriesData = stackedData.slice(-1).shift();
1475
+
1476
+ var domainIndexScale = d3.scale.linear()
1477
+ .domain([topSeriesData[0].x, topSeriesData.slice(-1).shift().x])
1478
+ .range([0, topSeriesData.length]);
1479
+
1480
+ var approximateIndex = Math.floor(domainIndexScale(domainX));
1481
+ var dataIndex = approximateIndex || 0;
1482
+
1483
+ for (var i = approximateIndex; i < stackedData[0].length - 1;) {
1484
+
1485
+ if (!stackedData[0][i] || !stackedData[0][i + 1]) {
1486
+ break;
1487
+ }
1488
+
1489
+ if (stackedData[0][i].x <= domainX && stackedData[0][i + 1].x > domainX) {
1490
+ dataIndex = i;
1491
+ break;
1492
+ }
1493
+ if (stackedData[0][i + 1] < domainX) { i++ } else { i-- }
1494
+ }
1495
+
1496
+ var domainX = stackedData[0][dataIndex].x;
1497
+ var formattedXValue = this.xFormatter(domainX);
1498
+ var graphX = graph.x(domainX);
1499
+ var order = 0;
1500
+
1501
+ var detail = graph.series.active()
1502
+ .map( function(s) { return { order: order++, series: s, name: s.name, value: s.stack[dataIndex] } } );
1503
+
1504
+ var activeItem;
1505
+
1506
+ var sortFn = function(a, b) {
1507
+ return (a.value.y0 + a.value.y) - (b.value.y0 + b.value.y);
1508
+ };
1509
+
1510
+ var domainMouseY = graph.y.magnitude.invert(graph.element.offsetHeight - eventY);
1511
+
1512
+ detail.sort(sortFn).forEach( function(d) {
1513
+
1514
+ d.formattedYValue = (this.yFormatter.constructor == Array) ?
1515
+ this.yFormatter[detail.indexOf(d)](d.value.y) :
1516
+ this.yFormatter(d.value.y);
1517
+
1518
+ d.graphX = graphX;
1519
+ d.graphY = graph.y(d.value.y0 + d.value.y);
1520
+
1521
+ if (domainMouseY > d.value.y0 && domainMouseY < d.value.y0 + d.value.y && !activeItem) {
1522
+ activeItem = d;
1523
+ d.active = true;
1524
+ }
1525
+
1526
+ }, this );
1527
+
1528
+ this.element.innerHTML = '';
1529
+ this.element.style.left = graph.x(domainX) + 'px';
1530
+
1531
+ if (this.visible) {
1532
+ this.render( {
1533
+ detail: detail,
1534
+ domainX: domainX,
1535
+ formattedXValue: formattedXValue,
1536
+ mouseX: eventX,
1537
+ mouseY: eventY
1538
+ } );
1539
+ }
1540
+ },
1541
+
1542
+ hide: function() {
1543
+ this.visible = false;
1544
+ this.element.classList.add('inactive');
1545
+
1546
+ if (typeof this.onHide == 'function') {
1547
+ this.onHide();
1548
+ }
1549
+ },
1550
+
1551
+ show: function() {
1552
+ this.visible = true;
1553
+ this.element.classList.remove('inactive');
1554
+
1555
+ if (typeof this.onShow == 'function') {
1556
+ this.onShow();
1557
+ }
1558
+ },
1559
+
1560
+ render: function(args) {
1561
+
1562
+ var detail = args.detail;
1563
+ var domainX = args.domainX;
1564
+
1565
+ var mouseX = args.mouseX;
1566
+ var mouseY = args.mouseY;
1567
+
1568
+ var formattedXValue = args.formattedXValue;
1569
+
1570
+ var xLabel = document.createElement('div');
1571
+ xLabel.className = 'x_label';
1572
+ xLabel.innerHTML = formattedXValue;
1573
+ this.element.appendChild(xLabel);
1574
+
1575
+ detail.forEach( function(d) {
1576
+
1577
+ var item = document.createElement('div');
1578
+ item.className = 'item';
1579
+ item.innerHTML = this.formatter(d.series, domainX, d.value.y, formattedXValue, d.formattedYValue);
1580
+ item.style.top = this.graph.y(d.value.y0 + d.value.y) + 'px';
1581
+
1582
+ this.element.appendChild(item);
1583
+
1584
+ var dot = document.createElement('div');
1585
+ dot.className = 'dot';
1586
+ dot.style.top = item.style.top;
1587
+ dot.style.borderColor = d.series.color;
1588
+
1589
+ this.element.appendChild(dot);
1590
+
1591
+ if (d.active) {
1592
+ item.className = 'item active';
1593
+ dot.className = 'dot active';
1594
+ }
1595
+
1596
+ }, this );
1597
+
1598
+ this.show();
1599
+
1600
+ if (typeof this.onRender == 'function') {
1601
+ this.onRender(args);
1602
+ }
1603
+ },
1604
+
1605
+ _addListeners: function() {
1606
+
1607
+ this.graph.element.addEventListener(
1608
+ 'mousemove',
1609
+ function(e) {
1610
+ this.visible = true;
1611
+ this.update(e)
1612
+ }.bind(this),
1613
+ false
1614
+ );
1615
+
1616
+ this.graph.onUpdate( function() { this.update() }.bind(this) );
1617
+
1618
+ this.graph.element.addEventListener(
1619
+ 'mouseout',
1620
+ function(e) {
1621
+ if (e.relatedTarget && !(e.relatedTarget.compareDocumentPosition(this.graph.element) & Node.DOCUMENT_POSITION_CONTAINS)) {
1622
+ this.hide();
1623
+ }
1624
+ }.bind(this),
1625
+ false
1626
+ );
1627
+ }
1628
+ });
1629
+
1630
+ Rickshaw.namespace('Rickshaw.Graph.JSONP');
1631
+
1632
+ Rickshaw.Graph.JSONP = function(args) {
1633
+
1634
+ var self = this;
1635
+ this.dataURL = args.dataURL;
1636
+
1637
+ $.ajax( {
1638
+ url: this.dataURL,
1639
+ dataType: 'jsonp',
1640
+ success: function(data, status, response) {
1641
+
1642
+ if (status === 'error') {
1643
+ console.log("error loading dataURL: " + this.dataURL);
1644
+ }
1645
+
1646
+ if (typeof args.onData === 'function') {
1647
+ var processedData = args.onData(data);
1648
+ data = processedData;
1649
+ }
1650
+
1651
+ if (args.series) {
1652
+
1653
+ args.series.forEach( function(s) {
1654
+
1655
+ var seriesKey = s.key || s.name;
1656
+ if (!seriesKey) throw "series needs a key or a name";
1657
+
1658
+ data.forEach( function(d) {
1659
+
1660
+ var dataKey = d.key || d.name;
1661
+ if (!dataKey) throw "data needs a key or a name";
1662
+
1663
+ if (seriesKey == dataKey) {
1664
+ var properties = ['color', 'name', 'data'];
1665
+ properties.forEach( function(p) {
1666
+ s[p] = s[p] || d[p];
1667
+ } );
1668
+ }
1669
+ } );
1670
+ } );
1671
+
1672
+ } else {
1673
+ args.series = data;
1674
+ }
1675
+
1676
+ self.graph = new Rickshaw.Graph(args);
1677
+ self.graph.render();
1678
+
1679
+ if (typeof args.onComplete == 'function') {
1680
+ args.onComplete(self);
1681
+ }
1682
+ }
1683
+ } );
1684
+ };
1685
+
1686
+ Rickshaw.namespace('Rickshaw.Graph.Legend');
1687
+
1688
+ Rickshaw.Graph.Legend = function(args) {
1689
+
1690
+ var element = this.element = args.element;
1691
+ var graph = this.graph = args.graph;
1692
+
1693
+ var self = this;
1694
+
1695
+ element.classList.add('rickshaw_legend');
1696
+
1697
+ var list = this.list = document.createElement('ul');
1698
+ element.appendChild(list);
1699
+
1700
+ var series = graph.series
1701
+ .map( function(s) { return s } )
1702
+ .reverse();
1703
+
1704
+ this.lines = [];
1705
+
1706
+ this.addLine = function (series) {
1707
+ var line = document.createElement('li');
1708
+ line.className = 'line';
1709
+
1710
+ var swatch = document.createElement('div');
1711
+ swatch.className = 'swatch';
1712
+ swatch.style.backgroundColor = series.color;
1713
+
1714
+ line.appendChild(swatch);
1715
+
1716
+ var label = document.createElement('span');
1717
+ label.className = 'label';
1718
+ label.innerHTML = series.name;
1719
+
1720
+ line.appendChild(label);
1721
+ list.appendChild(line);
1722
+
1723
+ line.series = series;
1724
+
1725
+ if (series.noLegend) {
1726
+ line.style.display = 'none';
1727
+ }
1728
+
1729
+ var _line = { element: line, series: series };
1730
+ if (self.shelving) {
1731
+ self.shelving.addAnchor(_line);
1732
+ self.shelving.updateBehaviour();
1733
+ }
1734
+ if (self.highlighter) {
1735
+ self.highlighter.addHighlightEvents(_line);
1736
+ }
1737
+ self.lines.push(_line);
1738
+ };
1739
+
1740
+ series.forEach( function(s) {
1741
+ self.addLine(s);
1742
+ } );
1743
+
1744
+ graph.onUpdate( function() {
1745
+
1746
+ } );
1747
+ };
1748
+ Rickshaw.namespace('Rickshaw.Graph.RangeSlider');
1749
+
1750
+ Rickshaw.Graph.RangeSlider = function(args) {
1751
+
1752
+ var element = this.element = args.element;
1753
+ var graph = this.graph = args.graph;
1754
+
1755
+ $( function() {
1756
+ $(element).slider( {
1757
+
1758
+ range: true,
1759
+ min: graph.dataDomain()[0],
1760
+ max: graph.dataDomain()[1],
1761
+ values: [
1762
+ graph.dataDomain()[0],
1763
+ graph.dataDomain()[1],
1764
+ ],
1765
+ slide: function( event, ui ) {
1766
+
1767
+ graph.window.xMin = ui.values[0];
1768
+ graph.window.xMax = ui.values[1];
1769
+ graph.update();
1770
+
1771
+ // if we're at an extreme, stick there
1772
+ if (graph.dataDomain()[0] == ui.values[0]) {
1773
+ graph.window.xMin = undefined;
1774
+ }
1775
+ if (graph.dataDomain()[1] == ui.values[1]) {
1776
+ graph.window.xMax = undefined;
1777
+ }
1778
+ }
1779
+ } );
1780
+ } );
1781
+
1782
+ element[0].style.width = graph.width + 'px';
1783
+
1784
+ graph.onUpdate( function() {
1785
+
1786
+ var values = $(element).slider('option', 'values');
1787
+
1788
+ $(element).slider('option', 'min', graph.dataDomain()[0]);
1789
+ $(element).slider('option', 'max', graph.dataDomain()[1]);
1790
+
1791
+ if (graph.window.xMin == undefined) {
1792
+ values[0] = graph.dataDomain()[0];
1793
+ }
1794
+ if (graph.window.xMax == undefined) {
1795
+ values[1] = graph.dataDomain()[1];
1796
+ }
1797
+
1798
+ $(element).slider('option', 'values', values);
1799
+
1800
+ } );
1801
+ };
1802
+
1803
+ Rickshaw.namespace("Rickshaw.Graph.Renderer");
1804
+
1805
+ Rickshaw.Graph.Renderer = Rickshaw.Class.create( {
1806
+
1807
+ initialize: function(args) {
1808
+ this.graph = args.graph;
1809
+ this.tension = args.tension || this.tension;
1810
+ this.graph.unstacker = this.graph.unstacker || new Rickshaw.Graph.Unstacker( { graph: this.graph } );
1811
+ this.configure(args);
1812
+ },
1813
+
1814
+ seriesPathFactory: function() {
1815
+ //implement in subclass
1816
+ },
1817
+
1818
+ seriesStrokeFactory: function() {
1819
+ // implement in subclass
1820
+ },
1821
+
1822
+ defaults: function() {
1823
+ return {
1824
+ tension: 0.8,
1825
+ strokeWidth: 2,
1826
+ unstack: true,
1827
+ padding: { top: 0.01, right: 0, bottom: 0.01, left: 0 },
1828
+ stroke: false,
1829
+ fill: false
1830
+ };
1831
+ },
1832
+
1833
+ domain: function() {
1834
+
1835
+ var values = [];
1836
+ var stackedData = this.graph.stackedData || this.graph.stackData();
1837
+
1838
+ var topSeriesData = this.unstack ? stackedData : [ stackedData.slice(-1).shift() ];
1839
+
1840
+ topSeriesData.forEach( function(series) {
1841
+ series.forEach( function(d) {
1842
+ values.push( d.y + d.y0 );
1843
+ } );
1844
+ } );
1845
+
1846
+ var xMin = stackedData[0][0].x;
1847
+ var xMax = stackedData[0][ stackedData[0].length - 1 ].x;
1848
+
1849
+ xMin -= (xMax - xMin) * this.padding.left;
1850
+ xMax += (xMax - xMin) * this.padding.right;
1851
+
1852
+ var yMin = this.graph.min === 'auto' ? d3.min( values ) : this.graph.min || 0;
1853
+ var yMax = this.graph.max || d3.max( values );
1854
+
1855
+ if (this.graph.min === 'auto' || yMin < 0) {
1856
+ yMin -= (yMax - yMin) * this.padding.bottom;
1857
+ }
1858
+
1859
+ if (this.graph.max === undefined) {
1860
+ yMax += (yMax - yMin) * this.padding.top;
1861
+ }
1862
+
1863
+ return { x: [xMin, xMax], y: [yMin, yMax] };
1864
+ },
1865
+
1866
+ render: function() {
1867
+
1868
+ var graph = this.graph;
1869
+
1870
+ graph.vis.selectAll('*').remove();
1871
+
1872
+ var nodes = graph.vis.selectAll("path")
1873
+ .data(this.graph.stackedData)
1874
+ .enter().append("svg:path")
1875
+ .attr("d", this.seriesPathFactory());
1876
+
1877
+ var i = 0;
1878
+ graph.series.forEach( function(series) {
1879
+ if (series.disabled) return;
1880
+ series.path = nodes[0][i++];
1881
+ this._styleSeries(series);
1882
+ }, this );
1883
+ },
1884
+
1885
+ _styleSeries: function(series) {
1886
+
1887
+ var fill = this.fill ? series.color : 'none';
1888
+ var stroke = this.stroke ? series.color : 'none';
1889
+
1890
+ series.path.setAttribute('fill', fill);
1891
+ series.path.setAttribute('stroke', stroke);
1892
+ series.path.setAttribute('stroke-width', this.strokeWidth);
1893
+ series.path.setAttribute('class', series.className);
1894
+ },
1895
+
1896
+ configure: function(args) {
1897
+
1898
+ args = args || {};
1899
+
1900
+ Rickshaw.keys(this.defaults()).forEach( function(key) {
1901
+
1902
+ if (!args.hasOwnProperty(key)) {
1903
+ this[key] = this[key] || this.graph[key] || this.defaults()[key];
1904
+ return;
1905
+ }
1906
+
1907
+ if (typeof this.defaults()[key] == 'object') {
1908
+
1909
+ Rickshaw.keys(this.defaults()[key]).forEach( function(k) {
1910
+
1911
+ this[key][k] =
1912
+ args[key][k] !== undefined ? args[key][k] :
1913
+ this[key][k] !== undefined ? this[key][k] :
1914
+ this.defaults()[key][k];
1915
+ }, this );
1916
+
1917
+ } else {
1918
+ this[key] =
1919
+ args[key] !== undefined ? args[key] :
1920
+ this[key] !== undefined ? this[key] :
1921
+ this.graph[key] !== undefined ? this.graph[key] :
1922
+ this.defaults()[key];
1923
+ }
1924
+
1925
+ }, this );
1926
+ },
1927
+
1928
+ setStrokeWidth: function(strokeWidth) {
1929
+ if (strokeWidth !== undefined) {
1930
+ this.strokeWidth = strokeWidth;
1931
+ }
1932
+ },
1933
+
1934
+ setTension: function(tension) {
1935
+ if (tension !== undefined) {
1936
+ this.tension = tension;
1937
+ }
1938
+ }
1939
+ } );
1940
+
1941
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Line');
1942
+
1943
+ Rickshaw.Graph.Renderer.Line = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
1944
+
1945
+ name: 'line',
1946
+
1947
+ defaults: function($super) {
1948
+
1949
+ return Rickshaw.extend( $super(), {
1950
+ unstack: true,
1951
+ fill: false,
1952
+ stroke: true
1953
+ } );
1954
+ },
1955
+
1956
+ seriesPathFactory: function() {
1957
+
1958
+ var graph = this.graph;
1959
+
1960
+ return d3.svg.line()
1961
+ .x( function(d) { return graph.x(d.x) } )
1962
+ .y( function(d) { return graph.y(d.y) } )
1963
+ .interpolate(this.graph.interpolation).tension(this.tension);
1964
+ }
1965
+ } );
1966
+
1967
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Stack');
1968
+
1969
+ Rickshaw.Graph.Renderer.Stack = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
1970
+
1971
+ name: 'stack',
1972
+
1973
+ defaults: function($super) {
1974
+
1975
+ return Rickshaw.extend( $super(), {
1976
+ fill: true,
1977
+ stroke: false,
1978
+ unstack: false,
1979
+ } );
1980
+ },
1981
+
1982
+ seriesPathFactory: function() {
1983
+
1984
+ var graph = this.graph;
1985
+
1986
+ return d3.svg.area()
1987
+ .x( function(d) { return graph.x(d.x) } )
1988
+ .y0( function(d) { return graph.y(d.y0) } )
1989
+ .y1( function(d) { return graph.y(d.y + d.y0) } )
1990
+ .interpolate(this.graph.interpolation).tension(this.tension);
1991
+ }
1992
+ } );
1993
+
1994
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Bar');
1995
+
1996
+ Rickshaw.Graph.Renderer.Bar = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
1997
+
1998
+ name: 'bar',
1999
+
2000
+ defaults: function($super) {
2001
+
2002
+ var defaults = Rickshaw.extend( $super(), {
2003
+ gapSize: 0.05,
2004
+ unstack: false,
2005
+ } );
2006
+
2007
+ delete defaults.tension;
2008
+ return defaults;
2009
+ },
2010
+
2011
+ initialize: function($super, args) {
2012
+ args = args || {};
2013
+ this.gapSize = args.gapSize || this.gapSize;
2014
+ $super(args);
2015
+ },
2016
+
2017
+ domain: function($super) {
2018
+
2019
+ var domain = $super();
2020
+
2021
+ var frequentInterval = this._frequentInterval();
2022
+ domain.x[1] += parseInt(frequentInterval.magnitude);
2023
+
2024
+ return domain;
2025
+ },
2026
+
2027
+ barWidth: function() {
2028
+
2029
+ var stackedData = this.graph.stackedData || this.graph.stackData();
2030
+ var data = stackedData.slice(-1).shift();
2031
+
2032
+ var frequentInterval = this._frequentInterval();
2033
+ var barWidth = this.graph.x(data[0].x + frequentInterval.magnitude * (1 - this.gapSize));
2034
+
2035
+ return barWidth;
2036
+ },
2037
+
2038
+ render: function() {
2039
+
2040
+ var graph = this.graph;
2041
+
2042
+ graph.vis.selectAll('*').remove();
2043
+
2044
+ var barWidth = this.barWidth();
2045
+ var barXOffset = 0;
2046
+
2047
+ var activeSeriesCount = graph.series.filter( function(s) { return !s.disabled; } ).length;
2048
+ var seriesBarWidth = this.unstack ? barWidth / activeSeriesCount : barWidth;
2049
+
2050
+ graph.series.forEach( function(series) {
2051
+
2052
+ if (series.disabled) return;
2053
+
2054
+ var nodes = graph.vis.selectAll("path")
2055
+ .data(series.stack)
2056
+ .enter().append("svg:rect")
2057
+ .attr("x", function(d) { return graph.x(d.x) + barXOffset })
2058
+ .attr("y", function(d) { return graph.y(d.y0 + d.y) })
2059
+ .attr("width", seriesBarWidth)
2060
+ .attr("height", function(d) { return graph.y.magnitude(d.y) });
2061
+
2062
+ Array.prototype.forEach.call(nodes[0], function(n) {
2063
+ n.setAttribute('fill', series.color);
2064
+ } );
2065
+
2066
+ if (this.unstack) barXOffset += seriesBarWidth;
2067
+
2068
+ }, this );
2069
+ },
2070
+
2071
+ _frequentInterval: function() {
2072
+
2073
+ var stackedData = this.graph.stackedData || this.graph.stackData();
2074
+ var data = stackedData.slice(-1).shift();
2075
+
2076
+ var intervalCounts = {};
2077
+
2078
+ for (var i = 0; i < data.length - 1; i++) {
2079
+ var interval = data[i + 1].x - data[i].x;
2080
+ intervalCounts[interval] = intervalCounts[interval] || 0;
2081
+ intervalCounts[interval]++;
2082
+ }
2083
+
2084
+ var frequentInterval = { count: 0 };
2085
+
2086
+ Rickshaw.keys(intervalCounts).forEach( function(i) {
2087
+ if (frequentInterval.count < intervalCounts[i]) {
2088
+
2089
+ frequentInterval = {
2090
+ count: intervalCounts[i],
2091
+ magnitude: i
2092
+ };
2093
+ }
2094
+ } );
2095
+
2096
+ this._frequentInterval = function() { return frequentInterval };
2097
+
2098
+ return frequentInterval;
2099
+ }
2100
+ } );
2101
+
2102
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.Area');
2103
+
2104
+ Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2105
+
2106
+ name: 'area',
2107
+
2108
+ defaults: function($super) {
2109
+
2110
+ return Rickshaw.extend( $super(), {
2111
+ unstack: false,
2112
+ fill: false,
2113
+ stroke: false
2114
+ } );
2115
+ },
2116
+
2117
+ seriesPathFactory: function() {
2118
+
2119
+ var graph = this.graph;
2120
+
2121
+ return d3.svg.area()
2122
+ .x( function(d) { return graph.x(d.x) } )
2123
+ .y0( function(d) { return graph.y(d.y0) } )
2124
+ .y1( function(d) { return graph.y(d.y + d.y0) } )
2125
+ .interpolate(graph.interpolation).tension(this.tension);
2126
+ },
2127
+
2128
+ seriesStrokeFactory: function() {
2129
+
2130
+ var graph = this.graph;
2131
+
2132
+ return d3.svg.line()
2133
+ .x( function(d) { return graph.x(d.x) } )
2134
+ .y( function(d) { return graph.y(d.y + d.y0) } )
2135
+ .interpolate(graph.interpolation).tension(this.tension);
2136
+ },
2137
+
2138
+ render: function() {
2139
+
2140
+ var graph = this.graph;
2141
+
2142
+ graph.vis.selectAll('*').remove();
2143
+
2144
+ var nodes = graph.vis.selectAll("path")
2145
+ .data(this.graph.stackedData)
2146
+ .enter().insert("svg:g", 'g');
2147
+
2148
+ nodes.append("svg:path")
2149
+ .attr("d", this.seriesPathFactory())
2150
+ .attr("class", 'area');
2151
+
2152
+ if (this.stroke) {
2153
+ nodes.append("svg:path")
2154
+ .attr("d", this.seriesStrokeFactory())
2155
+ .attr("class", 'line');
2156
+ }
2157
+
2158
+ var i = 0;
2159
+ graph.series.forEach( function(series) {
2160
+ if (series.disabled) return;
2161
+ series.path = nodes[0][i++];
2162
+ this._styleSeries(series);
2163
+ }, this );
2164
+ },
2165
+
2166
+ _styleSeries: function(series) {
2167
+
2168
+ if (!series.path) return;
2169
+
2170
+ d3.select(series.path).select('.area')
2171
+ .attr('fill', series.color);
2172
+
2173
+ if (this.stroke) {
2174
+ d3.select(series.path).select('.line')
2175
+ .attr('fill', 'none')
2176
+ .attr('stroke', series.stroke || d3.interpolateRgb(series.color, 'black')(0.125))
2177
+ .attr('stroke-width', this.strokeWidth);
2178
+ }
2179
+
2180
+ if (series.className) {
2181
+ series.path.setAttribute('class', series.className);
2182
+ }
2183
+ }
2184
+ } );
2185
+
2186
+ Rickshaw.namespace('Rickshaw.Graph.Renderer.ScatterPlot');
2187
+
2188
+ Rickshaw.Graph.Renderer.ScatterPlot = Rickshaw.Class.create( Rickshaw.Graph.Renderer, {
2189
+
2190
+ name: 'scatterplot',
2191
+
2192
+ defaults: function($super) {
2193
+
2194
+ return Rickshaw.extend( $super(), {
2195
+ unstack: true,
2196
+ fill: true,
2197
+ stroke: false,
2198
+ padding:{ top: 0.01, right: 0.01, bottom: 0.01, left: 0.01 },
2199
+ dotSize: 4
2200
+ } );
2201
+ },
2202
+
2203
+ initialize: function($super, args) {
2204
+ $super(args);
2205
+ },
2206
+
2207
+ render: function() {
2208
+
2209
+ var graph = this.graph;
2210
+
2211
+ graph.vis.selectAll('*').remove();
2212
+
2213
+ graph.series.forEach( function(series) {
2214
+
2215
+ if (series.disabled) return;
2216
+
2217
+ var nodes = graph.vis.selectAll("path")
2218
+ .data(series.stack)
2219
+ .enter().append("svg:circle")
2220
+ .attr("cx", function(d) { return graph.x(d.x) })
2221
+ .attr("cy", function(d) { return graph.y(d.y) })
2222
+ .attr("r", this.dotSize);
2223
+
2224
+ Array.prototype.forEach.call(nodes[0], function(n) {
2225
+ n.setAttribute('fill', series.color);
2226
+ } );
2227
+
2228
+ }, this );
2229
+ }
2230
+ } );
2231
+ Rickshaw.namespace('Rickshaw.Graph.Smoother');
2232
+
2233
+ Rickshaw.Graph.Smoother = function(args) {
2234
+
2235
+ this.graph = args.graph;
2236
+ this.element = args.element;
2237
+
2238
+ var self = this;
2239
+
2240
+ this.aggregationScale = 1;
2241
+
2242
+ if (this.element) {
2243
+
2244
+ $( function() {
2245
+ $(self.element).slider( {
2246
+ min: 1,
2247
+ max: 100,
2248
+ slide: function( event, ui ) {
2249
+ self.setScale(ui.value);
2250
+ self.graph.update();
2251
+ }
2252
+ } );
2253
+ } );
2254
+ }
2255
+
2256
+ self.graph.stackData.hooks.data.push( {
2257
+ name: 'smoother',
2258
+ orderPosition: 50,
2259
+ f: function(data) {
2260
+
2261
+ var aggregatedData = [];
2262
+
2263
+ data.forEach( function(seriesData) {
2264
+
2265
+ var aggregatedSeriesData = [];
2266
+
2267
+ while (seriesData.length) {
2268
+
2269
+ var avgX = 0, avgY = 0;
2270
+ var slice = seriesData.splice(0, self.aggregationScale);
2271
+
2272
+ slice.forEach( function(d) {
2273
+ avgX += d.x / slice.length;
2274
+ avgY += d.y / slice.length;
2275
+ } );
2276
+
2277
+ aggregatedSeriesData.push( { x: avgX, y: avgY } );
2278
+ }
2279
+
2280
+ aggregatedData.push(aggregatedSeriesData);
2281
+ } );
2282
+
2283
+ return aggregatedData;
2284
+ }
2285
+ } );
2286
+
2287
+ this.setScale = function(scale) {
2288
+
2289
+ if (scale < 1) {
2290
+ throw "scale out of range: " + scale;
2291
+ }
2292
+
2293
+ this.aggregationScale = scale;
2294
+ this.graph.update();
2295
+ }
2296
+ };
2297
+
2298
+ Rickshaw.namespace('Rickshaw.Graph.Unstacker');
2299
+
2300
+ Rickshaw.Graph.Unstacker = function(args) {
2301
+
2302
+ this.graph = args.graph;
2303
+ var self = this;
2304
+
2305
+ this.graph.stackData.hooks.after.push( {
2306
+ name: 'unstacker',
2307
+ f: function(data) {
2308
+
2309
+ if (!self.graph.renderer.unstack) return data;
2310
+
2311
+ data.forEach( function(seriesData) {
2312
+ seriesData.forEach( function(d) {
2313
+ d.y0 = 0;
2314
+ } );
2315
+ } );
2316
+
2317
+ return data;
2318
+ }
2319
+ } );
2320
+ };
2321
+
2322
+ Rickshaw.namespace('Rickshaw.Series');
2323
+
2324
+ Rickshaw.Series = Rickshaw.Class.create( Array, {
2325
+
2326
+ initialize: function (data, palette, options) {
2327
+
2328
+ options = options || {}
2329
+
2330
+ this.palette = new Rickshaw.Color.Palette(palette);
2331
+
2332
+ this.timeBase = typeof(options.timeBase) === 'undefined' ?
2333
+ Math.floor(new Date().getTime() / 1000) :
2334
+ options.timeBase;
2335
+
2336
+ if (data && (typeof(data) == "object") && (data instanceof Array)) {
2337
+ data.forEach( function(item) { this.addItem(item) }, this );
2338
+ }
2339
+ },
2340
+
2341
+ addItem: function(item) {
2342
+
2343
+ if (typeof(item.name) === 'undefined') {
2344
+ throw('addItem() needs a name');
2345
+ }
2346
+
2347
+ item.color = (item.color || this.palette.color(item.name));
2348
+ item.data = (item.data || []);
2349
+
2350
+ // backfill, if necessary
2351
+ if ((item.data.length == 0) && this.length && (this.getIndex() > 0)) {
2352
+ this[0].data.forEach( function(plot) {
2353
+ item.data.push({ x: plot.x, y: 0 });
2354
+ } );
2355
+ } else if (item.data.length == 0) {
2356
+ item.data.push({ x: this.timeBase - (this.timeInterval || 0), y: 0 });
2357
+ }
2358
+
2359
+ this.push(item);
2360
+
2361
+ if (this.legend) {
2362
+ this.legend.addLine(this.itemByName(item.name));
2363
+ }
2364
+ },
2365
+
2366
+ addData: function(data) {
2367
+
2368
+ var index = this.getIndex();
2369
+
2370
+ Rickshaw.keys(data).forEach( function(name) {
2371
+ if (! this.itemByName(name)) {
2372
+ this.addItem({ name: name });
2373
+ }
2374
+ }, this );
2375
+
2376
+ this.forEach( function(item) {
2377
+ item.data.push({
2378
+ x: (index * this.timeInterval || 1) + this.timeBase,
2379
+ y: (data[item.name] || 0)
2380
+ });
2381
+ }, this );
2382
+ },
2383
+
2384
+ getIndex: function () {
2385
+ return (this[0] && this[0].data && this[0].data.length) ? this[0].data.length : 0;
2386
+ },
2387
+
2388
+ itemByName: function(name) {
2389
+
2390
+ for (var i = 0; i < this.length; i++) {
2391
+ if (this[i].name == name)
2392
+ return this[i];
2393
+ }
2394
+ },
2395
+
2396
+ setTimeInterval: function(iv) {
2397
+ this.timeInterval = iv / 1000;
2398
+ },
2399
+
2400
+ setTimeBase: function (t) {
2401
+ this.timeBase = t;
2402
+ },
2403
+
2404
+ dump: function() {
2405
+
2406
+ var data = {
2407
+ timeBase: this.timeBase,
2408
+ timeInterval: this.timeInterval,
2409
+ items: [],
2410
+ };
2411
+
2412
+ this.forEach( function(item) {
2413
+
2414
+ var newItem = {
2415
+ color: item.color,
2416
+ name: item.name,
2417
+ data: []
2418
+ };
2419
+
2420
+ item.data.forEach( function(plot) {
2421
+ newItem.data.push({ x: plot.x, y: plot.y });
2422
+ } );
2423
+
2424
+ data.items.push(newItem);
2425
+ } );
2426
+
2427
+ return data;
2428
+ },
2429
+
2430
+ load: function(data) {
2431
+
2432
+ if (data.timeInterval) {
2433
+ this.timeInterval = data.timeInterval;
2434
+ }
2435
+
2436
+ if (data.timeBase) {
2437
+ this.timeBase = data.timeBase;
2438
+ }
2439
+
2440
+ if (data.items) {
2441
+ data.items.forEach( function(item) {
2442
+ this.push(item);
2443
+ if (this.legend) {
2444
+ this.legend.addLine(this.itemByName(item.name));
2445
+ }
2446
+
2447
+ }, this );
2448
+ }
2449
+ }
2450
+ } );
2451
+
2452
+ Rickshaw.Series.zeroFill = function(series) {
2453
+
2454
+ var x;
2455
+ var i = 0;
2456
+
2457
+ var data = series.map( function(s) { return s.data } );
2458
+
2459
+ while ( i < Math.max.apply(null, data.map( function(d) { return d.length } )) ) {
2460
+
2461
+ x = Math.min.apply( null,
2462
+ data
2463
+ .filter(function(d) { return d[i] })
2464
+ .map(function(d) { return d[i].x })
2465
+ );
2466
+
2467
+ data.forEach( function(d) {
2468
+ if (!d[i] || d[i].x != x) {
2469
+ d.splice(i, 0, { x: x, y: 0 });
2470
+ }
2471
+ } );
2472
+
2473
+ i++;
2474
+ }
2475
+ };
2476
+ Rickshaw.namespace('Rickshaw.Series.FixedDuration');
2477
+
2478
+ Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, {
2479
+
2480
+ initialize: function (data, palette, options) {
2481
+
2482
+ var options = options || {}
2483
+
2484
+ if (typeof(options.timeInterval) === 'undefined') {
2485
+ throw new Error('FixedDuration series requires timeInterval');
2486
+ }
2487
+
2488
+ if (typeof(options.maxDataPoints) === 'undefined') {
2489
+ throw new Error('FixedDuration series requires maxDataPoints');
2490
+ }
2491
+
2492
+ this.palette = new Rickshaw.Color.Palette(palette);
2493
+ this.timeBase = typeof(options.timeBase) === 'undefined' ? Math.floor(new Date().getTime() / 1000) : options.timeBase;
2494
+ this.setTimeInterval(options.timeInterval);
2495
+
2496
+ if (this[0] && this[0].data && this[0].data.length) {
2497
+ this.currentSize = this[0].data.length;
2498
+ this.currentIndex = this[0].data.length;
2499
+ } else {
2500
+ this.currentSize = 0;
2501
+ this.currentIndex = 0;
2502
+ }
2503
+
2504
+ this.maxDataPoints = options.maxDataPoints;
2505
+
2506
+
2507
+ if (data && (typeof(data) == "object") && (data instanceof Array)) {
2508
+ data.forEach( function (item) { this.addItem(item) }, this );
2509
+ this.currentSize += 1;
2510
+ this.currentIndex += 1;
2511
+ }
2512
+
2513
+ // reset timeBase for zero-filled values if needed
2514
+ this.timeBase -= (this.maxDataPoints - this.currentSize) * this.timeInterval;
2515
+
2516
+ // zero-fill up to maxDataPoints size if we don't have that much data yet
2517
+ if ((typeof(this.maxDataPoints) !== 'undefined') && (this.currentSize < this.maxDataPoints)) {
2518
+ for (var i = this.maxDataPoints - this.currentSize - 1; i > 0; i--) {
2519
+ this.currentSize += 1;
2520
+ this.currentIndex += 1;
2521
+ this.forEach( function (item) {
2522
+ item.data.unshift({ x: ((i-1) * this.timeInterval || 1) + this.timeBase, y: 0, i: i });
2523
+ }, this );
2524
+ }
2525
+ }
2526
+ },
2527
+
2528
+ addData: function($super, data) {
2529
+
2530
+ $super(data)
2531
+
2532
+ this.currentSize += 1;
2533
+ this.currentIndex += 1;
2534
+
2535
+ if (this.maxDataPoints !== undefined) {
2536
+ while (this.currentSize > this.maxDataPoints) {
2537
+ this.dropData();
2538
+ }
2539
+ }
2540
+ },
2541
+
2542
+ dropData: function() {
2543
+
2544
+ this.forEach(function(item) {
2545
+ item.data.splice(0, 1);
2546
+ } );
2547
+
2548
+ this.currentSize -= 1;
2549
+ },
2550
+
2551
+ getIndex: function () {
2552
+ return this.currentIndex;
2553
+ }
2554
+ } );
2555
+