cms/app-client/app/components/visu-langues.js
changeset 286 57762c0b601e
parent 261 02e2396bcbbc
child 287 4e199fd29689
equal deleted inserted replaced
285:6d8e7e6dc83c 286:57762c0b601e
     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     }