chart.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var App = require('app');
  19. App.ChartView = Em.View.extend({
  20. dateFormat:'dd/mm/yy',
  21. timeFormat:'h:m',
  22. w:900,
  23. p:30, // axis padding
  24. shift:30,
  25. ticksCount:10,
  26. pointsLimit:300,
  27. areaHeight:30, // px
  28. axis:false,
  29. x:false,
  30. y:false,
  31. init:function () {
  32. this._super();
  33. var renderer = this;
  34. this.x = d3.time.scale().domain([renderer.getMinDate({}), renderer.getMaxDate({})]).range([0, this.get('w')]);
  35. this.y = d3.scale.linear().domain([0, 50]).range([this.get('h'), 0]);
  36. this.axis = d3.svg.axis().orient("top").scale(this.x).ticks(this.get('ticksCount'));
  37. },
  38. h: function(){
  39. return this.get('p') + this.get('nodeAttributes').length * this.get('areaHeight'); //default: 160
  40. }.property('nodeAttributes', 'p'),
  41. activeH: function(){
  42. return this.get('nodeAttributes').length * this.get('areaHeight'); // default 160;
  43. }.property('h'),
  44. ruleHeight: function(){
  45. return this.get('nodeAttributes').length * this.get('areaHeight');
  46. }.property('nodeAttributes'),
  47. updateY: function(){
  48. this.y = d3.scale.linear().domain([0, 50]).range([this.get('h'), 0]);
  49. }.observes('h'),
  50. getMinDate:function (data) {
  51. if (data.length)
  52. return new Date(Date.parse(data[0]['date']));
  53. return new Date();
  54. },
  55. getMaxDate:function (data) {
  56. if (data.length)
  57. return new Date(Date.parse(data[data.length - 1]['date']));
  58. return new Date();
  59. },
  60. area:function () {
  61. var renderer = this;
  62. var area = d3.svg.area().x(function (d) {
  63. return renderer.x(renderer.getDate(d));
  64. });
  65. area.y1(function (d) {
  66. return renderer.get('h') - (renderer.get('h') - renderer.y(d[$(this).attr("getter")])) / renderer.get('koef');
  67. });
  68. area.y0(function (d) {
  69. return renderer.get('h');
  70. });
  71. return area;
  72. },
  73. line:function () {
  74. var renderer = this;
  75. var area = d3.svg.line().x(function (d) {
  76. return renderer.x(renderer.getDate(d));
  77. })
  78. .interpolate("basis");
  79. area.y(function (d) {
  80. return renderer.get('h');
  81. });
  82. return area;
  83. },
  84. /**
  85. * @todo: calculate this
  86. * coefficient of compression
  87. * @param shift
  88. * @return {Number}
  89. */
  90. koef:function () {
  91. // max value divide on area height;
  92. return 2 * (this.get('nodeAttributes').length + 1);
  93. }.property('h'),
  94. getDate:function (d) {
  95. return new Date(Date.parse(d.date));
  96. },
  97. dateTimeToDateObject:function (string) {
  98. var ren = this;
  99. return new Date($.datepicker.parseDateTime(ren.dateFormat, ren.timeFormat, string));
  100. },
  101. getDefaultShift:function () {
  102. return -1 * this.get('areaHeight') * (this.get('nodeAttributes').length - 1);
  103. },
  104. percentScaleXDefaultTranslate:function () {
  105. return this.w + 3
  106. },
  107. clearPlot: function(){
  108. d3.select(this.get('chartContainerSelector')).selectAll("*").remove();
  109. },
  110. drawPlot:function () {
  111. this.clearPlot();
  112. var renderer = this;
  113. this.x.domain([renderer.getMinDate({}), renderer.getMaxDate({})]);
  114. var rule = $('<div></div>').addClass("rule").css('height', renderer.get('ruleHeight')).mouseenter(function () { $(this).hide(); });
  115. $(this.get('chartContainerSelector')).prepend(rule);
  116. var vis = d3.select(this.get('chartContainerSelector'))
  117. .append("svg:svg")
  118. .attr("width", renderer.get('w') + 5)
  119. .attr("height", renderer.get('h'))
  120. .attr("rendererId", this.get('elementId'))
  121. .on("mousemove", function () {
  122. var area = d3.select(this).select("path.line");
  123. var d = area.data()[0];
  124. var x = d3.mouse(this)[0];
  125. var renderer = Em.View.views[d3.select(this).attr('rendererId')];
  126. var container = $(this).parent();
  127. var scale = renderer.x;
  128. // first move rule
  129. var rule = $(container).children("div.rule");
  130. rule.css("left", (168 + x) + "px"); // 168 - left container margin
  131. rule.show();
  132. x = x + 5; // some correction
  133. var selectedDate = scale.invert(x);
  134. // search date between this coordinates
  135. var prevVal = false;
  136. var nextVal = d[0];
  137. $.each(d, function (i, point) {
  138. if (renderer.getDate(point).getTime() <= selectedDate.getTime()) {
  139. prevVal = nextVal;
  140. nextVal = point;
  141. }
  142. });
  143. var len1 = Math.abs(x - scale(renderer.getDate(prevVal)));
  144. var len2 = Math.abs(x - scale(renderer.getDate(nextVal)));
  145. var clearing = 5;
  146. var pointToShow = false;
  147. // if the first point if closer
  148. if ((len1 < len2) && (len1 <= clearing)) {
  149. pointToShow = prevVal;
  150. } else if (len2 <= clearing) { // the second point is closer
  151. pointToShow = nextVal;
  152. }
  153. $.each(renderer.get('nodeAttributes'), function (i, v) {
  154. var value = !pointToShow ? "" : pointToShow[v] + "%";
  155. $(rule).children("div." + v).html(value);
  156. });
  157. });
  158. vis.append("svg:g")
  159. .attr("class", "axis")
  160. .attr("transform", "translate(0," + this.get('p') + ")")
  161. .call(renderer.axis);
  162. $.each(this.get('nodeAttributes'), function (i, v) {
  163. var element = $('<div></div>').addClass(v).addClass("stateValue").html("");
  164. rule.append(element);
  165. });
  166. var shift = this.getDefaultShift();
  167. vis.append("svg:path")
  168. .attr("class", "horizontal-line")
  169. .data([
  170. {}
  171. ])
  172. .attr("transform", "translate(0," + (shift - this.get('areaHeight')) + ")")
  173. .attr("d", renderer.line())
  174. .style("stroke", "#000");
  175. $.each(this.get('nodeAttributes'), function (i, v) {
  176. vis.append("svg:path").data([
  177. {}
  178. ])
  179. .attr("class", "line")
  180. .attr("getter", v)
  181. .attr("transform", "translate(0, " + shift + ")")
  182. .attr("d", renderer.area())
  183. .style("fill", function () {
  184. return "#31a354";
  185. });
  186. vis.append("svg:path")
  187. .attr("class", "horizontal-line")
  188. .data([
  189. {}
  190. ])
  191. .attr("transform", "translate(0," + shift + ")")
  192. .attr("d", renderer.line())
  193. .style("stroke", "#000");
  194. shift += renderer.get('areaHeight');
  195. });
  196. },
  197. getData:function (containerId) {
  198. return (d3.select(containerId + " path.line").data())[0];
  199. },
  200. drawChart:function () {
  201. var containerSel = this.get('chartContainerSelector');
  202. var data = this.get('data');
  203. while (data.length > this.get('pointsLimit')) {
  204. data.shift();
  205. }
  206. var renderer = this;
  207. var minDate = this.getMinDate(data);
  208. var maxDate = this.getMaxDate(data);
  209. this.x.domain([minDate, maxDate]);
  210. var ticks = data.length > 10 ? 10 : data.length;
  211. this.axis.scale(renderer.x).ticks(ticks);
  212. // remove dots axis
  213. $(containerSel + " svg g.axis g").remove();
  214. d3.select(containerSel + " svg g.axis")
  215. .call(this.axis);
  216. $.each(this.get('nodeAttributes'), function (i, v) {
  217. d3.select(containerSel + " path.line[getter='" + v + "']")
  218. .data([data])
  219. .transition()
  220. .attr("d", renderer.area());
  221. });
  222. // lines between charts
  223. $(containerSel + " path.horizontal-line").each(
  224. function (i, path) {
  225. d3.select(path).data([
  226. [
  227. {date:minDate},
  228. {date:maxDate}
  229. ]
  230. ]).attr("d", renderer.line());
  231. }
  232. );
  233. }
  234. });