rickshaw_rails 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+