rickshaw_rails 1.2.0 → 1.4.3

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