cms/app-client/app/components/visu-langues.js
author ymh <ymh.work@gmail.com>
Tue, 15 Nov 2016 17:42:57 +0100
changeset 424 feb0d3e0fef9
parent 394 48458e099b05
child 467 762fc0eb4946
permissions -rw-r--r--
add dynamic date range calculation for dates, and add color gradient component, add color gradient for language and chrono

import Ember from 'ember';
import d3 from 'd3';
import ENV from 'app-client/config/environment';
import _ from 'lodash/lodash';
import URI from 'urijs';

export default Ember.Component.extend({

    constants: Ember.inject.service(),
    filter: Ember.inject.service(),
    colors: Ember.inject.service(),

    scale: Ember.computed('maxCount', 'minCount', function() {
      let maxCount = this.get('maxCount');
      let minCount = this.get('minCount');
      return this.get('colors').getScaleLinear(minCount, maxCount);
    }),
    maxCount: 0,
    minCount: 0,

    filterObserver: Ember.observer('filter.language', function() {
        Ember.$('.node').removeClass("selected");
        Ember.$('.node[data-id="' + this.get('filter').get('language') + '"]').addClass("selected");
    }),

    didInsertElement: function(){
        var self = this;
        var baseurl = (ENV.APP.backRootURL || ENV.rootURL).replace(/\/$/,"")+'/api/v1';
        var url = URI(baseurl+"/stats/languages").search(this.get('filter').get('queryParamsValuesURI'));

        d3.json(url.href(), function(data) {
            var margin = { top: 30, right: 0, bottom: 0, left: 0 };
            var width = Ember.$('#' + self.get('elementId')).width();
            var height = Ember.$('#' + self.get('elementId')).height() - margin.top - margin.bottom;

            var languages = data['languages'];
            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 x = d3.scale.linear()
                .domain([0, width])
                .range([0, width]),
                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; })
                .value(function(d){
                    return Math.floor((((d.value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin);
                })
                .round(false);

            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 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;

            accumulate(root);
            layout(root);
            display(root);

            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 (d.values) {
                    d.value = d.values.reduce(function(p, v) { return p + (languages[v] ? languages[v] : 0); }, 0);
                } else {
                    d.value = languages[d.id] ? languages[d.id] : 0;
                }
                return d.value;
            }

            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') {
                              if(node._children) {
                                  node._children.forEach(function(child) {
                                      count = getCount(child, count);
                                  });
                              } else if(node.values) {
                                  count = node.values.reduce(function(p, v) { return p + (languages[v] ? languages[v] : 0); }, 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;
                        c.dy *= d.dy;
                        c.parent = d;
                        layout(c);
                    });
                }
            }

            function position() {
                return this.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'; });
            }

            function display(d) {
                breadcrumbs
                    .datum(d.parent)
                    .html(name(d))
                    .on("click", transition);

                var nodes = element.append("div")
                    .attr("class", "nodes")
                    .datum(d);

                var node = nodes.selectAll()
                    .data(d._children)
                    .enter()
                    .append("div")
                    .attr("data-id", function(d) { return d.id; });

                var dMin = Math.min.apply(null, d._children.map(function(d){ return d.count; }));
                var dMax = Math.max.apply(null, d._children.map(function(d){ return d.count; }));
                self.setProperties({minCount: dMin, maxCount: dMax});
                var scale = self.get('scale');
                var backgroundColor = function(_d) { return scale(_d.count);};
                var colorClass = function(_d) { return (self.get('colors').getPerceptiveLuminance(backgroundColor(_d)) >= 0.5)?'light-color':'dark-color'; };

                node.attr("class", function(_d) { return "node " + colorClass(_d) + ( _d.id === self.get('filter').get('language') ? " selected" : "" ); })
                    .call(position)
                    .style("background-color", backgroundColor)
                    .on("click", selectHandler);

                node.filter(function(d) { return d._children; })
                    .classed("children", true)
                    .on("click", transition)
                    .append("i")
                    .attr("class", "fa fa-folder-o");

                node.append("span")
                    .html(function(d) { return d.name + ' <span class="count">(' + d.count + ')</span>'; });

                function transition(d) {
                    if (transitioning || !d) {
                        return;
                    }

                    selectHandler(d);
                    transitioning = true;

                    var newNode = display(d),
                    transitionNodes = node.transition().duration(750),
                    transitionNewNodes = newNode.transition().duration(750);

                    x.domain([d.x, d.x + d.dx]);
                    y.domain([d.y, d.y + d.dy]);

                    element.style("shape-rendering", null);

                    element.selectAll(".node").sort(function(a, b) { return a.depth - b.depth; });

                    newNode.selectAll().style("fill-opacity", 0);

                    transitionNodes.style("opacity", 0)
                        .call(position);

                    transitionNewNodes.style("opacity", 1)
                        .call(position);

                    transitionNodes.remove().each("end", function() {
                        element.style("shape-rendering", "crispEdges");
                        transitioning = false;
                    });
                }

                function selectHandler (d){
                    if (d.id){
                        self.get('filter').setFilter('language', d.id);
                    }
                }

                return node;
            }

            function name(d) {
                return d.parent ? name(d.parent) + '<span class="level">' + d.name + '</span>' : '<span class="root">' + d.name + '</span>';
            }

        });
    }

});