Migrate d3js to v4 and correct d3js visualisations i.e. bug 3.20. Breadcrumb navigation for the language treemap has been improved
--- a/build/build.sh Sun Dec 04 13:49:44 2016 +0100
+++ b/build/build.sh Fri Dec 09 11:41:15 2016 +0100
@@ -16,34 +16,34 @@
}
function install() {
- pushd "$DIR"
+ pushd "$DIR" > /dev/null
echoblue "---> preparing bo client"
- pushd ../server/bo_client
+ pushd ../server/bo_client > /dev/null
/usr/local/bin/npm install
./node_modules/.bin/bower install
- popd
+ popd > /dev/null
echoblue "---> preparing bo client done"
echoblue "---> preparing back"
- pushd ../server/src
+ pushd ../server/src > /dev/null
php composer.phar install
/usr/local/bin/npm install
./node_modules/.bin/bower install
- popd
+ popd > /dev/null
echoblue "---> preparing back done"
echoblue "---> preparing app-client"
- pushd ../cms/app-client
+ pushd ../cms/app-client > /dev/null
/usr/local/bin/npm install
./node_modules/.bin/bower install
- popd
+ popd > /dev/null
echoblue "---> preparing app-client done"
echoblue "---> preparing module"
- pushd ../cms
+ pushd ../cms > /dev/null
npm install
- popd
+ popd > /dev/null
echoblue "---> preparing module done"
@@ -111,7 +111,7 @@
echo "do_install: $do_install"
[[ "$do_install" == true ]] && echoblue "DO INSTALL" && install;
-pushd "$DIR"
+pushd "$DIR" > /dev/null
echoblue "---> cleaning build folder"
rm -fr root
@@ -121,28 +121,28 @@
mkdir -p root/var/www/corpusdelaparole/drupal/sites/all/modules
echoblue "---> building back"
-pushd ../server/src
+pushd ../server/src > /dev/null
version=$(sed -n "s/[[:space:]]*\'version\'[[:space:]]*=>[[:space:]]*\'\([\.0-9]*\)\'/\1/p" config/version.php)
version=${version:-0.0.0}
npm install
./node_modules/.bin/bower install
./node_modules/.bin/gulp copy-build ${build_option_back}
-popd
+popd > /dev/null
echoblue "---> building back done"
echoblue "---> building app-client"
-pushd ../cms/app-client
+pushd ../cms/app-client > /dev/null
npm install
./node_modules/.bin/bower install
./node_modules/.bin/ember build ${build_option}
-popd
+popd > /dev/null
echoblue "---> building app-client done"
echoblue "---> building module"
-pushd ../cms
+pushd ../cms > /dev/null
npm install
./node_modules/.bin/gulp copy-build ${build_option} --version="$version"
-popd
+popd > /dev/null
echoblue "---> building package"
vagrant ssh -c "/vagrant/build_rpm.sh"
@@ -156,5 +156,22 @@
popd > /dev/null
+echoblue "--> archiving dist"
+
+pushd "$DIR/dist" > /dev/null
+
+rm -f corpusdelaparole-back_*_*.tar.gz
+ARCHIVE_NAME="corpusdelaparole-back_$(date +%Y-%m-%d)_${version}"
+
+mkdir "$ARCHIVE_NAME"
+
+cp bootstrap-puppet.sh corpusdelaparole-$version-*.noarch.rpm installDrupal.sh puppet-corpusdelaparole-$version-*.noarch.rpm "$ARCHIVE_NAME"
+
+tar zcvf "${ARCHIVE_NAME}.tar.gz" "$ARCHIVE_NAME"
+
+rm -fr "$ARCHIVE_NAME"
+
+popd > /dev/null
+
echoblue "---> done"
--- a/build/build_puppet.sh Sun Dec 04 13:49:44 2016 +0100
+++ b/build/build_puppet.sh Fri Dec 09 11:41:15 2016 +0100
@@ -98,5 +98,28 @@
popd > /dev/null
+echoblue "--> archiving dist"
+
+pushd ../server/src > /dev/null
+version=$(sed -n "s/[[:space:]]*\'version\'[[:space:]]*=>[[:space:]]*\'\([\.0-9]*\)\'/\1/p" config/version.php)
+version=${version:-0.0.0}
+popd > /dev/null
+
+pushd "$DIR/dist" > /dev/null
+
+rm -f corpusdelaparole-back_*_*.tar.gz
+ARCHIVE_NAME="corpusdelaparole-back_$(date +%Y-%m-%d)_${version}"
+
+mkdir "$ARCHIVE_NAME"
+
+cp bootstrap-puppet.sh corpusdelaparole-$version-*.noarch.rpm installDrupal.sh puppet-corpusdelaparole-$version-*.noarch.rpm "$ARCHIVE_NAME"
+
+tar zcvf "${ARCHIVE_NAME}.tar.gz" "$ARCHIVE_NAME"
+
+rm -fr "$ARCHIVE_NAME"
+
+popd > /dev/null
+
+
echoblue "---> done"
--- a/cms/app-client/app/adapters/application.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/adapters/application.js Fri Dec 09 11:41:15 2016 +0100
@@ -7,7 +7,8 @@
datestat: 'stats/datestats',
dateminmax: 'stats/dateminmax',
theme: 'stats/themes',
- discourse: 'stats/discourses'
+ discourse: 'stats/discourses',
+ language: 'stats/languages'
};
export default RESTAdapter.extend({
--- a/cms/app-client/app/components/discourses-component.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/components/discourses-component.js Fri Dec 09 11:41:15 2016 +0100
@@ -1,13 +1,18 @@
import Ember from 'ember';
-import d3 from 'd3';
+import * as d3 from 'd3-selection';
+import * as d3h from 'd3-hierarchy';
+import * as d3s from 'd3-scale';
import _ from 'lodash/lodash';
+const MINIMUM_CIRCLE_WIDTH = 60.0;
+
export default Ember.Component.extend({
classNames: ['discourses-component'],
constants: Ember.inject.service(),
filter: Ember.inject.service(),
+ utils: Ember.inject.service(),
discourseObserver: Ember.observer('filter.discourse', function() {
Ember.$('.item').removeClass("selected");
@@ -25,22 +30,36 @@
var discourses = this.get('discourses');
var array = discourses.map(function (d) { return d.get('count'); });
- 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 width = self.$().parent().width();
var height = self.$().parent().height() - self.$().siblings().outerHeight(true);
- var bubble = d3.layout.pack()
- .sort(function comparator(a, b) { return a.value + b.value; })
+ //Determine the minimum circle width
+ var longerStr = _.max(
+ [].concat(...(discourses.map(function(d) { return d.get('label').split(' ');}))),
+ function(s) { return s.length; }
+ );
+ var w = this.get('utils').getWidthOfText(longerStr, '11px');
+ // we try to take into account the fact that there is at least 2 lines
+ var minimum_circle_width = Math.max( Math.sqrt((w*w)+Math.pow(11+11/2, 2)) + 10, MINIMUM_CIRCLE_WIDTH);
+
+ // to avoid division by zero. In any case it makes no sense to consider dimensions
+ // under MINIMUM_CIRCLE_WIDTH
+ var scaleFactor = minimum_circle_width/Math.max(minimum_circle_width, Math.min(width, height));
+
+ var min = Math.min(...array),
+ max = Math.max(...array);
+
+ var scale = d3s.scaleLinear();
+ // The range is the range for font sizes
+ var fontScale = d3s.scaleQuantize().domain([min, max]).range(_.range(10, 14));
+
+ if((min/max) < scaleFactor) {
+ scale = scale.domain([min, max]).range([scaleFactor, 1]);
+ }
+
+ var bubble = d3h.pack()
.size([width, height])
- .value(function(d){
- return Math.floor((((d.value - oldMin) * (newMax - newMin)) / (oldMax - oldMin)) + newMin);
- })
.padding(10);
var element = d3.select('#' + self.get('elementId'))
@@ -49,8 +68,10 @@
.style('position','absolute')
.style('left', -100000);
- var bubbles = bubble.nodes(self.createNodes());
+ var root = d3h.hierarchy(self.createNodes())
+ .sum(function(d) {return scale(d.value);});
+ var bubbles = bubble(root).descendants();
var nodes = element
.selectAll()
.data(bubbles);
@@ -59,20 +80,20 @@
.attr("class", function(d) { return ( d.children ? "category": "item" ) + ( (self.get('filter').get('discourse') !== null && _.contains(self.get('filter').get('discourse'), d.id)) ? " selected" : "" ) ; });
var item = element.selectAll(".item")
- .attr("data-id", function(d) { return d.id; })
+ .attr("data-id", function(d) { return d.data.id; })
.style("left", function(d) { return ( d.x - d.r) + "px"; })
.style("top", function(d) { return ( d.y - d.r) + "px"; })
.style("width", function(d) { return (d.r * 2) + "px"; })
.style("height", function(d) { return (d.r * 2) + "px"; })
- .style("background-color", function(d) { return d.fill; })
- .style("border-color", function(d) { return d.stroke; })
- .style("font-size", function(d) { return Math.floor((((d.value - oldMin) * (13 - 10)) / (oldMax - oldMin)) + 10) + 'px'; })
+ .style("background-color", function(d) { return d.data.fill; })
+ .style("border-color", function(d) { return d.data.stroke; })
+ .style("font-size", function(d) { return fontScale(d.data.count) + 'px'; })
.on('click', function(d) {
- self.get('filter').setFilter('discourse', d.id);
+ self.get('filter').setFilter('discourse', d.data.id);
});
item.append("span")
- .html(function(d) { return d.name + ' <span class="count">(' + d.count + ')</span>'; })
+ .html(function(d) { return d.data.name + ' <span class="count">(' + d.data.count + ')</span>'; })
.style("margin-left", function() { return ( Ember.$(this).width() > Ember.$(this).parent().width() ? - ( Ember.$(this).width() / 2 ) + ( Ember.$(this).parent().width() / 2 ) : 0 ) + 'px'; })
.style("margin-top", function() { return Ember.$(this).parent().height() / 2 - Ember.$(this).height() / 2 + 'px'; });
--- a/cms/app-client/app/components/visu-langues.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/components/visu-langues.js Fri Dec 09 11:41:15 2016 +0100
@@ -1,221 +1,237 @@
import Ember from 'ember';
-import d3 from 'd3';
-import ENV from 'app-client/config/environment';
+import * as d3 from 'd3-selection';
+import * as d3h from 'd3-hierarchy';
+import * as d3s from 'd3-scale';
+import * as d3t from 'd3-transition';
+import * as d3e from 'd3-ease';
import _ from 'lodash/lodash';
-import URI from 'urijs';
+// inspired by http://bl.ocks.org/ganeshv/6a8e9ada3ab7f2d88022
+// and https://bost.ocks.org/mike/treemap/
export default Ember.Component.extend({
- constants: Ember.inject.service(),
- filter: Ember.inject.service(),
- colors: Ember.inject.service(),
+ constants: Ember.inject.service(),
+ filter: Ember.inject.service(),
+ colors: Ember.inject.service(),
+ utils: 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");
- }),
+ 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,
- 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'));
+ firstRender: true,
+
+ filterLanguageObserver: Ember.observer('filter.language', function () {
+ Ember.$('.node').removeClass("selected");
+ Ember.$('.node[data-id="' + this.get('filter').get('language') + '"]').addClass("selected");
+ }),
- 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;
+ filterOtherModified: false,
- 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));
+ filterOtherObserver: Ember.observer('filter.date.[]', 'filter.discourse.[]', 'filter.theme.[]', 'filter.location', function() {
+ this.set('filterOtherModified', true);
+ this.set('firstRender', true);
+ }),
- var x = d3.scale.linear()
- .domain([0, width])
- .range([0, width]),
- y = d3.scale.linear()
- .domain([0, height])
- .range([0, height]);
+ didRender: function () {
- 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);
+ if(!this.get('filterOtherModified') && !this.get('firstRender')) {
+ return;
+ }
+ this.set('firstRender', false);
+ this.set('filterOtherModified', false);
+ var self = this;
- 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 margin = { top: 30, right: 0, bottom: 0, left: 0 };
+ var width = Ember.$('#' + this.get('elementId')).width();
+ var height = Ember.$('#' + this.get('elementId')).height() - margin.top - margin.bottom;
+ var ratio = (1+Math.sqrt(5))/2;
- var breadcrumbs = element.insert("div", ":first-child")
- .attr("class", "breadcrumbs")
- .attr("y", -margin.top)
- .style("width", width + 'px')
- .style("height", margin.top + 'px');
+ var languages = this.get('languages');
+ var languagesMap = languages.reduce(function (res, l) { res[l.get('id')] = l.get('count'); return res; }, {});
- 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);
+ var treemap = d3h.treemap()
+ .size([width/ratio, height])
+ .tile(d3h.treemapSquarify.ratio(1));
- 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;
- }
+ var element = d3.select('#' + this.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");
+
+ var breadcrumbs = element.select("div.breadcrumbs");
- 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);
- });
- }
- }
+ if(breadcrumbs.empty()) {
+ 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(this.constants.LANGUAGES_TREEMAP);
+
+ function ancestors(d) {
+ let ancestors = [];
+ let currentNode = d;
+ while (currentNode.parent) {
+ ancestors.unshift(currentNode = currentNode.parent);
+ }
+ return ancestors;
+ }
- 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);
+ // This function set the count and parent attributes
+ function decorateData(d) {
+ if (d.children) {
+ d.count = d.children.reduce(function (p, v) { v.parent = d; return p + decorateData(v); }, 0);
+ } else if (d.values) {
+ d.count = d.values.reduce(function (p, v) { return p + (languagesMap[v] ? languagesMap[v] : 0); }, 0);
+ } else {
+ d.count = languagesMap[d.id] ? languagesMap[d.id] : 0;
+ }
+ return d.count;
+ }
+ decorateData(root);
- 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>'; });
+ // Clean tree with empty nodes
+ function cleanTree(n) {
+ if(n.children) {
+ n.children = n.children.filter(function(c) { return c.count !== 0;});
+ n.children.forEach(function(c) {
+ cleanTree(c);
+ });
+ }
+ }
+ cleanTree(root);
- function transition(d) {
- if (transitioning || !d) {
- return;
- }
+ var transitioning = false;
- selectHandler(d);
- transitioning = true;
-
- var newNode = display(d),
- transitionNodes = node.transition().duration(750),
- transitionNewNodes = newNode.transition().duration(750);
+ display(root, 1);
- x.domain([d.x, d.x + d.dx]);
- y.domain([d.y, d.y + d.dy]);
-
- element.style("shape-rendering", null);
+ function transition(d, node, nodeWrapper) {
+ if (transitioning || !d) {
+ return;
+ }
- element.selectAll(".node").sort(function(a, b) { return a.depth - b.depth; });
-
- newNode.selectAll().style("fill-opacity", 0);
-
- transitionNodes.style("opacity", 0)
- .call(position);
+ selectHandler(d);
+ transitioning = true;
- transitionNewNodes.style("opacity", 1)
- .call(position);
-
- transitionNodes.remove().each("end", function() {
- element.style("shape-rendering", "crispEdges");
- transitioning = false;
- });
- }
+ var t = d3t.transition().duration(750).ease(d3e.easeLinear);
+ var newNode = display(d, 0);
+ var transitionNodes = node.transition(t);
+ var transitionNewNodes = newNode.transition(t);
- function selectHandler (d){
- if (d.id){
- self.get('filter').setFilter('language', d.id);
- }
- }
+ newNode.style("fill-opacity", 0);
+ transitionNodes.style("opacity", 0).remove().on("end", function () {
+ nodeWrapper.remove();
+ transitioning = false;
+ });
+ transitionNewNodes.style("opacity", 1);
- return node;
- }
-
- function name(d) {
- return d.parent ? name(d.parent) + '<span class="level">' + d.name + '</span>' : '<span class="root">' + d.name + '</span>';
- }
-
- });
}
+ function selectHandler(d) {
+ if (d.id) {
+ self.get('filter').setFilter('language', d.id);
+ }
+ }
+
+ function display(rData, opacity) {
+
+ var countArray = rData.children.map(function (d) { return d.count; });
+ var dMin = Math.min.apply(null, countArray);
+ var dMax = Math.max.apply(null, countArray);
+ var globalMin = rData.children.reduce(function minRec(m, c) {
+ if(c.count < m) {
+ m = c.count;
+ }
+ if(c.children) {
+ m = c.children.reduce(minRec, m);
+ }
+ return m;
+ }, dMax);
+ var dataScale = d3s.scaleLinear().domain([globalMin, dMax]).range([globalMin, dMax]);
+
+ var nameArray = rData.children.reduce(function (res, d) {
+ res = res.concat(d.name.split(/[\s\-]/));
+ return res;
+ }, ["(nnnnn)"]).sort(function(a,b) { return b.length - a.length; });
+
+ var w = self.get('utils').getWidthOfText(nameArray[0], '11px');
+ var p = w*w/(width*height);
+
+ if(dMin/dMax < p) {
+ dataScale = dataScale.range([p*100,100]);
+ }
+
+ var rNode = d3h.hierarchy(rData)
+ .sum(function (d) { return (d.children) ? 0 : dataScale(d.count); })
+ .sort(function (a, b) { return b.value - a.value; });
+
+ self.setProperties({ minCount: dMin, maxCount: dMax });
+ var scale = self.get('scale');
+ var backgroundColor = function (_d) { return scale(_d.data.count); };
+ var colorClass = function (_d) { return (self.get('colors').getPerceptiveLuminance(backgroundColor(_d)) >= 0.5) ? 'light-color' : 'dark-color'; };
+
+ var nodeWrapper = element.append("div")
+ .attr("class", "nodes")
+ .datum(rNode);
+
+ var descendants = treemap(rNode).descendants().filter(function (c) { return c.depth === rNode.depth + 1; });
+
+ var node = nodeWrapper.selectAll()
+ .data(descendants)
+ .enter()
+ .append("div")
+ .attr("data-id", function (d) { return d.data.id; });
+
+
+ node.attr("class", function (_d) { return "node " + colorClass(_d) + (_d.id === self.get('filter').get('language') ? " selected" : ""); })
+ .style("width", function (d) { return (Math.round(d.x1 * ratio) - Math.round(d.x0 * ratio)) + 'px'; })
+ .style("height", function (d) { return (d.y1 - d.y0) + 'px'; })
+ .style("left", function (d) { return Math.round(d.x0*ratio) + 'px'; })
+ .style("top", function (d) { return d.y0 + 'px'; })
+ .style("background-color", backgroundColor)
+ .style('opacity', opacity)
+ .attr('title', function (d) { return d.data.name + ' (' + d.data.count + ')'; })
+ .on("click", function (d) { selectHandler(d.data); });
+
+ node.filter(function (d) { return d.children; })
+ .classed("children", true)
+ .on("click", function (d) { transition(d.data, node, nodeWrapper); })
+ .append("i")
+ .attr("class", "fa fa-folder-o");
+
+ node.append("span")
+ .html(function (d) { return d.data.name + ' <span class="count">(' + d.data.count + ')</span>'; });
+
+ breadcrumbs
+ .selectAll("span")
+ .remove();
+ let i = 0;
+ ancestors(rNode.data).forEach(function (a) {
+ breadcrumbs
+ .append("span")
+ .attr('class', (i++) ? "level" : "root")
+ .html(a.name)
+ .datum(a)
+ .on("click", function (d) { transition(d, node, nodeWrapper); });
+ });
+ breadcrumbs
+ .append("span")
+ .attr('class', (i) ? "level" : "root")
+ .html(rNode.data.name)
+ .datum(rNode.data);
+
+ return node;
+ }
+
+ }
+
});
--- a/cms/app-client/app/helpers/annotation-content.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/helpers/annotation-content.js Fri Dec 09 11:41:15 2016 +0100
@@ -1,7 +1,7 @@
import Ember from 'ember';
export function annotationContent(params/*, hash*/) {
- if(!params || params.length==0) {
+ if(!params || (params.length === 0)) {
return "";
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/models/language.js Fri Dec 09 11:41:15 2016 +0100
@@ -0,0 +1,5 @@
+import DS from 'ember-data';
+
+export default DS.Model.extend({
+ count: DS.attr('number')
+});
--- a/cms/app-client/app/models/transcript.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/models/transcript.js Fri Dec 09 11:41:15 2016 +0100
@@ -2,8 +2,8 @@
export default DS.Model.extend({
- title: DS.attr(),
- annotations: DS.attr(),
- sections: DS.attr(),
-
+ title: DS.attr(),
+ annotations: DS.attr(),
+ sections: DS.attr(),
+
});
--- a/cms/app-client/app/routes/tabs/langues.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/routes/tabs/langues.js Fri Dec 09 11:41:15 2016 +0100
@@ -2,10 +2,15 @@
export default Ember.Route.extend({
- player: Ember.inject.service(),
+ player: Ember.inject.service(),
+ filter: Ember.inject.service(),
- activate: function() {
- this.get('player').set('window', false);
- }
+ model() {
+ return this.get('store').query('language', this.get('filter').get('queryParamsValues'));
+ },
+
+ activate() {
+ this.get('player').set('window', false);
+ }
});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/serializers/language.js Fri Dec 09 11:41:15 2016 +0100
@@ -0,0 +1,22 @@
+import DS from 'ember-data';
+
+export default DS.JSONSerializer.extend({
+
+ normalizeResponse: function(store, primaryModelClass, payload) {
+ var data = [];
+ var languages = payload['languages'];
+ Object.keys(languages).forEach(function(key) {
+ data.push({
+ 'id': key,
+ 'type': 'language',
+ 'attributes': {
+ 'count': languages[key]
+ }
+ });
+ });
+ return {
+ 'data': data
+ };
+ }
+
+});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/app/services/utils.js Fri Dec 09 11:41:15 2016 +0100
@@ -0,0 +1,27 @@
+
+import Ember from 'ember';
+
+export default Ember.Service.extend({
+
+ // inspired by http://stackoverflow.com/a/39089679
+ getWidthOfText(txt, fontsize){
+ // Create dummy span
+ var e = document.createElement('span');
+
+ // set the base font defined in app.scss
+ //e.className = 'base-font';
+ e.style.fontFamily = 'sans-serif';
+ // Set font-size
+ e.style.fontSize = fontsize;
+ // Set text
+ e.innerHTML = txt;
+ // Return width
+ e.style.visibility = 'hidden';
+
+ document.body.appendChild(e);
+ let w = e.offsetWidth;
+ document.body.removeChild(e);
+
+ return w;
+ }
+});
--- a/cms/app-client/app/styles/app.scss Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/styles/app.scss Fri Dec 09 11:41:15 2016 +0100
@@ -36,8 +36,12 @@
outline:0;
}
+.base-font {
+ font-family: sans-serif;
+}
+
#corpus-app {
- font-family: sans-serif;
+ @extend .base-font;
font-size: 12px;
line-height: initial;
}
--- a/cms/app-client/app/styles/tabs/langues.scss Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/styles/tabs/langues.scss Fri Dec 09 11:41:15 2016 +0100
@@ -5,71 +5,70 @@
#tabs-langues .breadcrumbs,
#tabs-langues .node {
- cursor: pointer;
+ cursor: pointer;
}
#tabs-langues .breadcrumbs:hover,
#tabs-langues .node:hover,
#tabs-langues .node.selected {
- background-color: $corpus-blue!important;
- color: $corpus-white!important;
+ background-color: $corpus-blue!important;
+ color: $corpus-white!important;
}
#tabs-langues .breadcrumbs {
- background-color: $corpus-white;
+ background-color: $corpus-white;
color: $corpus-black;
position: relative;
- line-height: 30px;
- padding: 0 10px;
- border-bottom: 1px solid $corpus-white;
- box-sizing: border-box;
+ line-height: 30px;
+ padding: 0 10px;
+ border-bottom: 1px solid $corpus-white;
+ box-sizing: border-box;
}
#tabs-langues .breadcrumbs .root:only-child {
- font-weight: bold;
+ font-weight: bold;
}
#tabs-langues .breadcrumbs .level::before {
- content: '>';
- margin: 0 10px;
- font-weight: normal;
+ content: '>';
+ margin: 0 10px;
+ font-weight: normal;
}
#tabs-langues .breadcrumbs .level:last-child {
- font-weight: bold;
+ font-weight: bold;
}
#tabs-langues .nodes {
- width: inherit;
- position: absolute;
+ width: inherit;
+ position: absolute;
}
#tabs-langues .node {
- position: absolute;
- margin: 0;
- padding: 0;
- border-left: 1px solid $corpus-white;
- border-bottom: 1px solid $corpus-white;
- padding: 10px;
- box-sizing: border-box;
- color: $corpus-white;
+ position: absolute;
+ margin: 0;
+ border-left: 1px solid $corpus-white;
+ border-bottom: 1px solid $corpus-white;
+ padding: 5px;
+ box-sizing: border-box;
+ color: $corpus-white;
}
#tabs-langues .node:hover {
- border-right: none;
+ border-right: none;
}
#tabs-langues .node .fa {
- margin-right: 5px;
+ margin-right: 5px;
}
#tabs-langues .node .fa::before {
- font-size: 14px;
- line-height: 12px;
+ font-size: 14px;
+ line-height: 12px;
}
#tabs-langues .node .count {
- font-weight: bold;
+ font-weight: bold;
}
#tabs-langues .light-color {
--- a/cms/app-client/app/templates/tabs/langues.hbs Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/app/templates/tabs/langues.hbs Fri Dec 09 11:41:15 2016 +0100
@@ -1,3 +1,3 @@
<div id="tabs-langues">
- {{visu-langues}}
+ {{visu-langues languages=model}}
</div>
\ No newline at end of file
--- a/cms/app-client/ember-cli-build.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/ember-cli-build.js Fri Dec 09 11:41:15 2016 +0100
@@ -2,6 +2,7 @@
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
+
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
// Add options here
--- a/cms/app-client/mirage/config.js Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/mirage/config.js Fri Dec 09 11:41:15 2016 +0100
@@ -31,34 +31,44 @@
return transcripts.find(id).transcript;
});
- this.get('/stats/languages', 'languages');
+ this.get('/stats/languages', ({languages}, request) => {
+ let qParams = request.queryParams['discourse'];
+ if(qParams) {
+ var res = [];
+ let allLanguages = languages.all().models;
+ let i=0;
+ while(i<allLanguages.length && res.length < (allLanguages.length/Math.pow(2,qParams.length))) {
+ let d = allLanguages[i++];
+ d.count = Math.max(Math.floor(d.count / 2), 1);
+ res.push(d);
+ }
+
+ return new Collection('language', res);
+ } else {
+ return languages.all();
+ }
+
+ });
this.get('/stats/geostats', 'geostats');
this.get('/stats/themes', 'themes');
this.get('/stats/discourses', ({discourses}, request) => {
- console.log("DISCOURSES", request.queryParams, discourses.all(), discourses.all().models);
- if(request.queryParams['discourse']) {
+ let qParams = request.queryParams['discourse'];
+ if(qParams) {
var res = [];
- request.queryParams.discourse.forEach( did => {
- let d = discourses.find(did);
- if(d) {
- res.push(d);
- }
- });
let allDiscourses = discourses.all().models;
let i=0;
- while(i<allDiscourses.length && res.length < (allDiscourses.length/2)) {
+ while(i<allDiscourses.length && res.length < (allDiscourses.length/Math.pow(2,qParams.length))) {
let d = allDiscourses[i++];
if(!_.contains(request.queryParams.discourse, d.id)) {
- d.count = Math.floor(d.count / 2);
+ d.count = Math.max(Math.floor(d.count / 2), 1);
res.push(d);
}
}
- console.log("DISCOURSES", request.queryParams, { modelName: "discourse", models: res });
- //return discourses.all();
+
return new Collection('discourse', res);
} else {
return discourses.all();
--- a/cms/app-client/package.json Sun Dec 04 13:49:44 2016 +0100
+++ b/cms/app-client/package.json Fri Dec 09 11:41:15 2016 +0100
@@ -31,7 +31,6 @@
"ember-cli": "2.10.0",
"ember-cli-app-version": "^2.0.0",
"ember-cli-babel": "^5.1.7",
- "ember-cli-d3": "1.1.6",
"ember-cli-dependency-checker": "^1.3.0",
"ember-cli-htmlbars": "^1.0.10",
"ember-cli-htmlbars-inline-precompile": "^0.3.3",
@@ -44,6 +43,7 @@
"ember-cli-sri": "^2.1.0",
"ember-cli-test-loader": "^1.1.0",
"ember-cli-uglify": "^1.2.0",
+ "ember-d3": "0.3.0",
"ember-data": "^2.10.0",
"ember-data-fixture-adapter": "1.13.0",
"ember-disable-proxy-controllers": "^1.0.1",
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/tests/unit/models/language-test.js Fri Dec 09 11:41:15 2016 +0100
@@ -0,0 +1,12 @@
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('language', 'Unit | Model | language', {
+ // Specify the other units that are required for this test.
+ needs: []
+});
+
+test('it exists', function(assert) {
+ let model = this.subject();
+ // let store = this.store();
+ assert.ok(!!model);
+});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/app-client/tests/unit/serializers/language-test.js Fri Dec 09 11:41:15 2016 +0100
@@ -0,0 +1,15 @@
+import { moduleForModel, test } from 'ember-qunit';
+
+moduleForModel('language', 'Unit | Serializer | language', {
+ // Specify the other units that are required for this test.
+ needs: ['serializer:language']
+});
+
+// Replace this with your real tests.
+test('it serializes records', function(assert) {
+ let record = this.subject();
+
+ let serializedRecord = record.serialize();
+
+ assert.ok(serializedRecord);
+});