8 constants: Ember.inject.service(), |
8 constants: Ember.inject.service(), |
9 filter: Ember.inject.service(), |
9 filter: Ember.inject.service(), |
10 |
10 |
11 didInsertElement: function(){ |
11 didInsertElement: function(){ |
12 var self = this; |
12 var self = this; |
13 var margin = {top: 20, right: 0, bottom: 0, left: 0}, |
13 var baseurl = ENV.rootURL.replace(/\/$/,"")+'/api/v1'; |
14 width = Ember.$("#chart_div").width(), |
|
15 height = 600 - margin.top - margin.bottom, |
|
16 formatNumber = d3.format(",d"), |
|
17 transitioning; |
|
18 |
14 |
19 var x = d3.scale.linear() |
15 d3.json(baseurl+"/languages", function(languages) { |
20 .domain([0, width]) |
16 var margin = { top: 30, right: 0, bottom: 0, left: 0 }; |
21 .range([0, width]); |
17 var width = $('#' + self.get('elementId')).width(); |
|
18 var height = $('#' + self.get('elementId')).height() - margin.top - margin.bottom; |
22 |
19 |
23 var y = d3.scale.linear() |
20 var array = Object.keys(languages).map(function (key) { return languages[key]; }); |
24 .domain([0, height]) |
21 var oldMin = Math.min(...array), |
25 .range([0, height]); |
22 oldMax = Math.max(...array); |
|
23 var sum = array.reduce(function(a, b) { return a + b; }); |
|
24 var average = sum / array.length; |
|
25 var newMin = Math.floor((average - oldMin)), |
|
26 newMax = Math.floor((oldMax - average)); |
|
27 |
26 |
28 |
27 var treemap = d3.layout.treemap() |
29 var x = d3.scale.linear() |
28 .children(function(d, depth) { return depth ? null : d._children; }) |
30 .domain([0, width]) |
29 .sort(function(a, b) { return a.value - b.value; }) |
31 .range([0, width]), |
30 .ratio(height / width * 0.5 * (1 + Math.sqrt(5))) |
32 y = d3.scale.linear() |
31 .round(false); |
33 .domain([0, height]) |
|
34 .range([0, height]); |
32 |
35 |
33 var svg = d3.select("#chart_div").append("svg") |
36 var treemap = d3.layout.treemap() |
34 .attr("width", width + margin.left + margin.right) |
37 .children(function(d, depth) { return depth ? null : d._children; }) |
35 .attr("height", height + margin.bottom + margin.top) |
38 .sort(function(a, b) { return a.value - b.value; }) |
36 .style("margin-left", -margin.left + "px") |
39 .value(function(d){ |
37 .style("margin.right", -margin.right + "px") |
40 return Math.floor((((d.value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin); |
38 .append("g") |
41 }) |
39 .attr("transform", "translate(" + margin.left + "," + margin.top + ")") |
42 .round(false); |
40 .style("shape-rendering", "crispEdges"); |
|
41 |
43 |
42 var grandparent = svg.append("g") |
44 var element = d3.select('#' + self.get('elementId')) |
43 .attr("class", "grandparent"); |
45 .style("width", width + margin.left + margin.right + 'px') |
|
46 .style("height", height + margin.bottom + margin.top + 'px') |
|
47 .style("margin-left", -margin.left + "px") |
|
48 .style("margin-right", -margin.right + "px") |
|
49 .attr("transform", "translate(" + margin.left + "," + margin.top + ")") |
|
50 .style("shape-rendering", "crispEdges"); |
44 |
51 |
45 grandparent.append("rect") |
52 var breadcrumbs = element.insert("div", ":first-child") |
46 .attr("y", -margin.top) |
53 .attr("class", "breadcrumbs") |
47 .attr("width", width) |
54 .attr("y", -margin.top) |
48 .attr("height", margin.top); |
55 .style("width", width + 'px') |
49 |
56 .style("height", margin.top + 'px'); |
50 grandparent.append("text") |
|
51 .attr("x", 6) |
|
52 .attr("y", 6 - margin.top) |
|
53 .attr("dy", ".75em"); |
|
54 |
|
55 var baseurl = ENV.rootURL.replace(/\/$/,"")+'/api/v1'; |
|
56 d3.json(baseurl+"/languages", function(languages) { |
|
57 |
57 |
58 var root = _.cloneDeep(self.constants.LANGUAGES_TREEMAP); |
58 var root = _.cloneDeep(self.constants.LANGUAGES_TREEMAP); |
|
59 root.x = root.y = 0; |
|
60 root.dx = width; |
|
61 root.dy = height; |
|
62 root.depth = 0; |
|
63 var transitioning = false; |
59 |
64 |
60 initialize(root); |
|
61 accumulate(root); |
65 accumulate(root); |
62 layout(root); |
66 layout(root); |
63 display(root); |
67 display(root); |
64 |
68 |
65 function initialize(root) { |
|
66 root.x = root.y = 0; |
|
67 root.dx = width; |
|
68 root.dy = height; |
|
69 root.depth = 0; |
|
70 } |
|
71 |
|
72 // Aggregate the values for internal nodes. This is normally done by the |
|
73 // treemap layout, but not here because of our custom implementation. |
|
74 // We also take a snapshot of the original children (_children) to avoid |
|
75 // the children being overwritten when when layout is computed. |
|
76 function accumulate(d) { |
69 function accumulate(d) { |
77 d._children = d.children; |
70 d._children = d.children; |
78 |
|
79 if(d.children) { |
71 if(d.children) { |
80 d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0); |
72 d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0); |
81 } else if (_.isArray(d.id)) { |
|
82 d.value = d.id.reduce(function(s,lid) { return s + (languages[lid]?languages[lid]:0); }, 0); |
|
83 } else { |
73 } else { |
84 d.value = languages[d.id]?languages[d.id]:0; |
74 d.value = languages[d.id] ? languages[d.id] : 0; |
85 } |
75 } |
86 |
|
87 return d.value; |
76 return d.value; |
88 } |
77 } |
89 |
78 |
90 // Compute the treemap layout recursively such that each group of siblings |
|
91 // uses the same size (1×1) rather than the dimensions of the parent cell. |
|
92 // This optimizes the layout for the current zoom state. Note that a wrapper |
|
93 // object is created for the parent node for each group of siblings so that |
|
94 // the parent’s dimensions are not discarded as we recurse. Since each group |
|
95 // of sibling was laid out in 1×1, we must rescale to fit using absolute |
|
96 // coordinates. This lets us use a viewport to zoom. |
|
97 function layout(d) { |
79 function layout(d) { |
98 if (d._children) { |
80 if (d._children) { |
99 treemap.nodes({_children: d._children}); |
81 treemap.nodes({_children: d._children}); |
100 d._children.forEach(function(c) { |
82 d._children.forEach(function(c) { |
|
83 function getCount(node, count = 0) { |
|
84 var c = languages[node.id]; |
|
85 if(typeof c === 'undefined') { |
|
86 node._children.forEach(function(child) { |
|
87 count = getCount(child, count); |
|
88 }); |
|
89 return count; |
|
90 } else { |
|
91 return count + c; |
|
92 } |
|
93 } |
|
94 c.count = getCount(c); |
101 c.x = d.x + c.x * d.dx; |
95 c.x = d.x + c.x * d.dx; |
102 c.y = d.y + c.y * d.dy; |
96 c.y = d.y + c.y * d.dy; |
103 c.dx *= d.dx; |
97 c.dx *= d.dx; |
104 c.dy *= d.dy; |
98 c.dy *= d.dy; |
105 c.parent = d; |
99 c.parent = d; |
107 }); |
101 }); |
108 } |
102 } |
109 } |
103 } |
110 |
104 |
111 function display(d) { |
105 function display(d) { |
112 grandparent |
106 breadcrumbs |
113 .datum(d.parent) |
107 .datum(d.parent) |
114 .on("click", transition) |
108 .html(name(d)) |
115 .select("text") |
109 .on("click", transition); |
116 .text(name(d)); |
|
117 |
110 |
118 var g1 = svg.insert("g", ".grandparent") |
111 var nodes = element.append("div") |
119 .datum(d) |
112 .attr("class", "nodes") |
120 .attr("class", "depth"); |
113 .datum(d); |
121 |
114 |
122 var g = g1.selectAll("g") |
115 var node = nodes.selectAll() |
123 .data(d._children) |
116 .data(d._children) |
124 .enter().append("g"); |
117 .enter() |
|
118 .append("div"); |
125 |
119 |
126 g.classed("bla", true) |
120 node.attr("class", "node") |
|
121 .style("width", function(d) { return x(d.x + d.dx) - x(d.x) + 'px'; }) |
|
122 .style("height", function(d) { return y(d.y + d.dy) - y(d.y) + 'px'; }) |
|
123 .style("left", function(d) { return d.x + 'px'; }) |
|
124 .style("top", function(d) { return d.y + 'px'; }) |
127 .on("click", selectHandler); |
125 .on("click", selectHandler); |
128 |
126 |
129 g.filter(function(d) { return d._children; }) |
127 node.filter(function(d) { return d._children; }) |
130 .classed("children", true) |
128 .classed("children", true) |
131 .on("click", transition); |
129 .on("click", transition); |
132 |
130 |
133 g.append("rect") |
131 node.append("span") |
134 .attr("class", "parent") |
132 .html(function(d) { return d.name + ' <span class="count">(' + d.count + ')</span>'; }); |
135 .call(rect) |
|
136 .append("title") |
|
137 .text(function(d) { return formatNumber(d.value); }); |
|
138 |
|
139 g.append("text") |
|
140 .attr("dy", ".75em") |
|
141 .text(function(d) { return d.name; }) |
|
142 .call(text); |
|
143 |
133 |
144 function transition(d) { |
134 function transition(d) { |
145 if (transitioning || !d) { |
135 if (transitioning || !d) { |
146 return; |
136 return; |
147 } |
137 } |
148 |
138 |
149 selectHandler(d); |
139 selectHandler(d); |
150 transitioning = true; |
140 transitioning = true; |
151 |
141 |
152 var g2 = display(d), |
142 var newNodes = display(d), |
153 t1 = g1.transition().duration(750), |
143 transitionNodes = nodes.transition().duration(750), |
154 t2 = g2.transition().duration(750); |
144 transitionNewNodes = newNodes.transition().duration(750); |
155 |
145 |
156 // Update the domain only after entering new elements. |
|
157 x.domain([d.x, d.x + d.dx]); |
146 x.domain([d.x, d.x + d.dx]); |
158 y.domain([d.y, d.y + d.dy]); |
147 y.domain([d.y, d.y + d.dy]); |
159 |
148 |
160 // Enable anti-aliasing during the transition. |
149 element.style("shape-rendering", null); |
161 svg.style("shape-rendering", null); |
|
162 |
150 |
163 // Draw child nodes on top of parent nodes. |
151 element.selectAll(".nodes").sort(function(a, b) { return a.depth - b.depth; }); |
164 svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; }); |
|
165 |
152 |
166 // Fade-in entering text. |
153 newNodes.selectAll("text").style("fill-opacity", 0); |
167 g2.selectAll("text").style("fill-opacity", 0); |
|
168 |
154 |
169 // Transition to the new view. |
155 transitionNodes.style("fill-opacity", 0) |
170 t1.selectAll("text").call(text).style("fill-opacity", 0); |
156 .style("width", function(d) { return x(d.x + d.dx) - x(d.x) + 'px'; }) |
171 t2.selectAll("text").call(text).style("fill-opacity", 1); |
157 .style("height", function(d) { return y(d.y + d.dy) - y(d.y) + 'px'; }) |
172 t1.selectAll("rect").call(rect); |
158 .style("left", function(d) { return x(d.x) + 'px'; }) |
173 t2.selectAll("rect").call(rect); |
159 .style("top", function(d) { return y(d.y) + 'px'; }) |
|
160 .attr("fill", function(d) { return (d.color || "#bbb"); }); |
174 |
161 |
175 // Remove the old node when the transition is finished. |
162 transitionNewNodes.style("fill-opacity", 1) |
176 t1.remove().each("end", function() { |
163 .style("width", function(d) { return x(d.x + d.dx) - x(d.x) + 'px'; }) |
177 svg.style("shape-rendering", "crispEdges"); |
164 .style("height", function(d) { return y(d.y + d.dy) - y(d.y) + 'px'; }) |
|
165 .style("left", function(d) { return x(d.x) + 'px'; }) |
|
166 .style("top", function(d) { return y(d.y) + 'px'; }) |
|
167 .attr("fill", function(d) { return (d.color || "#bbb"); }); |
|
168 |
|
169 transitionNodes.remove().each("end", function() { |
|
170 element.style("shape-rendering", "crispEdges"); |
178 transitioning = false; |
171 transitioning = false; |
179 }); |
172 }); |
180 } |
173 } |
181 |
174 |
182 function selectHandler (d){ |
175 function selectHandler (d){ |
183 if (d.id){ |
176 if (d.id){ |
184 self.get('filter').set('language', d.id); |
177 self.get('filter').set('language', d.id); |
185 } |
178 } |
186 } |
179 } |
187 |
180 |
188 return g; |
181 return node; |
189 } |
|
190 |
|
191 function text(text) { |
|
192 text.attr("x", function(d) { return x(d.x) + 6; }) |
|
193 .attr("y", function(d) { return y(d.y) + 6; }); |
|
194 } |
|
195 |
|
196 function rect(rect) { |
|
197 rect.attr("x", function(d) { return x(d.x); }) |
|
198 .attr("y", function(d) { return y(d.y); }) |
|
199 .attr("width", function(d) { return x(d.x + d.dx) - x(d.x); }) |
|
200 .attr("height", function(d) { return y(d.y + d.dy) - y(d.y); }) |
|
201 .attr("fill", function(d) { return (d.color || "#bbb"); }); |
|
202 } |
182 } |
203 |
183 |
204 function name(d) { |
184 function name(d) { |
205 return d.parent ? name(d.parent) + "." + d.name : d.name; |
185 return d.parent ? name(d.parent) + '<span class="level">' + d.name + '</span>' : '<span class="root">' + d.name + '</span>'; |
206 } |
186 } |
207 |
187 |
208 }); |
188 }); |
209 |
189 |
210 } |
190 } |