kanaui 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +41 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/images/back_disabled.png +0 -0
  6. data/app/assets/images/back_enabled.png +0 -0
  7. data/app/assets/images/back_enabled_hover.png +0 -0
  8. data/app/assets/images/favicon.ico +0 -0
  9. data/app/assets/images/forward_disabled.png +0 -0
  10. data/app/assets/images/forward_enabled.png +0 -0
  11. data/app/assets/images/forward_enabled_hover.png +0 -0
  12. data/app/assets/images/sort_asc.png +0 -0
  13. data/app/assets/images/sort_asc_disabled.png +0 -0
  14. data/app/assets/images/sort_both.png +0 -0
  15. data/app/assets/images/sort_desc.png +0 -0
  16. data/app/assets/images/sort_desc_disabled.png +0 -0
  17. data/app/assets/javascripts/application.js +19 -0
  18. data/app/assets/javascripts/kanaui/dashboard.js +131 -0
  19. data/app/assets/javascripts/kanaui/killbill.js +1114 -0
  20. data/app/assets/javascripts/kanaui/purl.js +271 -0
  21. data/app/assets/javascripts/kanaui/reports.dataTables.js +31 -0
  22. data/app/assets/javascripts/kanaui/reports.graphs.js +190 -0
  23. data/app/assets/javascripts/kanaui/reports.js +201 -0
  24. data/app/assets/javascripts/kanaui/reports.urls.js +44 -0
  25. data/app/assets/javascripts/kanaui/tests.js +2 -0
  26. data/app/assets/stylesheets/application.css +8 -0
  27. data/app/assets/stylesheets/bootstrap_and_overrides.css +7 -0
  28. data/app/assets/stylesheets/kanaui/dashboard.css +121 -0
  29. data/app/assets/stylesheets/kanaui/tests.css +4 -0
  30. data/app/controllers/kanaui/dashboard_controller.rb +59 -0
  31. data/app/controllers/kanaui/engine_controller.rb +18 -0
  32. data/app/helpers/kanaui/application_helper.rb +6 -0
  33. data/app/helpers/kanaui/dashboard_helper.rb +28 -0
  34. data/app/models/kanaui/dashboard.rb +5 -0
  35. data/app/views/kanaui/dashboard/index.html.erb +48 -0
  36. data/app/views/kanaui/layouts/kanaui_application.html.erb +43 -0
  37. data/config/locales/en.bootstrap.yml +18 -0
  38. data/config/routes.rb +10 -0
  39. data/lib/kanaui/engine.rb +7 -0
  40. data/lib/kanaui/version.rb +3 -0
  41. data/lib/kanaui.rb +22 -0
  42. data/lib/tasks/kanaui_tasks.rake +4 -0
  43. data/test/dummy/README.rdoc +261 -0
  44. data/test/dummy/Rakefile +7 -0
  45. data/test/dummy/app/controllers/application_controller.rb +3 -0
  46. data/test/dummy/app/helpers/application_helper.rb +2 -0
  47. data/test/dummy/config/application.rb +64 -0
  48. data/test/dummy/config/boot.rb +10 -0
  49. data/test/dummy/config/database.yml +25 -0
  50. data/test/dummy/config/environment.rb +5 -0
  51. data/test/dummy/config/environments/development.rb +39 -0
  52. data/test/dummy/config/environments/production.rb +67 -0
  53. data/test/dummy/config/environments/test.rb +37 -0
  54. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  55. data/test/dummy/config/initializers/inflections.rb +15 -0
  56. data/test/dummy/config/initializers/killbill_client.rb +4 -0
  57. data/test/dummy/config/initializers/mime_types.rb +5 -0
  58. data/test/dummy/config/initializers/secret_token.rb +7 -0
  59. data/test/dummy/config/initializers/session_store.rb +8 -0
  60. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  61. data/test/dummy/config/locales/en.yml +5 -0
  62. data/test/dummy/config/routes.rb +4 -0
  63. data/test/dummy/config.ru +4 -0
  64. data/test/dummy/db/development.sqlite3 +0 -0
  65. data/test/dummy/db/production.sqlite3 +0 -0
  66. data/test/dummy/db/test.sqlite3 +0 -0
  67. data/test/dummy/log/test.log +6 -0
  68. data/test/dummy/public/404.html +26 -0
  69. data/test/dummy/public/422.html +26 -0
  70. data/test/dummy/public/500.html +25 -0
  71. data/test/dummy/public/favicon.ico +0 -0
  72. data/test/dummy/script/rails +6 -0
  73. data/test/dummy/tmp/cache/assets/C49/370/sprockets%2Fe0912451e460967cb546e2ac00632667 +0 -0
  74. data/test/dummy/tmp/cache/assets/C59/3D0/sprockets%2F22311d65dd6f27979f0144f4e9603762 +0 -0
  75. data/test/dummy/tmp/cache/assets/C5C/180/sprockets%2F6b609032785fe05974b318057b808dc9 +0 -0
  76. data/test/dummy/tmp/cache/assets/C60/440/sprockets%2F9e401779c0877cf93949e930228032ac +0 -0
  77. data/test/dummy/tmp/cache/assets/C61/8F0/sprockets%2Fe8f725654ee48124c086d488379340e0 +0 -0
  78. data/test/dummy/tmp/cache/assets/C70/FA0/sprockets%2F43c86018a096a0a65a0905da2b709455 +0 -0
  79. data/test/dummy/tmp/cache/assets/C7A/180/sprockets%2F19b994259f9c7b6f2879718c3275f359 +0 -0
  80. data/test/dummy/tmp/cache/assets/C8A/AB0/sprockets%2Fdc2f837cd27313996699c80342b07e04 +0 -0
  81. data/test/dummy/tmp/cache/assets/C8C/2E0/sprockets%2F8ef8759ba58c509567a8444201c19b24 +0 -0
  82. data/test/dummy/tmp/cache/assets/C97/750/sprockets%2F34d94639228f869665db78f42ca3118e +0 -0
  83. data/test/dummy/tmp/cache/assets/CA2/C20/sprockets%2F1d186d79051934c85a0594a610a1ab6b +0 -0
  84. data/test/dummy/tmp/cache/assets/CB3/D00/sprockets%2F6e18e78b81412e49d335a04a68f16c35 +0 -0
  85. data/test/dummy/tmp/cache/assets/CBF/560/sprockets%2Fe944ecb8026957a176e29a392a069f46 +0 -0
  86. data/test/dummy/tmp/cache/assets/CC5/C70/sprockets%2Fc4dacfb400c37f046545be4501065313 +0 -0
  87. data/test/dummy/tmp/cache/assets/CCA/090/sprockets%2Fc04069b3046b34950b843e32bbdf2a60 +0 -0
  88. data/test/dummy/tmp/cache/assets/CCF/750/sprockets%2F0512cc5ddae07c2bf0861332668640e4 +0 -0
  89. data/test/dummy/tmp/cache/assets/CCF/FC0/sprockets%2F09d578e3a00293a0e60b65f01ed551c4 +0 -0
  90. data/test/dummy/tmp/cache/assets/CD0/E60/sprockets%2F34b252a436ea97340eb6a33353ec7c60 +0 -0
  91. data/test/dummy/tmp/cache/assets/CD9/960/sprockets%2F9faf733d3c943879081f001120abfc09 +0 -0
  92. data/test/dummy/tmp/cache/assets/CDA/100/sprockets%2F1421707c6cd2bf4619c218d15c5ed449 +0 -0
  93. data/test/dummy/tmp/cache/assets/CDD/750/sprockets%2F51c5d9b965a7a23e2703b0d8c74d1358 +0 -0
  94. data/test/dummy/tmp/cache/assets/CE3/750/sprockets%2Fc54b480c74716d0ff447cc13f9471f25 +0 -0
  95. data/test/dummy/tmp/cache/assets/CE6/EF0/sprockets%2F358f2a7450053d2e7e60d219787ede7a +0 -0
  96. data/test/dummy/tmp/cache/assets/CF1/600/sprockets%2F758e0f59cc7d377813b72e9f17109c2d +0 -0
  97. data/test/dummy/tmp/cache/assets/CF4/4C0/sprockets%2F66ae774f6007684bc3ce544d15a879f9 +0 -0
  98. data/test/dummy/tmp/cache/assets/CF4/910/sprockets%2F47246555fd8dde5f73d513149bd7d495 +0 -0
  99. data/test/dummy/tmp/cache/assets/CFB/570/sprockets%2F70a944fdac8557dc70819ec989604e95 +0 -0
  100. data/test/dummy/tmp/cache/assets/D04/E10/sprockets%2F32fb56262b67a8013f6d1fe3bc45263b +0 -0
  101. data/test/dummy/tmp/cache/assets/D0A/8E0/sprockets%2Faaccd4041294c01f9d52b59d90e61649 +0 -0
  102. data/test/dummy/tmp/cache/assets/D0D/B20/sprockets%2F83c8c6d067f200b94289a27ae217cd2d +0 -0
  103. data/test/dummy/tmp/cache/assets/D16/140/sprockets%2F71765207ccd3c89388ba290d1ead7e73 +0 -0
  104. data/test/dummy/tmp/cache/assets/D16/610/sprockets%2Fc45f9a1f74ae89810e305307c8c2c98a +0 -0
  105. data/test/dummy/tmp/cache/assets/D1A/1A0/sprockets%2Fa9e56834936cc1d4e033ed75459f7ac1 +0 -0
  106. data/test/dummy/tmp/cache/assets/D1D/880/sprockets%2Fe29808c67d12f86b83bb3f545e0685cc +0 -0
  107. data/test/dummy/tmp/cache/assets/D28/2F0/sprockets%2F0c216cfb363ad008c4dd27e21e286e20 +0 -0
  108. data/test/dummy/tmp/cache/assets/D29/350/sprockets%2Fa251294b0bc12651ee0dd16fb427ed08 +0 -0
  109. data/test/dummy/tmp/cache/assets/D2E/DD0/sprockets%2F4aab8120d7fdda3c3101795fc937044a +0 -0
  110. data/test/dummy/tmp/cache/assets/D33/E20/sprockets%2F01e77b5ab7c8f25d7e007e42d210e60f +0 -0
  111. data/test/dummy/tmp/cache/assets/D3A/5A0/sprockets%2F3205c58e5fc648bb07247dd6fc1aa058 +0 -0
  112. data/test/dummy/tmp/cache/assets/D3B/B20/sprockets%2Ff25927a9a796c888c93f9892db4ded57 +0 -0
  113. data/test/dummy/tmp/cache/assets/D3C/BA0/sprockets%2F499c3460b0df4ea435a25d1fa3776ae7 +0 -0
  114. data/test/dummy/tmp/cache/assets/D3D/FD0/sprockets%2F6cd2d6e10e5aa7649192213d87ae1dd8 +0 -0
  115. data/test/dummy/tmp/cache/assets/D40/880/sprockets%2Fa65183bd762da798d82ca16b2384ae3e +0 -0
  116. data/test/dummy/tmp/cache/assets/D4A/220/sprockets%2Fc3fa86c7916fd5cf3448a46c2701d48f +0 -0
  117. data/test/dummy/tmp/cache/assets/D4A/710/sprockets%2F19bdf938259f801b6bc26fda54907c3f +0 -0
  118. data/test/dummy/tmp/cache/assets/D50/880/sprockets%2F6491c413570f9b5ee67b43dae6fb988b +0 -0
  119. data/test/dummy/tmp/cache/assets/D5C/600/sprockets%2Fdd005cf1e2da44e307bfb30f6e711177 +0 -0
  120. data/test/dummy/tmp/cache/assets/D62/6B0/sprockets%2Feb40cf55d9fba5bb18801041e5a755b4 +0 -0
  121. data/test/dummy/tmp/cache/assets/D65/7F0/sprockets%2Fc431bdd5c16d69e6d48b025be24e07b3 +0 -0
  122. data/test/dummy/tmp/cache/assets/D6D/170/sprockets%2Fe7474ced980c0a511ab3ed94ca66507f +0 -0
  123. data/test/dummy/tmp/cache/assets/D6F/2F0/sprockets%2Ff83749b55e1a31b4bc71d8f10c9c8ed1 +0 -0
  124. data/test/dummy/tmp/cache/assets/D71/CA0/sprockets%2F2efce3b73b5a0834d61c30f3e88f943e +0 -0
  125. data/test/dummy/tmp/cache/assets/D71/D00/sprockets%2F8f431cf56efe254dc4ad2f39f1f05325 +0 -0
  126. data/test/dummy/tmp/cache/assets/D72/FC0/sprockets%2F0e88a2c64eef7d00ae16ec3d6587446b +0 -0
  127. data/test/dummy/tmp/cache/assets/D74/AA0/sprockets%2F2a2594863afdfb329cf44429c3f4cdb6 +0 -0
  128. data/test/dummy/tmp/cache/assets/D78/5F0/sprockets%2Fd779c87f1d25bc5e67e24be981ab005e +0 -0
  129. data/test/dummy/tmp/cache/assets/D7D/230/sprockets%2F0d9f8069a79511caa7b72d8f7e9ba3e6 +0 -0
  130. data/test/dummy/tmp/cache/assets/D7E/D40/sprockets%2Fbb1ee49d28b0f4826af433e66b99f68b +0 -0
  131. data/test/dummy/tmp/cache/assets/D7F/D40/sprockets%2F5df2d7d854a34efce561038973fea75e +0 -0
  132. data/test/dummy/tmp/cache/assets/D80/C00/sprockets%2F807575a6bca580997ea68b2849eeebab +0 -0
  133. data/test/dummy/tmp/cache/assets/D8D/640/sprockets%2F3cb906dcb26ce1bb8b148a9ec38151a0 +0 -0
  134. data/test/dummy/tmp/cache/assets/D8F/740/sprockets%2Fc28d091b6eec75f690312b1babac173e +0 -0
  135. data/test/dummy/tmp/cache/assets/D99/810/sprockets%2F40a77b8fdb5dbb4a8be79582930bac11 +0 -0
  136. data/test/dummy/tmp/cache/assets/D9B/590/sprockets%2F415dfd73bc9dcf11a3c443636a7cc85d +0 -0
  137. data/test/dummy/tmp/cache/assets/D9F/0E0/sprockets%2Fe225dd3abeea78368f1e094da4d6e038 +0 -0
  138. data/test/dummy/tmp/cache/assets/DA0/D50/sprockets%2Fbe08c987f77ac1cf011f2c295f64ae0e +0 -0
  139. data/test/dummy/tmp/cache/assets/DA0/DF0/sprockets%2F54a3fbb89812981e0ddd9f293ee0cca0 +0 -0
  140. data/test/dummy/tmp/cache/assets/DA2/9A0/sprockets%2Fc217dc63d5bb979ab458f9440b8aaeb5 +0 -0
  141. data/test/dummy/tmp/cache/assets/DAC/420/sprockets%2F8419ce35fe9f11d3ea2c9f8edb48a661 +0 -0
  142. data/test/dummy/tmp/cache/assets/DB0/FA0/sprockets%2Ff3e0e6737d4f66ef48581ed3f26cc2fc +0 -0
  143. data/test/dummy/tmp/cache/assets/DB7/B30/sprockets%2Fa35d57efd41de3d595e79db74d99a6a7 +0 -0
  144. data/test/dummy/tmp/cache/assets/DBD/520/sprockets%2F646b4ef551d2e6dbcafc30ec6a1a1921 +0 -0
  145. data/test/dummy/tmp/cache/assets/DC3/A40/sprockets%2Fec0ae4ff1ed6a72c0fc4ec564700195b +0 -0
  146. data/test/dummy/tmp/cache/assets/DCA/970/sprockets%2Fa5b14cb6e98a7b2f9dfdd413420d3be7 +0 -0
  147. data/test/dummy/tmp/cache/assets/DCD/570/sprockets%2Fdacdabcd4b599417317af696f0bc70f3 +0 -0
  148. data/test/dummy/tmp/cache/assets/DD0/630/sprockets%2F1a6f238c9a7e8609caef71fff2ad1a16 +0 -0
  149. data/test/dummy/tmp/cache/assets/DD4/F20/sprockets%2Fe280ef07c4f086afbc447e8a3df6e4e2 +0 -0
  150. data/test/dummy/tmp/cache/assets/DD5/460/sprockets%2F6670c88dab8f5cb0b36bd51caf58a87f +0 -0
  151. data/test/dummy/tmp/cache/assets/DD9/7A0/sprockets%2Fa913b7e4c6665cd4d75cd5e4efa46b8c +0 -0
  152. data/test/dummy/tmp/cache/assets/DE8/770/sprockets%2Fce3edfabfb12520c063ee21bd2ea1858 +0 -0
  153. data/test/dummy/tmp/cache/assets/DEA/910/sprockets%2F96c4857b4f859eef5c6bf113ecdb8ce9 +0 -0
  154. data/test/dummy/tmp/cache/assets/DEF/590/sprockets%2Fb8d8adfe3d233e16821faf0cbd03a09d +0 -0
  155. data/test/dummy/tmp/cache/assets/DF6/EA0/sprockets%2Fda2b70e5c0a5e5e5b2b8e4f3438aaf6f +0 -0
  156. data/test/dummy/tmp/cache/assets/E01/6A0/sprockets%2Feeec15d595b932dabdbb8f2fd66266b1 +0 -0
  157. data/test/dummy/tmp/cache/assets/E60/DF0/sprockets%2F7dced5bcfe17976cc5e41ffc1c33dbd2 +0 -0
  158. data/test/dummy/tmp/cache/assets/E8B/700/sprockets%2F3bd227fedfbfab5ede67a13feec45e14 +0 -0
  159. data/test/fixtures/kanaui/dashboards.yml +11 -0
  160. data/test/functional/kanaui/tests_controller_test.rb +9 -0
  161. data/test/integration/navigation_test.rb +10 -0
  162. data/test/kanaui_test.rb +7 -0
  163. data/test/test_helper.rb +15 -0
  164. data/test/unit/helpers/kanaui/tests_helper_test.rb +6 -0
  165. metadata +551 -0
@@ -0,0 +1,1114 @@
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) ? "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", "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 = w + m[1] + m[3];
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", "0 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(" + translateX + "," + translateY + ")");
1108
+
1109
+ }
1110
+ };
1111
+
1112
+ }(window.killbillGraph = window.killbillGraph || {}, jQuery)
1113
+ )
1114
+ ;