diff -r 6d8e7e6dc83c -r 57762c0b601e cms/app-client/app/components/visu-langues.js --- a/cms/app-client/app/components/visu-langues.js Fri Sep 23 15:56:38 2016 +0200 +++ b/cms/app-client/app/components/visu-langues.js Fri Sep 23 21:44:56 2016 +0200 @@ -10,94 +10,88 @@ didInsertElement: function(){ var self = this; - var margin = {top: 20, right: 0, bottom: 0, left: 0}, - width = Ember.$("#chart_div").width(), - height = 600 - margin.top - margin.bottom, - formatNumber = d3.format(",d"), - transitioning; + var baseurl = ENV.rootURL.replace(/\/$/,"")+'/api/v1'; - var x = d3.scale.linear() - .domain([0, width]) - .range([0, width]); + d3.json(baseurl+"/languages", function(languages) { + var margin = { top: 30, right: 0, bottom: 0, left: 0 }; + var width = $('#' + self.get('elementId')).width(); + var height = $('#' + self.get('elementId')).height() - margin.top - margin.bottom; - var y = d3.scale.linear() - .domain([0, height]) - .range([0, height]); - - var treemap = d3.layout.treemap() - .children(function(d, depth) { return depth ? null : d._children; }) - .sort(function(a, b) { return a.value - b.value; }) - .ratio(height / width * 0.5 * (1 + Math.sqrt(5))) - .round(false); + var array = Object.keys(languages).map(function (key) { return languages[key]; }); + var oldMin = Math.min(...array), + oldMax = Math.max(...array); + var sum = array.reduce(function(a, b) { return a + b; }); + var average = sum / array.length; + var newMin = Math.floor((average - oldMin)), + newMax = Math.floor((oldMax - average)); + - var svg = d3.select("#chart_div").append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.bottom + margin.top) - .style("margin-left", -margin.left + "px") - .style("margin.right", -margin.right + "px") - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")") - .style("shape-rendering", "crispEdges"); - - var grandparent = svg.append("g") - .attr("class", "grandparent"); + var x = d3.scale.linear() + .domain([0, width]) + .range([0, width]), + y = d3.scale.linear() + .domain([0, height]) + .range([0, height]); - grandparent.append("rect") - .attr("y", -margin.top) - .attr("width", width) - .attr("height", margin.top); + var treemap = d3.layout.treemap() + .children(function(d, depth) { return depth ? null : d._children; }) + .sort(function(a, b) { return a.value - b.value; }) + .value(function(d){ + return Math.floor((((d.value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin); + }) + .round(false); - grandparent.append("text") - .attr("x", 6) - .attr("y", 6 - margin.top) - .attr("dy", ".75em"); + var element = d3.select('#' + self.get('elementId')) + .style("width", width + margin.left + margin.right + 'px') + .style("height", height + margin.bottom + margin.top + 'px') + .style("margin-left", -margin.left + "px") + .style("margin-right", -margin.right + "px") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")") + .style("shape-rendering", "crispEdges"); - var baseurl = ENV.rootURL.replace(/\/$/,"")+'/api/v1'; - d3.json(baseurl+"/languages", function(languages) { + var breadcrumbs = element.insert("div", ":first-child") + .attr("class", "breadcrumbs") + .attr("y", -margin.top) + .style("width", width + 'px') + .style("height", margin.top + 'px'); var root = _.cloneDeep(self.constants.LANGUAGES_TREEMAP); + root.x = root.y = 0; + root.dx = width; + root.dy = height; + root.depth = 0; + var transitioning = false; - initialize(root); accumulate(root); layout(root); display(root); - function initialize(root) { - root.x = root.y = 0; - root.dx = width; - root.dy = height; - root.depth = 0; - } - - // Aggregate the values for internal nodes. This is normally done by the - // treemap layout, but not here because of our custom implementation. - // We also take a snapshot of the original children (_children) to avoid - // the children being overwritten when when layout is computed. function accumulate(d) { d._children = d.children; - if(d.children) { d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0); - } else if (_.isArray(d.id)) { - d.value = d.id.reduce(function(s,lid) { return s + (languages[lid]?languages[lid]:0); }, 0); } else { - d.value = languages[d.id]?languages[d.id]:0; + d.value = languages[d.id] ? languages[d.id] : 0; } - return d.value; } - // Compute the treemap layout recursively such that each group of siblings - // uses the same size (1×1) rather than the dimensions of the parent cell. - // This optimizes the layout for the current zoom state. Note that a wrapper - // object is created for the parent node for each group of siblings so that - // the parent’s dimensions are not discarded as we recurse. Since each group - // of sibling was laid out in 1×1, we must rescale to fit using absolute - // coordinates. This lets us use a viewport to zoom. function layout(d) { if (d._children) { treemap.nodes({_children: d._children}); d._children.forEach(function(c) { + function getCount(node, count = 0) { + var c = languages[node.id]; + if(typeof c === 'undefined') { + node._children.forEach(function(child) { + count = getCount(child, count); + }); + return count; + } else { + return count + c; + } + } + c.count = getCount(c); c.x = d.x + c.x * d.dx; c.y = d.y + c.y * d.dy; c.dx *= d.dx; @@ -109,37 +103,33 @@ } function display(d) { - grandparent + breadcrumbs .datum(d.parent) - .on("click", transition) - .select("text") - .text(name(d)); + .html(name(d)) + .on("click", transition); + + var nodes = element.append("div") + .attr("class", "nodes") + .datum(d); - var g1 = svg.insert("g", ".grandparent") - .datum(d) - .attr("class", "depth"); + var node = nodes.selectAll() + .data(d._children) + .enter() + .append("div"); - var g = g1.selectAll("g") - .data(d._children) - .enter().append("g"); - - g.classed("bla", true) + node.attr("class", "node") + .style("width", function(d) { return x(d.x + d.dx) - x(d.x) + 'px'; }) + .style("height", function(d) { return y(d.y + d.dy) - y(d.y) + 'px'; }) + .style("left", function(d) { return d.x + 'px'; }) + .style("top", function(d) { return d.y + 'px'; }) .on("click", selectHandler); - g.filter(function(d) { return d._children; }) + node.filter(function(d) { return d._children; }) .classed("children", true) .on("click", transition); - g.append("rect") - .attr("class", "parent") - .call(rect) - .append("title") - .text(function(d) { return formatNumber(d.value); }); - - g.append("text") - .attr("dy", ".75em") - .text(function(d) { return d.name; }) - .call(text); + node.append("span") + .html(function(d) { return d.name + ' (' + d.count + ')'; }); function transition(d) { if (transitioning || !d) { @@ -149,32 +139,35 @@ selectHandler(d); transitioning = true; - var g2 = display(d), - t1 = g1.transition().duration(750), - t2 = g2.transition().duration(750); + var newNodes = display(d), + transitionNodes = nodes.transition().duration(750), + transitionNewNodes = newNodes.transition().duration(750); - // Update the domain only after entering new elements. x.domain([d.x, d.x + d.dx]); y.domain([d.y, d.y + d.dy]); - // Enable anti-aliasing during the transition. - svg.style("shape-rendering", null); + element.style("shape-rendering", null); - // Draw child nodes on top of parent nodes. - svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; }); + element.selectAll(".nodes").sort(function(a, b) { return a.depth - b.depth; }); - // Fade-in entering text. - g2.selectAll("text").style("fill-opacity", 0); + newNodes.selectAll("text").style("fill-opacity", 0); - // Transition to the new view. - t1.selectAll("text").call(text).style("fill-opacity", 0); - t2.selectAll("text").call(text).style("fill-opacity", 1); - t1.selectAll("rect").call(rect); - t2.selectAll("rect").call(rect); + transitionNodes.style("fill-opacity", 0) + .style("width", function(d) { return x(d.x + d.dx) - x(d.x) + 'px'; }) + .style("height", function(d) { return y(d.y + d.dy) - y(d.y) + 'px'; }) + .style("left", function(d) { return x(d.x) + 'px'; }) + .style("top", function(d) { return y(d.y) + 'px'; }) + .attr("fill", function(d) { return (d.color || "#bbb"); }); - // Remove the old node when the transition is finished. - t1.remove().each("end", function() { - svg.style("shape-rendering", "crispEdges"); + transitionNewNodes.style("fill-opacity", 1) + .style("width", function(d) { return x(d.x + d.dx) - x(d.x) + 'px'; }) + .style("height", function(d) { return y(d.y + d.dy) - y(d.y) + 'px'; }) + .style("left", function(d) { return x(d.x) + 'px'; }) + .style("top", function(d) { return y(d.y) + 'px'; }) + .attr("fill", function(d) { return (d.color || "#bbb"); }); + + transitionNodes.remove().each("end", function() { + element.style("shape-rendering", "crispEdges"); transitioning = false; }); } @@ -185,24 +178,11 @@ } } - return g; - } - - function text(text) { - text.attr("x", function(d) { return x(d.x) + 6; }) - .attr("y", function(d) { return y(d.y) + 6; }); - } - - function rect(rect) { - rect.attr("x", function(d) { return x(d.x); }) - .attr("y", function(d) { return y(d.y); }) - .attr("width", function(d) { return x(d.x + d.dx) - x(d.x); }) - .attr("height", function(d) { return y(d.y + d.dy) - y(d.y); }) - .attr("fill", function(d) { return (d.color || "#bbb"); }); + return node; } function name(d) { - return d.parent ? name(d.parent) + "." + d.name : d.name; + return d.parent ? name(d.parent) + '' + d.name + '' : '' + d.name + ''; } });