<commit_before>'use strict'

var VisBubbles = Class.extend({
  init: function(divId, budgetCategory, data) {
    this.container = divId;
    d3.select(this.container).html('');
    this.currentYear = parseInt(d3.select('body').attr('data-year'));
    this.data = data;
    this.budget_category = budgetCategory;
    this.forceStrength = 0.045;
    this.isMobile = window.innerWidth <= 590;
    this.locale = I18n.locale;

    // d3.formatDefaultLocale(d3locale[this.locale]);

    this.margin = {top: 20, right: 10, bottom: 20, left: 10},
    this.width = parseInt(d3.select(this.container).node().getBoundingClientRect().width) - this.margin.left - this.margin.right;
    this.height = this.isMobile ? 320 : 520 - this.margin.top - this.margin.bottom;
    this.center = { x: this.width / 2, y: this.height / 2 };

    this.selectionNode = d3.select(this.container).node();

    this.budgetColor = d3.scaleThreshold()
      .domain([-30, -10, -5, 0, 5, 10, 30, 100])
      .range(['#b2182b','#d6604d','#f4a582','#fddbc7','#f7f7f7','#d1e5f0','#92c5de','#4393c3','#2166ac']);

    this.fontSize = d3.scaleLinear()
      .domain([0, 90])
      .range([0, 22]);

    this.tooltip = d3.select(this.container)
      .append('div')
      .attr('class', 'tooltip');

    this.nodes = [];

    this.simulation = d3.forceSimulation()
      .velocityDecay(0.3)
      .force('x', d3.forceX().strength(this.forceStrength).x(this.center.x))
      .force('y', d3.forceY().strength(this.forceStrength).y(this.center.y))
      .force('charge', d3.forceManyBody().strength(this._charge))
      .force("collide", d3.forceCollide().radius(function(d) { return d.radius + 0.5; }).iterations(2))
      .on('tick', this._ticked.bind(this));

    this.simulation.stop();

    this.svg = d3.select(this.container).append('svg')
      .attr('width', this.width + this.margin.left + this.margin.right)
      .attr('height', this.height + this.margin.top + this.margin.bottom)
      .append('g')
      .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
  },
  render: function() {
    this.updateRender();
  },
  resize: function() {
    this.svg.parent().remove();
    this.init(this.container, this.budget_category, this.data);
    this.render();
  },
  createNodes: function(rawData, year) {
    var data = rawData;
    if(this.locale === 'en') this.locale = 'es';

    this.maxAmount = d3.max(data, function (d) { return d.values[year] }.bind(this));
    this.filtered = data.filter(function(d) { return d.budget_category === this.budget_category; }.bind(this));

    this.radiusScale = d3.scaleSqrt()
      .range(this.isMobile ? [0, 80] : [0, 120])
      .domain([0, this.maxAmount]);

    // Assigns the nodes an initial y before the force takes place
    this.nodeScale = d3.scaleLinear()
      .range([0, 500]) // SVG coordinates
      .domain([80, -80]) // Percentage diff between this year and last
      .clamp(true);

    // If we enter for the first time, we build the data
    // If we update, we update the data but not the x and the y
    if (!this.nodes.length > 0) {
      this.nodes = this.filtered.map(function (d) {
        return {
          values: d.values,
          pct_diffs: d.pct_diff,
          id: d.id,
          values_per_inhabitant: d.values_per_inhabitant,
          radius: d.values[year] ? this.radiusScale(d.values[year]) : 0,
          value: d.values[year],
          name: d['level_2_' + this.locale],
          pct_diff: d.pct_diff[year],
          per_inhabitant: d.values_per_inhabitant[year],
          x: Math.random() * 600,
          y: d.pct_diff[year] ? this.nodeScale(d.pct_diff[year]) : 0,
          year: year
        };
      }.bind(this))
    } else {
      this.nodes.forEach(function(d) {
        d.radius = this.radiusScale(d.values[year])
        d.radius = d.values[year] ? this.radiusScale(d.values[year]) : 0
        d.value = d.values[year]
        d.pct_diff = d.pct_diffs[year]
        d.per_inhabitant = d.values_per_inhabitant[year]
        d.year = year
      }.bind(this))
    }

    this.nodes.sort(function (a, b) { return b.value - a.value; });

    return this.nodes;
  },
  update: function(year) {
    var t = d3.transition()
      .duration(500);

    this.nodes = this.createNodes(this.data, year);
    this.bubbles.data(this.nodes, function (d) { return d.id; })

    d3.selectAll('.bubble')
      .data(this.nodes, function (d) { return d.name; })
      .attr('class', function(d) { return 'bubble bubble-' + d.year})
      .transition(t)
      .attr('r', function (d) { return d.radius; })
      .attr('fill', function(d) { return this.budgetColor(d.pct_diff)}.bind(this))

    d3.selectAll('.bubble-g text')
      .data(this.nodes, function (d) { return d.name; })
      .transition(t)
      .attr('fill', function(d) { return d.pct_diff > 30 || d.pct_diff < -10 ? 'white' : 'black'; })
      .style('font-size', function(d) { return this.fontSize(d.radius) + 'px'; }.bind(this))

    this.simulation.nodes(this.nodes)
    this.simulation.alpha(1).restart();
  },
  updateRender: function() {

    // var budgetCategory = this.budget_category;
    this.nodes = this.createNodes(this.data, this.currentYear);

    this.bubbles = this.svg.selectAll('g')
      .data(this.nodes, function (d) { return d.name; })
      .enter()
      .append('g')
      .attr('class', 'bubble-g');

    var bubblesG = this.bubbles.append('a')
      .attr('xlink:href', function(d) {
        return this.budget_category === 'income' ? '/presupuestos/partidas/' + d.id + '/' + d.year + '/economic/I' : '/presupuestos/partidas/' + d.id + '/' + d.year + '/functional/G';
      }.bind(this))
      .attr('target', '_top')
      .append('circle')
      .attr('class', function(d) { return d.year + ' bubble'})
      .attr('r', function (d) { return d.radius; })
      .attr('fill', function(d) { return this.budgetColor(d.pct_diff)}.bind(this))
      .attr('stroke-width', 2)<commit_msg>Fix bubbles graph links<commit_after>'use strict'

var VisBubbles = Class.extend({
  init: function(divId, budgetCategory, data) {
    this.container = divId;
    d3.select(this.container).html('');
    this.currentYear = parseInt(d3.select('body').attr('data-year'));
    this.data = data;
    this.budget_category = budgetCategory;
    this.forceStrength = 0.045;
    this.isMobile = window.innerWidth <= 590;
    this.locale = I18n.locale;

    // d3.formatDefaultLocale(d3locale[this.locale]);

    this.margin = {top: 20, right: 10, bottom: 20, left: 10},
    this.width = parseInt(d3.select(this.container).node().getBoundingClientRect().width) - this.margin.left - this.margin.right;
    this.height = this.isMobile ? 320 : 520 - this.margin.top - this.margin.bottom;
    this.center = { x: this.width / 2, y: this.height / 2 };

    this.selectionNode = d3.select(this.container).node();

    this.budgetColor = d3.scaleThreshold()
      .domain([-30, -10, -5, 0, 5, 10, 30, 100])
      .range(['#b2182b','#d6604d','#f4a582','#fddbc7','#f7f7f7','#d1e5f0','#92c5de','#4393c3','#2166ac']);

    this.fontSize = d3.scaleLinear()
      .domain([0, 90])
      .range([0, 22]);

    this.tooltip = d3.select(this.container)
      .append('div')
      .attr('class', 'tooltip');

    this.nodes = [];

    this.simulation = d3.forceSimulation()
      .velocityDecay(0.3)
      .force('x', d3.forceX().strength(this.forceStrength).x(this.center.x))
      .force('y', d3.forceY().strength(this.forceStrength).y(this.center.y))
      .force('charge', d3.forceManyBody().strength(this._charge))
      .force("collide", d3.forceCollide().radius(function(d) { return d.radius + 0.5; }).iterations(2))
      .on('tick', this._ticked.bind(this));

    this.simulation.stop();

    this.svg = d3.select(this.container).append('svg')
      .attr('width', this.width + this.margin.left + this.margin.right)
      .attr('height', this.height + this.margin.top + this.margin.bottom)
      .append('g')
      .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');
  },
  render: function() {
    this.updateRender();
  },
  resize: function() {
    this.svg.parent().remove();
    this.init(this.container, this.budget_category, this.data);
    this.render();
  },
  createNodes: function(rawData, year) {
    var data = rawData;
    if(this.locale === 'en') this.locale = 'es';

    this.maxAmount = d3.max(data, function (d) { return d.values[year] }.bind(this));
    this.filtered = data.filter(function(d) { return d.budget_category === this.budget_category; }.bind(this));

    this.radiusScale = d3.scaleSqrt()
      .range(this.isMobile ? [0, 80] : [0, 120])
      .domain([0, this.maxAmount]);

    // Assigns the nodes an initial y before the force takes place
    this.nodeScale = d3.scaleLinear()
      .range([0, 500]) // SVG coordinates
      .domain([80, -80]) // Percentage diff between this year and last
      .clamp(true);

    // If we enter for the first time, we build the data
    // If we update, we update the data but not the x and the y
    if (!this.nodes.length > 0) {
      this.nodes = this.filtered.map(function (d) {
        return {
          values: d.values,
          pct_diffs: d.pct_diff,
          id: d.id,
          values_per_inhabitant: d.values_per_inhabitant,
          radius: d.values[year] ? this.radiusScale(d.values[year]) : 0,
          value: d.values[year],
          name: d['level_2_' + this.locale],
          pct_diff: d.pct_diff[year],
          per_inhabitant: d.values_per_inhabitant[year],
          x: Math.random() * 600,
          y: d.pct_diff[year] ? this.nodeScale(d.pct_diff[year]) : 0,
          year: year
        };
      }.bind(this))
    } else {
      this.nodes.forEach(function(d) {
        d.radius = this.radiusScale(d.values[year])
        d.radius = d.values[year] ? this.radiusScale(d.values[year]) : 0
        d.value = d.values[year]
        d.pct_diff = d.pct_diffs[year]
        d.per_inhabitant = d.values_per_inhabitant[year]
        d.year = year
      }.bind(this))
    }

    this.nodes.sort(function (a, b) { return b.value - a.value; });

    return this.nodes;
  },
  update: function(year) {
    var t = d3.transition()
      .duration(500);

    this.nodes = this.createNodes(this.data, year);
    this.bubbles.data(this.nodes, function (d) { return d.id; })

    d3.selectAll('.bubble')
      .data(this.nodes, function (d) { return d.name; })
      .attr('class', function(d) { return 'bubble bubble-' + d.year})
      .transition(t)
      .attr('r', function (d) { return d.radius; })
      .attr('fill', function(d) { return this.budgetColor(d.pct_diff)}.bind(this))

    d3.selectAll('.bubble-g text')
      .data(this.nodes, function (d) { return d.name; })
      .transition(t)
      .attr('fill', function(d) { return d.pct_diff > 30 || d.pct_diff < -10 ? 'white' : 'black'; })
      .style('font-size', function(d) { return this.fontSize(d.radius) + 'px'; }.bind(this))

    this.simulation.nodes(this.nodes)
    this.simulation.alpha(1).restart();
  },
  updateRender: function() {

    // var budgetCategory = this.budget_category;
    this.nodes = this.createNodes(this.data, this.currentYear);

    this.bubbles = this.svg.selectAll('g')
      .data(this.nodes, function (d) { return d.name; })
      .enter()
      .append('g')
      .attr('class', 'bubble-g');

    var bubblesG = this.bubbles.append('a')
      .attr('xlink:href', function(d) {
        var currentPath = window.location.pathname;
        var pathSegment = currentPath.match(/places.*\/\d{4}/)[0];
        var placeSlug = pathSegment.replace("places/", "").replace(/\/\d{4}/, "");
        return this.budget_category === 'income' ? '/budget_lines/' + placeSlug + '/' + d.year + '/' + d.id  + '/I/economic' : '/budget_lines/' + placeSlug  + '/' + d.year + '/' + d.id + '/G/functional';
      }.bind(this))
      .attr('target', '_top')
      .append('circle')
      .attr('class', function(d) { return d.year + ' bubble'})
      .attr('r', function (d) { return d.radius; })
      .attr('fill', function(d) { return this.budgetColor(d.pct_diff)}.bind(this))
      .attr('stroke-width', 2)
{'fraction_non_alphanumeric': 0.18324261916542967, 'fraction_numerical': 0.02251360712518555, 'mean_word_length': 2.8147216105693613, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2647939', 'n_tokens_mistral': 4469, 'n_tokens_neox': 4237, 'n_words': 780}