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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/kanaui/kiddo/axes.js +25 -25
- data/app/assets/javascripts/kanaui/kiddo/charts/line_chart.js +12 -7
- data/app/assets/javascripts/kanaui/kiddo/charts/utils/mouse_over.js +67 -39
- data/app/assets/javascripts/kanaui/kiddo/kiddo_initialize.js +19 -11
- data/app/assets/javascripts/kanaui/kiddo/renderer.js +7 -2
- data/app/assets/javascripts/kanaui/kiddo/settings.js +1 -1
- data/app/assets/stylesheets/kanaui/reports.css +1 -1
- data/app/controllers/kanaui/dashboard_controller.rb +82 -22
- data/app/controllers/kanaui/engine_controller.rb +10 -0
- data/app/controllers/kanaui/reports_controller.rb +60 -0
- data/app/helpers/kanaui/dashboard_helper.rb +28 -2
- data/app/views/kanaui/dashboard/index.html.erb +90 -15
- data/app/views/kanaui/reports/_form.html.erb +49 -0
- data/app/views/kanaui/reports/_reports_table.html.erb +60 -0
- data/app/views/kanaui/reports/edit.html.erb +10 -0
- data/app/views/kanaui/reports/index.html.erb +17 -0
- data/app/views/kanaui/reports/new.html.erb +10 -0
- data/config/routes.rb +5 -1
- data/lib/kanaui/version.rb +1 -1
- metadata +121 -121
- data/app/assets/javascripts/kanaui/killbill.js +0 -1114
- data/app/assets/javascripts/kanaui/purl.js +0 -271
- data/app/assets/javascripts/kanaui/reports.graphs.js +0 -190
- data/app/assets/javascripts/kanaui/reports.js +0 -201
- data/app/assets/javascripts/kanaui/reports.urls.js +0 -44
- data/app/assets/javascripts/kanaui/tests.js +0 -2
@@ -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
|
-
;
|