kanaui 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1114 +0,0 @@
1
- /*
2
- * 'killbillGraph' is the namespace required to access all the public objects
3
- *
4
- * PIE
5
- *
6
- * Input for pie chart should be of the form
7
- * dataforPie = {"name":"pie1", "data": [{"label":"one", "value":10}, ]};
8
- *
9
- * LAYERS and LINES
10
- *
11
- * Input for layers and line graphs are expected to be of the form:
12
- * dataForGraph = [ {"name":"line1", "values":[{"x":"2013-01-01", "y":6}, {"x":"2013-01-02", "y":6}] },
13
- * {"name":"line2", "values":[{"x":"2013-01-01", "y":12}, {"x":"2013-01-02", "y":3}] } ];
14
- *
15
- * There can be up to 20 lines -- limited by the color palette -- per graph; the graph can be either:
16
- * - layered graph (KBLayersGraph)
17
- * - lines graph (KBLinesGraph)
18
- *
19
- * Description of the fields:
20
- * - name is the 'name of the line-- as shown in the label
21
- * - values are the {x,y} coordinates for each point; the x coordinates should be dates and should all be the same for each entries.
22
- *
23
- */
24
- (function (killbillGraph, $, undefined) {
25
-
26
-
27
- /**
28
- * Input parameters to draw all the graphs
29
- */
30
- killbillGraph.KBInputGraphs = function (canvasWidth, canvasHeigth, topMargin, rightMargin, bottomMargin, leftMargin, betweenGraphMargin, graphData) {
31
-
32
- this.topMargin = topMargin;
33
- this.rightMargin = rightMargin;
34
- this.bottomMargin = bottomMargin;
35
- this.leftMargin = leftMargin;
36
-
37
-
38
- this.betweenGraphMargin = betweenGraphMargin;
39
-
40
- this.canvasWidth = canvasWidth;
41
- this.canvasHeigth = canvasHeigth;
42
-
43
- this.data = graphData;
44
-
45
- }
46
-
47
-
48
- /**
49
- * KBHistogram : Histogram chart
50
- */
51
- killbillGraph.KBHistogram = function (graphCanvas, title, data, width, heigth, palette) {
52
-
53
- // For non 'bin' histogram interesting blod post : http://www.recursion.org/d3-for-mere-mortals/
54
-
55
- this.graphCanvas = graphCanvas;
56
- this.name = name
57
- this.data = data;
58
- this.width = width;
59
- this.heigth = heigth;
60
- this.palette = palette;
61
- this.title = title;
62
-
63
- this.minValue;
64
- this.maxValue;
65
-
66
-
67
- this.computeMinMax = function () {
68
- var min;
69
- var max;
70
- for (var i = 0; i < this.data.length; i++) {
71
- if (min == null || this.data[i] < min) {
72
- min = this.data[i];
73
- }
74
- if (max == null || this.data[i] > max) {
75
- max = this.data[i];
76
- }
77
- }
78
- this.minValue = min - 1;
79
- this.maxValue = max + 2;
80
- }
81
-
82
-
83
- this.draw = function () {
84
-
85
- this.computeMinMax();
86
-
87
- var formatCount = d3.format(",.0f");
88
-
89
- var x = d3.scale.linear()
90
- .domain([this.minValue, this.maxValue])
91
- .range([0, this.width]);
92
-
93
- var data = d3.layout.histogram()
94
- .bins(x.ticks(10))
95
- (this.data);
96
-
97
- var y = d3.scale.linear()
98
- .domain([0, d3.max(data, function (d) {
99
- return d.y;
100
- })])
101
- .range([this.heigth, 0]);
102
-
103
- var xAxis = d3.svg.axis()
104
- .scale(x)
105
- .orient("bottom");
106
-
107
- var svg = this.graphCanvas
108
- .append("svg")
109
- .attr("width", this.width)
110
- .attr("height", this.height)
111
- .append("g")
112
- .attr("transform", "translate(" + 0 + "," + 0 + ")");
113
-
114
- var bar = svg.selectAll(".bar")
115
- .data(data)
116
- .enter().append("g")
117
- .attr("class", "bar")
118
- .attr("transform", function (d) {
119
- return "translate(" + x(d.x) + "," + y(d.y) + ")";
120
- });
121
-
122
- var myself = this;
123
- bar.append("rect")
124
- .attr("x", 1)
125
- .attr("width", x(data[0].dx) - 1)
126
- .attr("height", function (d) {
127
- return myself.heigth - y(d.y);
128
- });
129
-
130
- bar.append("text")
131
- .attr("dy", ".75em")
132
- .attr("y", 6)
133
- .attr("x", x(data[0].dx) / 2)
134
- .attr("text-anchor", "middle")
135
- .text(function (d) {
136
- return formatCount(d.y);
137
- });
138
-
139
- svg.append("g")
140
- .attr("class", "x axis")
141
- .attr("transform", "translate(" + 0 + "," + myself.heigth + ")")
142
- .call(xAxis);
143
-
144
- this.graphCanvas.append("svg:text")
145
- .attr("class", "title")
146
- .attr("x", (this.width - this.title.length) / 2)
147
- .attr("y", -30)
148
- .text(this.title);
149
- }
150
-
151
- this.addOnMouseHandlers = function () {
152
- // Not implemented
153
- }
154
-
155
- }
156
-
157
- /**
158
- * KBPie : A Pie chart
159
- */
160
- killbillGraph.KBPie = function (graphCanvas, title, inputData, width, heigth, palette) {
161
-
162
- // If our value is less than that -- compared to total, we don't display this is too small.
163
- this.minDisplayRatio = 0.05;
164
-
165
- this.graphCanvas = graphCanvas;
166
- this.name = name
167
- this.inputData = inputData;
168
- this.width = width;
169
- this.heigth = heigth;
170
- this.radius = (this.width / 4);
171
- this.palette = palette;
172
- this.title = title;
173
-
174
- this.totalValue = function () {
175
- var result = 0;
176
- for (var i = 0; i < this.data.length; i++) {
177
- result = result + this.data[i].value;
178
- }
179
- return result;
180
- }
181
-
182
- this.draw = function () {
183
- this.addLegend();
184
- this.drawPie();
185
- this.addOnMouseHandlers();
186
- }
187
-
188
- this.drawPie = function () {
189
-
190
- var vis = this.graphCanvas
191
- .append("svg:svg")
192
- .data([this.data])
193
- .attr("width", this.width)
194
- .attr("height", this.heigth)
195
- .append("svg:g")
196
- .attr("transform", "translate(" + (this.width / 2) + "," + this.radius + ")");
197
-
198
- var arc = d3.svg.arc()
199
- .outerRadius(this.radius);
200
-
201
- var pie = d3.layout.pie()
202
- .value(function (d) {
203
- return d.value;
204
- });
205
-
206
- var arcs = vis.selectAll("g.slice")
207
- .data(pie)
208
- .enter()
209
- .append("svg:g")
210
- .attr("class", "slice");
211
-
212
- var myself = this;
213
- arcs.append("svg:path")
214
- .style("fill", function (d, i) {
215
- return palette(i);
216
- })
217
- .attr("id", function (d, i) {
218
- return "arc-" + myself.data[i]['id'];
219
- })
220
- .attr("d", arc);
221
- this.addValues(arcs, arc);
222
- }
223
-
224
- this.getDisplayValue = function (value) {
225
- var total = this.totalValue();
226
- var minDisplayRatio = this.minDisplayRatio;
227
- return (value / total > minDisplayRatio) ? "list-inline" : "none";
228
- }
229
-
230
- this.addValues = function (arcs, arc) {
231
-
232
- var myself = this;
233
- arcs.append("svg:text")
234
- .attr("transform", function (d) {
235
- d.innerRadius = 0;
236
- d.outerRadius = this.radius;
237
- return "translate(" + arc.centroid(d) + ")";
238
- })
239
- .attr("id", function (d, i) {
240
- return "arc-value-" + myself.data[i]['id'];
241
- })
242
- .attr("text-anchor", "middle")
243
- .text(function (d, i) {
244
- return myself.data[i].value;
245
- })
246
- .attr("display", function (d, i) {
247
- return myself.getDisplayValue(myself.data[i].value);
248
- });
249
- }
250
-
251
-
252
- this.addLegend = function () {
253
-
254
- this.graphCanvas.append("svg:text")
255
- .attr("class", "title")
256
- .attr("x", (this.width - this.title.length) / 2)
257
- .attr("y", -30)
258
- .text(this.title);
259
-
260
- var legend = this.graphCanvas.append("g")
261
- .attr("class", "legend")
262
- .attr("height", 100)
263
- .attr("width", 200)
264
- .attr('transform', 'translate(-100,0)')
265
-
266
-
267
- var myself = this;
268
- legend.selectAll('rect')
269
- .data(this.data)
270
- .enter()
271
- .append("rect")
272
- .attr("x", this.width - 65)
273
- .attr("y", function (d, i) {
274
- return i * 20;
275
- })
276
- .attr("id", function (d, i) {
277
- return "pie-legend-" + myself.data[i]['id'];
278
- })
279
- .attr("width", 11)
280
- .attr("height", 11)
281
- .attr("rx", 3)
282
- .attr("ry", 3)
283
- .style("fill", function (d, i) {
284
- var color = palette(i);
285
- return color;
286
- })
287
-
288
- legend.selectAll('text')
289
- .data(this.data)
290
- .enter()
291
- .append("text")
292
- .attr("x", this.width - 52)
293
- .attr("y", function (d, i) {
294
- return i * 20 + 9;
295
- })
296
- .text(function (d, i) {
297
- var text = d.label;
298
- return text;
299
- });
300
- }
301
-
302
- this.addOnMouseHandlers = function () {
303
- this.addMouseLegend();
304
- }
305
-
306
- this.addMouseLegend = function () {
307
-
308
- var myself = this;
309
- $('rect').each(function (i) {
310
-
311
-
312
- var curLegendId = $(this).attr("id");
313
- if (curLegendId === undefined || curLegendId.substring(0, 11) != "pie-legend-") {
314
- return;
315
- }
316
-
317
- var arcId = $(this).attr("id").replace("pie-legend", "arc");
318
- var arcValueId = $(this).attr("id").replace("pie-legend", "arc-value");
319
- var arcValue = $("#" + arcValueId);
320
-
321
- var otherArcs = new Array();
322
- var otherArcValues = new Array();
323
- for (var i = 0; i < myself.data.length; i++) {
324
-
325
- var curArcId = "arc-" + myself.data[i]['id'];
326
- var curArcValueId = "arc-value-" + myself.data[i]['id'];
327
- if (curArcId != arcId) {
328
- var curArc = $("#" + curArcId);
329
- otherArcs.push(curArc);
330
- var curArcValue = $("#" + curArcValueId);
331
- otherArcValues.push(curArcValue);
332
- }
333
- }
334
-
335
- var myPieLegendRect = $(this);
336
- $(this).hover(function () {
337
- for (var i = 0; i < otherArcs.length; i++) {
338
- otherArcs[i].attr("opacity", 0.1);
339
- otherArcValues[i].attr("display", "none");
340
- }
341
-
342
- arcValue.attr("display", "list-inline");
343
- myPieLegendRect.attr("width", 15)
344
- .attr("height", 15)
345
- .attr('transform', 'translate(-3,-3)');
346
- }, function () {
347
- for (var i = 0; i < otherArcs.length; i++) {
348
- otherArcs[i].attr("opacity", 1.0);
349
- otherArcValues[i].attr("display", myself.getDisplayValue(parseInt(otherArcValues[i].text())));
350
- }
351
- arcValue.attr("display", myself.getDisplayValue(parseInt(arcValue.text())));
352
-
353
- myPieLegendRect.attr("width", 11)
354
- .attr("height", 11)
355
- .attr('transform', 'translate(0,0)');
356
- });
357
- });
358
- }
359
-
360
- this.addDataId = function () {
361
- for (var i = 0; i < this.inputData.length; i++) {
362
- this.inputData[i]['id'] = (Math.random() + 1).toString(36).substring(7);
363
- }
364
- return this.inputData;
365
- }
366
-
367
- this.data = this.addDataId();
368
-
369
- }
370
-
371
- /**
372
- * KBTimeSeriesBase : Base class for both layered and non layered graphs
373
- */
374
- killbillGraph.KBTimeSeriesBase = function (graphCanvas, title, inputData, width, heigth, palette) {
375
-
376
- this.graphCanvas = graphCanvas;
377
- this.inputData = inputData;
378
-
379
- this.width = width;
380
- this.heigth = heigth;
381
- this.title = title;
382
-
383
- // the palette function out of which we create color map
384
- this.palette = palette;
385
-
386
-
387
- this.addDataId = function () {
388
- for (var i = 0; i < this.inputData.length; i++) {
389
- this.inputData[i]['id'] = (Math.random() + 1).toString(36).substring(7);
390
- }
391
- return this.inputData;
392
- }
393
-
394
- /**
395
- * Create the 'x' date scale
396
- * - dataX is is an ordered array of all the dates
397
- */
398
- this.getScaleDate = function () {
399
-
400
- var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
401
- var minDate = new Date(dataX[0]);
402
- var maxDate = new Date(dataX[dataX.length - 1]);
403
- return d3.time.scale().domain([minDate, maxDate]).range([0, width]);
404
- }
405
-
406
-
407
- this.formatDate = function (date) {
408
-
409
- // We want to display a UTC date, so before we extract year, month, day info, we add the time difference
410
- // between our timezone and UTC
411
- date.setHours(date.getHours() + (date.getTimezoneOffset() / 60));
412
- var date_part = date.getDate();
413
- var month_part = date.getMonth() + 1
414
- var year_part = date.getFullYear();
415
-
416
- return moment(date).format('MM[/]DD[/]YYYY')
417
- }
418
-
419
- /**
420
- * Create the 'Y' axis in a new svg group
421
- * - scaleY is the d3 scale built based on height and y point range
422
- */
423
- this.createYAxis = function (scaleY) {
424
- var yAxisLeft = d3.svg.axis().scale(scaleY).ticks(6).tickSize([-(this.width + 25)]).orient("left");
425
-
426
- this.graphCanvas.append("svg:g")
427
- .attr("class", "y axis")
428
- .attr("id", "yaxis-" + this['id'])
429
- .attr("transform", "translate(-25,0)")
430
- .call(yAxisLeft);
431
-
432
- /*
433
- $("#yaxis-" + this['id']).children().each(function (i) {
434
- if ($(this).attr('class') == 'domain') {
435
- //$(this).attr("display", "none");
436
- }
437
- console.log("Got element " + $(this).attr('class'));
438
- });
439
- */
440
- }
441
-
442
- /**
443
- * Create the 'X' axis in a new svg group
444
- * - dataLayer : the data for the layer format
445
- * - xAxisGraphGroup the group where this axis will be attached to
446
- * - xAxisHeightTick the height of the ticks
447
- */
448
- this.createXAxis = function (xAxisGraphGroup, xAxisHeightTick) {
449
-
450
- var scaleX = this.getScaleDate();
451
- var xAxis = d3.svg.axis().scale(scaleX).tickSize(-xAxisHeightTick).tickSubdivide(true);
452
- xAxisGraphGroup.append("svg:g")
453
- .attr("class", "x axis")
454
- .call(xAxis);
455
- }
456
-
457
-
458
- /**
459
- * Add the cirles for each point in the graph line
460
- *
461
- * This is used for both stacked and non stacked lines
462
- */
463
- this.addCirclesForGraph = function (circleGroup, lineId, dataX, dataY, scaleX, scaleY, lineColor) {
464
-
465
- var nodes = circleGroup.selectAll("g")
466
- .data(dataY)
467
- .enter();
468
-
469
- nodes.append("svg:circle")
470
- .attr("id", function (d, i) {
471
- return "circle-" + lineId + "-" + i;
472
- })
473
- .attr("cx", function (d, i) {
474
- return scaleX(new Date(dataX[i]));
475
- })
476
- .attr("cy", function (d, i) {
477
- return scaleY(d);
478
- })
479
- .attr("r", 3.5)
480
- .attr("fill", lineColor)
481
- .attr("value", function (d, i) {
482
- return d;
483
- });
484
- }
485
-
486
- this.addOverlayForGraph = function (circleGroup, lineId, dataX, dataY, scaleX, scaleY) {
487
-
488
- var myself = this;
489
-
490
- var nodes = circleGroup.selectAll("g")
491
- .data(dataY)
492
- .enter()
493
- .append("svg:g");
494
-
495
- nodes.append("svg:rect")
496
- .attr("id", function (d, i) {
497
- return "rect-" + lineId + "-" + i;
498
- })
499
- .attr("x", function (d, i) {
500
- return scaleX(new Date(dataX[i]));
501
- })
502
- .attr("y", function (d, i) {
503
- return scaleY(d);
504
- })
505
- .attr("width", 140)
506
- .attr("height", 50)
507
- .attr("rx", 5)
508
- .attr("ry", 5)
509
- .attr("display", "none")
510
- .style("fill", function (d, i) {
511
- return "#222";
512
- })
513
- .attr("transform", 'translate(10,-30)');
514
-
515
-
516
- nodes.append("svg:text")
517
- .attr("id", function (d, i) {
518
- return "text-" + lineId + "-" + i + "-1";
519
- })
520
- .attr("x", function (d, i) {
521
- return scaleX(new Date(dataX[i]));
522
- })
523
- .attr("y", function (d, i) {
524
- return scaleY(d);
525
- })
526
- .attr("fill", "#bbb")
527
- .attr("display", "none")
528
- .text(function (d, i) {
529
- return "Date = " + myself.formatDate(new Date(dataX[i]));
530
- })
531
- .attr("transform", 'translate(30,-10)');
532
-
533
- nodes.append("svg:text")
534
- .attr("id", function (d, i) {
535
- return "text-" + lineId + "-" + i + "-2";
536
- })
537
- .attr("x", function (d, i) {
538
- return scaleX(new Date(dataX[i]));
539
- })
540
- .attr("y", function (d, i) {
541
- return scaleY(d);
542
- })
543
- .attr("fill", "#bbb")
544
- .attr("display", "none")
545
- .text(function (d, i) {
546
- return "Value = " + myself.numberWithCommas(d);
547
- })
548
- .attr("transform", 'translate(30, 10)');
549
- }
550
-
551
-
552
- this.numberWithCommas = function (x) {
553
- var parts = x.toString().split(".");
554
- parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
555
- return parts.join(".");
556
- }
557
-
558
- /**
559
- * Extract the 'x' or 'y' from dataLyer format where each entry if of the form:
560
- * - attr is either the 'x' or 'y'
561
- * - dataLayer : the data for a given layer
562
- * E.g:
563
- * "name": "crescendo",
564
- * "values": [
565
- * { "x": "2010-07-08", "y": 0},
566
- * ....
567
- */
568
- this.extractKeyOrValueFromDataLayer = function (dataLayer, attr) {
569
- var result = [];
570
- for (var i = 0; i < dataLayer.values.length; i++) {
571
- result.push(dataLayer.values[i][attr])
572
- }
573
- return result;
574
- }
575
-
576
-
577
- /**
578
- * Add on the path the name of the line -- not used anymore as we are using external labels
579
- */
580
- this.addPathLabel = function (graph, lineId, positionPercent) {
581
- graph.append("svg:g")
582
- .append("text")
583
- .attr("font-size", "15")
584
- .append("svg:textPath")
585
- .attr("xlink:href", "#path-" + lineId)
586
- .attr("startOffset", positionPercent)
587
- .text(function (d) {
588
- return lineId;
589
- });
590
- }
591
-
592
-
593
- /**
594
- * Create a new group for the circles-- no translations needed
595
- */
596
- this.createCircleGroup = function (lineId) {
597
- return this.graphCanvas.append("svg:g")
598
- .attr("id", "circles-" + lineId);
599
- }
600
-
601
- this.createOverlayGroup = function (lineId) {
602
- return this.graphCanvas.append("svg:g")
603
- .attr("id", "overlay-" + lineId);
604
- }
605
-
606
- /**
607
- * Given a colorMap, extract the k-ieme color
608
- *
609
- * The colormap are standard d3 colormap which switch to new color every 4 colors;
610
- * in order to maximize the difference among colors we first get colors that are far apart
611
- *
612
- */
613
- this.getColor = function (k) {
614
- var div = Math.floor(k / 4);
615
- var mod = k % 4;
616
- var value = div + 4 * mod;
617
- return this.colorMap[value];
618
- }
619
-
620
- /**
621
- * Create the color map from the d3 palette
622
- */
623
- this.createColorMap = function () {
624
- var colorMap = {}
625
- for (var i = 0; i < 20; i++) {
626
- colorMap[i] = this.palette(i);
627
- }
628
- return colorMap;
629
- }
630
-
631
- this.addLegend = function () {
632
-
633
- this.graphCanvas.append("svg:text")
634
- .attr("class", "title")
635
- .attr("x", (this.width - this.title.length ) / 2)
636
- .attr("y", -30)
637
- .text(this.title);
638
-
639
- var legend = this.graphCanvas.append("g")
640
- .attr("class", "legend")
641
- .attr("height", 100)
642
- .attr("width", 200)
643
- .attr('transform', 'translate(+80,+0)')
644
-
645
-
646
- var myself = this;
647
- legend.selectAll('rect')
648
- .data(this.data)
649
- .enter()
650
- .append("rect")
651
- .attr("id", function (d, i) {
652
- return "ts-legend-" + myself.data[i]['id'];
653
- })
654
- .attr("x", this.width - 65)
655
- .attr("y", function (d, i) {
656
- return i * 20;
657
- })
658
- .attr("width", 11)
659
- .attr("height", 11)
660
- .attr("rx", 3)
661
- .attr("ry", 3)
662
- .style("fill", function (d, i) {
663
- var color = myself.getColor(i);
664
- return color;
665
- })
666
-
667
- legend.selectAll('text')
668
- .data(this.data)
669
- .enter()
670
- .append("text")
671
- .attr("x", this.width - 52)
672
- .attr("y", function (d, i) {
673
- return i * 20 + 9;
674
- })
675
- .text(function (d, i) {
676
- var text = d.name;
677
- var displayText = text;
678
- if (text.length > 25) {
679
- displayText = text.substring(0,25) + "...";
680
- }
681
- return displayText;
682
- });
683
- }
684
-
685
- this.addOnMouseHandlers = function () {
686
- this.addMouseOverCircleForValue();
687
- this.addMouseLegend();
688
- }
689
-
690
- /**
691
- * Attach handlers to all circles so as to display value
692
- *
693
- * Note that this will attach for all graphs-- not only the one attached to that objec
694
- */
695
- this.addMouseOverCircleForValue = function () {
696
-
697
- $('circle').each(function (i) {
698
-
699
- var textId1 = $(this).attr("id").replace("circle", "text") + "-1";
700
- var textId2 = $(this).attr("id").replace("circle", "text") + "-2";
701
- var rectId = $(this).attr("id").replace("circle", "rect");
702
-
703
- var circleText1 = $('#'.concat(textId1));
704
- var circleText2 = $('#'.concat(textId2));
705
- var circleRect = $('#'.concat(rectId));
706
-
707
- $(this).hover(function () {
708
- circleRect.show();
709
- circleText1.show();
710
- circleText2.show();
711
- }, function () {
712
- setTimeout(
713
- function () {
714
- circleRect.hide();
715
- circleText1.hide();
716
- circleText2.hide();
717
- }, 100);
718
-
719
- });
720
- });
721
- }
722
-
723
-
724
- this.performActionOnMouseHoverLegend = function () {
725
- }
726
-
727
- /* Build and save colorMap */
728
- this.colorMap = this.createColorMap();
729
-
730
- this.data = this.addDataId();
731
- this.id = (Math.random() + 1).toString(36).substring(7);
732
-
733
- }
734
-
735
- /**
736
- * KBLayersGraph : Inherits KBTimeSeriesBase abd offers specifics for layered graphs
737
- */
738
- killbillGraph.KBLayersGraph = function (graphCanvas, title, data, width, heigth, palette) {
739
-
740
- killbillGraph.KBTimeSeriesBase.call(this, graphCanvas, title, data, width, heigth, palette);
741
-
742
-
743
- /**
744
- * Create the area function that defines for each point in the stack graph
745
- * its x, y0 (offest from previous stacked graph) and y position
746
- */
747
- this.createLayerArea = function (scaleX, scaleY) {
748
- var area = d3.svg.area()
749
- .x(function (d) {
750
- return scaleX(new Date(d.x));
751
- })
752
- .y0(function (d) {
753
- return scaleY(d.y0);
754
- })
755
- .y1(function (d) {
756
- return scaleY(d.y + d.y0);
757
- });
758
- return area;
759
- }
760
-
761
- /**
762
- * Create the 'y' scale for the stack graph
763
- *
764
- * Extract min/max for each x value across all layers
765
- *
766
- */
767
- this.getLayerScaleValue = function () {
768
-
769
- var tmp = [];
770
- for (var i = 0; i < this.data.length; i++) {
771
- tmp.push(this.data[i].values)
772
- }
773
-
774
- var sumValues = [];
775
- for (var i = 0; i < tmp[0].length; i++) {
776
- var max = 0;
777
- for (var j = 0; j < tmp.length; j++) {
778
- max = max + tmp[j][i].y;
779
- }
780
- sumValues.push(max);
781
- }
782
- var minValue = 0;
783
- var maxValue = 0;
784
- for (var i = 0; i < sumValues.length; i++) {
785
- if (sumValues[i] < minValue) {
786
- minValue = sumValues[i];
787
- }
788
- if (sumValues[i] > maxValue) {
789
- maxValue = sumValues[i];
790
- }
791
- }
792
- if (minValue > 0) {
793
- minValue = 0;
794
- }
795
- return d3.scale.linear().domain([minValue, maxValue]).range([heigth, 0]);
796
- }
797
-
798
-
799
- /**
800
- * All all layers on the graph
801
- */
802
- this.addLayers = function (stack, area, dataLayers) {
803
-
804
- var dataLayerStack = stack(dataLayers);
805
-
806
- var currentObj = this;
807
-
808
- this.graphCanvas.selectAll("path")
809
- .data(dataLayerStack)
810
- .enter()
811
- .append("path")
812
- .style("fill",function (d, i) {
813
- return currentObj.getColor(i);
814
- }).attr("d", function (d) {
815
- return area(d.values);
816
- })
817
- .attr("id", function (d) {
818
- return "path-" + d.id;
819
- });
820
- }
821
-
822
- this.draw = function () {
823
- this.addLegend();
824
- this.drawStackLayers();
825
- this.addOnMouseHandlers();
826
- }
827
-
828
- /**
829
- * Draw all layers-- calls previous function addLayers
830
- * It will create its Y axis
831
- */
832
- this.drawStackLayers = function () {
833
-
834
- var scaleX = this.getScaleDate();
835
- var scaleY = this.getLayerScaleValue();
836
-
837
- var stack = d3.layout.stack()
838
- .offset("zero")
839
- .values(function (d) {
840
- return d.values;
841
- });
842
-
843
- var area = this.createLayerArea(scaleX, scaleY);
844
-
845
- this.addLayers(stack, area, this.data);
846
-
847
- var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
848
- var dataY0 = null;
849
- for (var i = 0; i < this.data.length; i++) {
850
-
851
- var circleGroup = this.createCircleGroup(this.data[i]['id']);
852
- var dataY = this.extractKeyOrValueFromDataLayer(this.data[i], 'y');
853
- if (dataY0) {
854
- for (var k = 0; k < dataY.length; k++) {
855
- dataY[k] = dataY[k] + dataY0[k];
856
- }
857
- }
858
- this.addCirclesForGraph(circleGroup, this.data[i]['id'], dataX, dataY, scaleX, scaleY, this.getColor(i));
859
- dataY0 = dataY;
860
- }
861
-
862
- this.createYAxis(scaleY);
863
-
864
- dataY0 = null;
865
- for (var i = 0; i < this.data.length; i++) {
866
- var circleGroup = this.createOverlayGroup(this.data[i]['id']);
867
- var dataY = this.extractKeyOrValueFromDataLayer(this.data[i], 'y');
868
- if (dataY0) {
869
- for (var k = 0; k < dataY.length; k++) {
870
- dataY[k] = dataY[k] + dataY0[k];
871
- }
872
- }
873
- this.addOverlayForGraph(circleGroup, this.data[i]['id'], dataX, dataY, scaleX, scaleY);
874
- dataY0 = dataY;
875
- }
876
-
877
- }
878
-
879
- this.addMouseLegend = function () {
880
-
881
- var myself = this;
882
- $('rect').each(function (i) {
883
-
884
- var curLegendId = $(this).attr("id");
885
- if (curLegendId === undefined || curLegendId.substring(0, 10) != "ts-legend-") {
886
- return;
887
- }
888
-
889
- var pathId = $(this).attr("id").replace("ts-legend", "path");
890
- var path = $("#" + pathId);
891
-
892
- var otherPaths = new Array();
893
- var otherCircles = new Array();
894
- for (var i = 0; i < myself.data.length; i++) {
895
- if ("path-" + myself.data[i]['id'] != pathId) {
896
- var curPath = $("#path-" + myself.data[i]['id']);
897
- otherPaths.push(curPath);
898
-
899
- var curCircle = $("#circles-" + myself.data[i]['id']);
900
- otherCircles.push(curCircle);
901
- }
902
- }
903
-
904
- var myLegendRect = $(this);
905
- $(this).hover(function () {
906
- for (var i = 0; i < otherPaths.length; i++) {
907
- otherPaths[i].attr("opacity", 0.1);
908
- otherCircles[i].attr("opacity", 0);
909
- }
910
-
911
- myLegendRect.attr("width", 15)
912
- .attr("height", 15)
913
- .attr('transform', 'translate(-3,-3)');
914
- }, function () {
915
- setTimeout(
916
- function () {
917
-
918
- for (var i = 0; i < otherPaths.length; i++) {
919
- otherPaths[i].attr("opacity", 1.0);
920
- otherCircles[i].attr("opacity", 1.0);
921
- }
922
- myLegendRect.attr("width", 11)
923
- .attr("height", 11)
924
- .attr('transform', 'translate(0,0)');
925
- }, 100);
926
- });
927
- });
928
- }
929
- }
930
- killbillGraph.KBLayersGraph.prototype = Object.create(killbillGraph.KBTimeSeriesBase.prototype);
931
-
932
-
933
- /**
934
- * KBLinesGraph : Inherits KBTimeSeriesBase abd offers specifics for layered graphs
935
- */
936
- killbillGraph.KBLinesGraph = function (graphCanvas, title, data, width, heigth, palette) {
937
-
938
- killbillGraph.KBTimeSeriesBase.call(this, graphCanvas, title, data, width, heigth, palette);
939
-
940
- /**
941
- * Create the 'y' scale for line graphs (non stacked)
942
- */
943
- this.getScaleValue = function () {
944
-
945
- var dataYs = [];
946
- for (var k = 0; k < this.data.length; k++) {
947
- var dataY = this.extractKeyOrValueFromDataLayer(this.data[k], 'y');
948
- dataYs.push(dataY);
949
- }
950
-
951
- var minValue = 0;
952
- var maxValue = 0;
953
- for (var i = 0; i < dataYs.length; i++) {
954
- for (var j = 0; j < dataYs[i].length; j++) {
955
- if (dataYs[i][j] < minValue) {
956
- minValue = dataYs[i][j];
957
- }
958
- if (dataYs[i][j] > maxValue) {
959
- maxValue = dataYs[i][j];
960
- }
961
- }
962
- }
963
- if (minValue > 0) {
964
- minValue = 0;
965
- }
966
- return d3.scale.linear().domain([minValue, maxValue]).range([this.heigth, 0]);
967
- }
968
-
969
- /**
970
- * Add the svg line for this data (dataX, dataY)
971
- */
972
- this.addLine = function (dataY, scaleX, scaleY, lineColor, lineId) {
973
-
974
- var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
975
- this.graphCanvas.selectAll("path.line")
976
- .data([dataY])
977
- .enter()
978
- .append("svg:path")
979
- .attr("stroke-width", 1.5)
980
- .attr("d", d3.svg.line()
981
- .x(function (d, i) {
982
- return scaleX(new Date(dataX[i]));
983
- })
984
- .y(function (d) {
985
- return scaleY(d);
986
- }))
987
- .attr("id", "path-" + lineId)
988
- .style("stroke", lineColor);
989
- }
990
-
991
- this.draw = function () {
992
- this.addLegend();
993
- this.drawLines();
994
- this.addOnMouseHandlers();
995
- }
996
-
997
- /**
998
- * Draw all lines
999
- * It will create its Y axis
1000
- */
1001
- this.drawLines = function () {
1002
-
1003
- var scaleX = this.getScaleDate();
1004
- var scaleY = this.getScaleValue();
1005
-
1006
- for (var k = 0; k < this.data.length; k++) {
1007
- var dataY = this.extractKeyOrValueFromDataLayer(this.data[k], 'y');
1008
- this.addLine(dataY, scaleX, scaleY, this.getColor(k), this.data[k]['id']);
1009
- }
1010
-
1011
- for (var k = 0; k < this.data.length; k++) {
1012
- var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
1013
- var dataY = this.extractKeyOrValueFromDataLayer(this.data[k], 'y');
1014
- var lineId = this.data[k]['id']
1015
- var circleGroup = this.createCircleGroup(lineId);
1016
- this.addCirclesForGraph(circleGroup, lineId, dataX, dataY, scaleX, scaleY, this.getColor(k));
1017
- }
1018
-
1019
- this.createYAxis(scaleY);
1020
-
1021
- for (var k = 0; k < this.data.length; k++) {
1022
- var dataX = this.extractKeyOrValueFromDataLayer(this.data[0], 'x');
1023
- var dataY = this.extractKeyOrValueFromDataLayer(this.data[k], 'y');
1024
- var lineId = this.data[k]['id']
1025
- var circleGroup = this.createOverlayGroup(lineId);
1026
- this.addOverlayForGraph(circleGroup, lineId, dataX, dataY, scaleX, scaleY);
1027
-
1028
- }
1029
- }
1030
-
1031
- this.addMouseLegend = function () {
1032
-
1033
- $('rect').each(function (i) {
1034
-
1035
- var curLegendId = $(this).attr("id");
1036
- if (curLegendId === undefined || curLegendId.substring(0, 10) != "ts-legend-") {
1037
- return;
1038
- }
1039
-
1040
- var pathId = $(this).attr("id").replace("ts-legend", "path");
1041
- var path = $("#" + pathId);
1042
-
1043
- var myLegendRect = $(this);
1044
- $(this).hover(function () {
1045
- path.attr("stroke-width", 3);
1046
- myLegendRect.attr("width", 15)
1047
- .attr("height", 15)
1048
- .attr('transform', 'translate(-3,-3)');
1049
- }, function () {
1050
- setTimeout(
1051
- function () {
1052
- path.attr("stroke-width", 1.5);
1053
- myLegendRect.attr("width", 11)
1054
- .attr("height", 11)
1055
- .attr('transform', 'translate(0,0)');
1056
- }, 100);
1057
- });
1058
- });
1059
- }
1060
- }
1061
-
1062
- killbillGraph.KBLinesGraph.prototype = Object.create(killbillGraph.KBTimeSeriesBase.prototype);
1063
-
1064
-
1065
- killbillGraph.GraphStructure = function () {
1066
-
1067
- /**
1068
- * Setup the main divs for both legend and main charts
1069
- *
1070
- * It is expected to have a mnain div anchir on the html with id = 'chartAnchor'.
1071
- */
1072
- this.setupDomStructure = function () {
1073
-
1074
- var $divChart = $('<div id="charts" class="charts">');
1075
- var $spanChart = $('<span id="chartId" class="charts"></span>');
1076
- $divChart.prepend($spanChart);
1077
-
1078
- $("#chartAnchor").append($divChart);
1079
- }
1080
-
1081
-
1082
- /**
1083
- * Create initial canvas on which to draw all graphs
1084
- */
1085
- this.createCanvas = function (m, w, h) {
1086
-
1087
- // See http://tutorials.jenkov.com/svg/svg-viewport-view-box.html
1088
- var canvasViewBoxWidth = $('#chartAnchor').width();
1089
- var canvasViewPortWidth = canvasViewBoxWidth;
1090
- var canvasHeight = h + m[0] + m[2];
1091
-
1092
- return d3.select("#chartId")
1093
- .append("svg:svg")
1094
- .attr("width", canvasViewPortWidth)
1095
- .attr("height", canvasHeight)
1096
- .attr("viewBox", m[3] + " 0 " + canvasViewBoxWidth + " " + canvasHeight)
1097
- .attr("preserveAspectRatio", "xMinYMin meet");
1098
- }
1099
-
1100
-
1101
- /**
1102
- * Create a new group and make the translation to leave room for margins
1103
- */
1104
- this.createCanvasGroup = function (canvas, translateX, translateY) {
1105
- return canvas
1106
- .append("svg:g")
1107
- .attr("transform", "translate(0," + translateY + ")");
1108
-
1109
- }
1110
- };
1111
-
1112
- }(window.killbillGraph = window.killbillGraph || {}, jQuery)
1113
- )
1114
- ;