Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / third_party / tvcm / src / tvcm / ui / line_chart.html
1 <!DOCTYPE html>
2 <!--
3 Copyright (c) 2014 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
6 -->
7 <link rel="import" href="/tvcm/range.html">
8 <link rel="import" href="/tvcm/ui/d3.html">
9 <link rel="import" href="/tvcm/ui/chart_base.html">
10 <link rel="import" href="/tvcm/ui/mouse_tracker.html">
11 <link rel="stylesheet" href="/tvcm/ui/line_chart.css">
12 <script>
13 'use strict';
14
15 tvcm.exportTo('tvcm.ui', function() {
16   var ChartBase = tvcm.ui.ChartBase;
17   var getColorOfKey = tvcm.ui.getColorOfKey;
18
19   function getSampleWidth(data, index, leftSide) {
20     var leftIndex, rightIndex;
21     if (leftSide) {
22       leftIndex = Math.max(index - 1, 0);
23       rightIndex = index;
24     } else {
25       leftIndex = index;
26       rightIndex = Math.min(index + 1, data.length - 1);
27     }
28     var leftWidth = data[index].x - data[leftIndex].x;
29     var rightWidth = data[rightIndex].x - data[index].x;
30     return leftWidth * 0.5 + rightWidth * 0.5;
31   }
32
33   /**
34    * @constructor
35    */
36   var LineChart = tvcm.ui.define('line-chart', ChartBase);
37
38   LineChart.prototype = {
39     __proto__: ChartBase.prototype,
40
41     decorate: function() {
42       ChartBase.prototype.decorate.call(this);
43       this.classList.add('line-chart');
44
45       this.brushedRange_ = new tvcm.Range();
46
47       this.xScale_ = d3.scale.linear();
48       this.yScale_ = d3.scale.linear();
49       d3.select(this.chartAreaElement)
50           .append('g')
51           .attr('id', 'brushes');
52       d3.select(this.chartAreaElement)
53           .append('g')
54           .attr('id', 'series');
55
56       this.addEventListener('mousedown', this.onMouseDown_.bind(this));
57     },
58
59     /**
60      * Sets the data array for the object
61      *
62      * @param {Array} data The data. Each element must be an object, with at
63      * least an x property. All other properties become series names in the
64      * chart.
65      */
66     set data(data) {
67       if (data.length == 0)
68         throw new Error('Data must be nonzero. Pass undefined.');
69
70       var keys;
71       if (data !== undefined) {
72         var d = data[0];
73         if (d.x === undefined)
74           throw new Error('Elements must have "x" fields');
75         keys = d3.keys(data[0]);
76         keys.splice(keys.indexOf('x'), 1);
77         if (keys.length == 0)
78           throw new Error('Elements must have at least one other field than X');
79       } else {
80         keys = undefined;
81       }
82       this.data_ = data;
83       this.seriesKeys_ = keys;
84
85       this.updateContents_();
86     },
87
88     // Note: range can only be set, not retrieved. It needs to be immutable
89     // or else odd data binding effects will result.
90     set brushedRange(range) {
91       this.brushedRange_.reset();
92       this.brushedRange_.addRange(range);
93       this.updateContents_();
94     },
95
96     computeBrushRangeFromIndices: function(indexA, indexB) {
97       var r = new tvcm.Range();
98       var leftIndex = Math.min(indexA, indexB);
99       var rightIndex = Math.max(indexA, indexB);
100       leftIndex = Math.max(0, leftIndex);
101       rightIndex = Math.min(this.data_.length - 1, rightIndex);
102       r.addValue(this.data_[leftIndex].x -
103           getSampleWidth(this.data_, leftIndex, true));
104       r.addValue(this.data_[rightIndex].x +
105           getSampleWidth(this.data_, rightIndex, false));
106       return r;
107     },
108
109     getLegendKeys_: function() {
110       if (this.seriesKeys_ &&
111           this.seriesKeys_.length > 1)
112         return this.seriesKeys_.slice();
113       return [];
114     },
115
116     updateScales_: function(width, height) {
117       if (this.data_ === undefined)
118         return;
119
120       // X.
121       this.xScale_.range([0, width]);
122       this.xScale_.domain(d3.extent(this.data_, function(d) { return d.x; }));
123
124       // Y.
125       var yRange = new tvcm.Range();
126       this.data_.forEach(function(d) {
127         this.seriesKeys_.forEach(function(k) {
128           yRange.addValue(d[k]);
129         });
130       }, this);
131
132       this.yScale_.range([height, 0]);
133       this.yScale_.domain([yRange.min, yRange.max]);
134     },
135
136     updateContents_: function() {
137       ChartBase.prototype.updateContents_.call(this);
138       if (!this.data_)
139         return;
140
141       var chartAreaSel = d3.select(this.chartAreaElement);
142
143       var brushes = this.brushedRange_.isEmpty ? [] : [this.brushedRange_];
144
145       var brushRectsSel = chartAreaSel.select('#brushes')
146           .selectAll('rect').data(brushes);
147       brushRectsSel.enter()
148           .append('rect');
149       brushRectsSel.exit().remove();
150       brushRectsSel
151         .attr('x', function(d) {
152             return this.xScale_(d.min);
153           }.bind(this))
154         .attr('y', 0)
155         .attr('width', function(d) {
156             return this.xScale_(d.max) - this.xScale_(d.min);
157           }.bind(this))
158         .attr('height', this.chartAreaSize.height);
159
160
161       var seriesSel = chartAreaSel.select('#series');
162       var pathsSel = seriesSel.selectAll('path').data(this.seriesKeys_);
163       pathsSel.enter()
164           .append('path')
165           .attr('class', 'line')
166           .style('stroke', function(key) {
167             return getColorOfKey(key);
168           })
169           .attr('d', function(key) {
170             var line = d3.svg.line()
171               .x(function(d) { return this.xScale_(d.x); }.bind(this))
172               .y(function(d) { return this.yScale_(d[key]); }.bind(this));
173             return line(this.data_);
174           }.bind(this));
175       pathsSel.exit().remove();
176     },
177
178     getDataIndexAtClientPoint_: function(clientX, clientY, clipToY) {
179       var rect = this.getBoundingClientRect();
180       var margin = this.margin;
181       var chartAreaSize = this.chartAreaSize;
182
183       var x = clientX - rect.left - margin.left;
184       var y = clientY - rect.top - margin.top;
185
186       // Don't check width: let people select the left- and right-most data
187       // points.
188       if (clipToY) {
189         if (y < 0 ||
190             y >= chartAreaSize.height)
191           return undefined;
192       }
193
194       var dataX = this.xScale_.invert(x);
195
196       var index;
197       if (this.data_) {
198         var bisect = d3.bisector(function(d) { return d.x; }).right;
199         index = bisect(this.data_, dataX) - 1;
200       }
201
202       return index;
203     },
204
205     onMouseDown_: function(e) {
206       var index = this.getDataIndexAtClientPoint_(e.clientX, e.clientY, true);
207
208       if (index !== undefined) {
209         tvcm.ui.trackMouseMovesUntilMouseUp(
210             this.onMouseMove_.bind(this, e.button),
211             this.onMouseUp_.bind(this, e.button));
212       }
213       e.preventDefault();
214       e.stopPropagation();
215
216       var event = new Event('item-mousedown');
217       event.data = this.data_[index];
218       event.index = index;
219       event.buttons = e.buttons;
220       this.dispatchEvent(event);
221     },
222
223     onMouseMove_: function(button, e) {
224       var index = this.getDataIndexAtClientPoint_(e.clientX, e.clientY, false);
225       if (e.buttons !== undefined) {
226         e.preventDefault();
227         e.stopPropagation();
228       }
229
230       var event = new Event('item-mousemove');
231       event.data = this.data_[index];
232       event.index = index;
233       event.button = button;
234       this.dispatchEvent(event);
235     },
236
237     onMouseUp_: function(button, e) {
238       var index = this.getDataIndexAtClientPoint_(e.clientX, e.clientY, false);
239       e.preventDefault();
240       e.stopPropagation();
241
242       var event = new Event('item-mouseup');
243       event.data = this.data_[index];
244       event.index = index;
245       event.button = button;
246       this.dispatchEvent(event);
247     }
248   };
249
250   return {
251     LineChart: LineChart
252   };
253 });
254 </script>