Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / third_party / tvcm / src / tvcm / ui / pie_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/dom_helpers.html">
10 <link rel="import" href="/tvcm/ui/chart_base.html">
11 <link rel="stylesheet" href="/tvcm/ui/pie_chart.css">
12
13 <script>
14 'use strict';
15
16 tvcm.exportTo('tvcm.ui', function() {
17   var ChartBase = tvcm.ui.ChartBase;
18   var getColorOfKey = tvcm.ui.getColorOfKey;
19
20   var MIN_RADIUS = 100;
21
22   /**
23    * @constructor
24    */
25   var PieChart = tvcm.ui.define('pie-chart', ChartBase);
26
27   PieChart.prototype = {
28     __proto__: ChartBase.prototype,
29
30     decorate: function() {
31       ChartBase.prototype.decorate.call(this);
32       this.classList.add('pie-chart');
33
34       this.data_ = undefined;
35       this.seriesKeys_ = undefined;
36
37       var chartAreaSel = d3.select(this.chartAreaElement);
38       var pieGroupSel = chartAreaSel.append('g')
39         .attr('class', 'pie-group');
40       this.pieGroup_ = pieGroupSel.node();
41
42       this.pathsGroup_ = pieGroupSel.append('g')
43         .attr('class', 'paths')
44         .node();
45       this.labelsGroup_ = pieGroupSel.append('g')
46         .attr('class', 'labels')
47         .node();
48       this.linesGroup_ = pieGroupSel.append('g')
49         .attr('class', 'lines')
50         .node();
51     },
52
53     get data() {
54       return this.data_;
55     },
56
57
58     /**
59      * @param {Array} data Data for the chart, where each element in the array
60      * must be of the form {label: str, value: number}.
61      */
62     set data(data) {
63       if (data !== undefined) {
64         // Figure out the label values in the data set. E.g. from
65         //   [{label: 'a', ...}, {label: 'b', ...}]
66         // we would commpute ['a', 'y']. These become the series keys.
67         var seriesKeys = [];
68         var seenSeriesKeys = {};
69         data.forEach(function(d) {
70           var k = d.label;
71           if (seenSeriesKeys[k])
72             throw new Error('Label ' + k + ' has been used already');
73           seriesKeys.push(k);
74           seenSeriesKeys[k] = true;
75         }, this);
76         this.seriesKeys_ = seriesKeys;
77       } else {
78         this.seriesKeys_ = undefined;
79       }
80       this.data_ = data;
81       this.updateContents_();
82     },
83
84     get margin() {
85       var margin = {top: 0, right: 0, bottom: 0, left: 0};
86       if (this.chartTitle_)
87         margin.top += 40;
88       return margin;
89     },
90
91     getMinSize: function() {
92       if (!tvcm.ui.isElementAttachedToDocument(this))
93         throw new Error('Cannot measure when unattached');
94       this.updateContents_();
95
96       var labelSel = d3.select(this.labelsGroup_).selectAll('.label');
97       var maxLabelWidth = -Number.MAX_VALUE;
98       var leftTextHeightSum = 0;
99       var rightTextHeightSum = 0;
100       labelSel.each(function(l) {
101         var r = this.getBoundingClientRect();
102         maxLabelWidth = Math.max(maxLabelWidth, r.width + 32);
103         if (this.style.textAnchor == 'end') {
104           leftTextHeightSum += r.height;
105         } else {
106           rightTextHeightSum += r.height;
107         }
108       });
109
110       var titleWidth = this.querySelector(
111           '#title').getBoundingClientRect().width;
112       var margin = this.margin;
113       var marginWidth = margin.left + margin.right;
114       var marginHeight = margin.top + margin.bottom;
115       return {
116         width: Math.max(2 * MIN_RADIUS + 2 * maxLabelWidth,
117                         titleWidth * 1.1) + marginWidth,
118         height: marginHeight + Math.max(2 * MIN_RADIUS,
119                                         leftTextHeightSum,
120                                         rightTextHeightSum) * 1.25
121       };
122     },
123
124
125     getLegendKeys_: function() {
126       // This class creates its own legend, instead of using ChartBase.
127       return undefined;
128     },
129
130     updateScales_: function(width, height) {
131       if (this.data_ === undefined)
132         return;
133     },
134
135     updateContents_: function() {
136       ChartBase.prototype.updateContents_.call(this);
137       if (!this.data_)
138         return;
139
140       var width = this.chartAreaSize.width;
141       var height = this.chartAreaSize.height;
142       var radius = Math.max(MIN_RADIUS, Math.min(width, height * 0.95) / 2);
143
144       d3.select(this.pieGroup_).attr(
145           'transform',
146           'translate(' + width / 2 + ',' + height / 2 + ')');
147
148       // Bind the pie layout to its data
149       var pieLayout = d3.layout.pie()
150         .value(function(d) { return d.value; })
151         .sort(null);
152
153       var piePathsSel = d3.select(this.pathsGroup_)
154           .datum(this.data_)
155           .selectAll('path')
156           .data(pieLayout);
157
158       function midAngle(d) {
159         return d.startAngle + (d.endAngle - d.startAngle) / 2;
160       }
161
162       var pathsArc = d3.svg.arc()
163         .innerRadius(0)
164         .outerRadius(radius - 30);
165
166       var valueLabelArc = d3.svg.arc()
167         .innerRadius(radius - 100)
168         .outerRadius(radius - 30);
169
170       var lineBeginArc = d3.svg.arc()
171         .innerRadius(radius - 50)
172         .outerRadius(radius - 50);
173
174       var lineEndArc = d3.svg.arc()
175         .innerRadius(radius)
176         .outerRadius(radius);
177
178       // Paths.
179       piePathsSel.enter().append('path')
180         .attr('class', 'arc')
181         .attr('fill', function(d, i) {
182             var origData = this.data_[i];
183             var highlighted = (origData.label ===
184                                this.currentHighlightedLegendKey);
185             return getColorOfKey(origData.label, highlighted);
186           }.bind(this))
187         .attr('d', pathsArc)
188         .on('click', function(d, i) {
189             var origData = this.data_[i];
190             var event = new Event('item-click');
191             event.data = origData;
192             event.index = i;
193             this.dispatchEvent(event);
194             d3.event.stopPropagation();
195           }.bind(this))
196         .on('mouseenter', function(d, i) {
197             var origData = this.data_[i];
198             this.pushTempHighlightedLegendKey(origData.label);
199           }.bind(this))
200         .on('mouseleave', function(d, i) {
201             var origData = this.data_[i];
202             this.popTempHighlightedLegendKey(origData.label);
203           }.bind(this));
204
205       // Value labels.
206       piePathsSel.enter().append('text')
207         .attr('class', 'arc-text')
208         .attr('transform', function(d) {
209             return 'translate(' + valueLabelArc.centroid(d) + ')';
210           })
211         .attr('dy', '.35em')
212         .style('text-anchor', 'middle')
213         .text(function(d, i) {
214             var origData = this.data_[i];
215             if (origData.valueText === undefined)
216               return '';
217
218             if (d.endAngle - d.startAngle < 0.4)
219               return '';
220             return origData.valueText;
221           }.bind(this));
222
223       piePathsSel.exit().remove();
224
225       // Labels.
226       var labelSel = d3.select(this.labelsGroup_).selectAll('.label')
227           .data(pieLayout(this.data_));
228       labelSel.enter()
229           .append('text')
230           .attr('class', 'label')
231           .attr('dy', '.35em');
232
233       labelSel.text(function(d) {
234         if (d.data.label.length > 40)
235           return d.data.label.substr(0, 40) + '...';
236         return d.data.label;
237       });
238       labelSel.attr('transform', function(d) {
239         var pos = lineEndArc.centroid(d);
240         pos[0] = radius * (midAngle(d) < Math.PI ? 1 : -1);
241         return 'translate(' + pos + ')';
242       });
243       labelSel.style('text-anchor', function(d) {
244         return midAngle(d) < Math.PI ? 'start' : 'end';
245       });
246
247       // Lines.
248       var lineSel = d3.select(this.linesGroup_).selectAll('.line')
249           .data(pieLayout(this.data_));
250       lineSel.enter()
251         .append('polyline')
252         .attr('class', 'line')
253         .attr('dy', '.35em');
254       lineSel.attr('points', function(d) {
255         var pos = lineEndArc.centroid(d);
256         pos[0] = radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
257         return [lineBeginArc.centroid(d), lineEndArc.centroid(d), pos];
258       });
259     },
260
261     updateHighlight_: function() {
262       ChartBase.prototype.updateHighlight_.call(this);
263       // Update color of pie segments.
264       var pathsGroupSel = d3.select(this.pathsGroup_);
265       var that = this;
266       pathsGroupSel.selectAll('.arc').each(function(d, i) {
267         var origData = that.data_[i];
268         var highlighted = origData.label == that.currentHighlightedLegendKey;
269         var color = getColorOfKey(origData.label, highlighted);
270         this.style.fill = color;
271       });
272     }
273   };
274
275   return {
276     PieChart: PieChart
277   };
278 });
279 </script>