<commit_before>//  addValuesToTransform, moveElementsIntoSVG and addDescriptionAndSourceBelowDiagram
//  are horrible and could break if some classname or other detail changes (e.g. h3 becomes h4)
//  unfortunately the alternative is to refactor the architecture around d3
//  (because d3 hijacks the 'this' keyword), which produces a tremendous amount of pains and would take crazy hours to fix.
//  if the diagram is buggy or not behaving properly - inspect this function, it's probably the cause.

//  to better understand how d3 is used throughout this code: http://alignedleft.com/tutorials/d3/binding-data

import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux'
import d3_save_svg from 'd3-save-svg'
import SvgText from 'svg-text'

import {CHARTS} from '../../config/chartTypes'
import {TABS} from '../../config/tabs'
import {findHeaderGroupForQuery} from '../../lib/queryUtil'
import UrlQuery from '../../lib/UrlQuery'
import {queryResultPresenter} from '../../lib/queryResultPresenter'
import TabBar from '../elements/TabBar'
import ToggleView from '../elements/ToggleView'
import ChartViewModeSelect from '../elements/ChartViewModeSelect'

import {queryToOptions, describeChart} from '../../lib/chartDescriber'
import {getHeaderKey} from '../../lib/regionUtil'
import '../../lib/element-closest'

import FilterBarContainer from './FilterBarContainer'
import CardMetadata from './CardMetadata'
import ChartDescriptionContainer from './ChartDescriptionContainer'
import ShareWidget from './ShareWidget'
import DownloadWidget from './DownloadWidget'

import {trackCronologicalTabOpen, trackBenchmarkTabOpen} from '../../actions/tracking'
import * as ImdiPropTypes from '../proptypes/ImdiPropTypes'

class Card extends Component {

  static propTypes = {
    dispatch: PropTypes.func,
    loading: PropTypes.bool,
    card: ImdiPropTypes.card.isRequired,
    region: ImdiPropTypes.region.isRequired,
    query: PropTypes.object,
    queryResult: PropTypes.array,
    cardsPage: PropTypes.object,
    currentTabName: PropTypes.string,
    headerGroups: PropTypes.array,
    table: PropTypes.object,
    cardsPageName: PropTypes.string.isRequired,
    activeTab: PropTypes.object,
    printable: PropTypes.bool,
    description: PropTypes.string,
    printView: PropTypes.bool,
    
    // really confusing; thisCard is 'this' for this class (Card).
    // access it by calling this.props.thisCard (from children)
    // why? Because d3 hijacks 'this' in child scope after mount.
    // so if we want to use 'this' for Card, we must use this.props.thisCard (in child components)
    thisCard: PropTypes.any
  };

  static contextTypes = {
    linkTo: PropTypes.func,
    goTo: PropTypes.func,
    navigate: PropTypes.func
  };

  constructor(props) {
    super()
    this.state = {
      chartViewMode: 'chart',
      screenshot: null,
      explicitView: false,
      description: null,
      printView: false,
      initialLoadComplete: false
    }

    this.setExplicitView = this.setExplicitView.bind(this)
    this.getUrlToTab = this.getUrlToTab.bind(this)
    this.getShareUrl = this.getShareUrl.bind(this)
    this.moveElementsIntoSVG = this.moveElementsIntoSVG.bind(this)
    this.addValuesToTransform = this.addValuesToTransform.bind(this)
    this.addDescriptionAndSourceBelowDiagram = this.addDescriptionAndSourceBelowDiagram.bind(this)
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps !== this.props || prevState !== this.state) {
      // add title and numbers above graph
      if (this.state.explicitView) {
        this.moveElementsIntoSVG()
      }
      this.addDescriptionAndSourceBelowDiagram()
    }
  }

  // takes a css transform: translate(500, 250) and adds to the X or/and Y value.
  // element is an element containing a transform attribute.
  // X and Y are the values you'd want to add to the existing X and Y.
  addValuesToTransform(element, addX, addY) {

    const transformValues = element.getAttribute('transform').split(',')

    const values = [transformValues[0].split('(')[1], transformValues[1].split(')')[0]]

    if (addX) values[0] = parseInt(values[0], 10) + addX
    if (addY) values[1] = parseInt(values[1], 10) + addY

    element.setAttribute('transform', `translate(${values[0].toString()}, ${values[1].toString()})`)
  }

  moveElementsIntoSVG() {
    let svg = this.toggleList
    if (!svg) return
    svg = svg.querySelector('.chart__svg')
    if (!svg) return

    //  extra height for the svg diagram
    const extraHeightDiagram = 80
    const extraHeightDiagramPyramid = 20

    //  if this chart is pyramidchart - use different padding for colored boxes below chart
    const pyramid = this.props.activeTab.chartKind == 'pyramid'

    //  get all svg elements
    const chart = svg.querySelector('.chart__d3-points')
    const colorExplanation = svg.querySelector('.chart__legend-wrapper')

    //  move chart and colored squares lower
    this.addValuesToTransform(chart, null, extraHeightDiagram)
    this.addValuesToTransform(colorExplanation, null, pyramid ? extraHeightDiagramPyramid : extraHeightDiagram)

    //  get the title
    const title = svg.closest('.toggle-list').querySelector('[data-graph-title]')

    //  add height
    const height = parseInt(svg.getAttribute('height'), 10) + extraHeightDiagram
    svg.setAttribute('height', height)

    const unit = this.props.query.unit[0]

    // Now `svgForAi` can be opened in Illustrator and the text element will render
    // correctly with Helvetica Bold.

    //  adds title above diagam
    const textContent = new SvgText({
      text: `${title.textContent} (${unit})`,
      element: svg,
      maxWidth: svg.clientWidth || 0,
      textOverflow: 'ellipsis',
      className: 'svg-text title',
      style: {
        fontFamily: 'Helvetica'
      }
    })
  }

  //  use refs from each card component that toggles the height.
  //  every class like this is a card. use refs.
  addDescriptionAndSourceBelowDiagram() {

    let svg = this.toggleList
    if (!svg) return
    svg = svg.querySelector('[data-chart]')

    //  extra height for the svg diagram
    const extraHeightSVG = 150
    const paddingBottom = 50
    const spaceBetween = 30

    //  add extra height to svg
    const height = parseInt(svg.getAttribute('height'), 10) + extraHeightSVG
    svg.setAttribute('height', height)

    const parent = svg.closest('[data-card]')
    const description = parent.querySelector('[data-chart-description]')
    const source = parent.querySelector('[data-chart-source]')

    //  adds description below diagram
    // eslint-disable-next-line no-unused-vars
    const descriptionSVGText = new SvgText({
      text: description.textContent,
      element: svg,
      style: {
        fontFamily: 'Helvetica',
      },
      maxWidth: svg.clientWidth || 0,
      textOverflow: 'ellipsis',
      className: 'svg-text text__description',
      verticalAlign: 'bottom'
    })

    //  adds source below diagram
    // eslint-disable-next-line no-unused-vars
    const sourceSVGText = new SvgText({
      text: source.textContent,
      element: svg,
      style: {
        fontFamily: 'Helvetica',
      },
      maxWidth: svg.clientWidth || 0,
      textOverflow: 'ellipsis',
      className: 'svg-text text__source',
      verticalAlign: 'bottom',
    })

    const textDescription = svg.querySelector('.text__description')
    const textSource = svg.querySelector('.text__source')

    const newDescriptionHeight = svg.clientHeight - (spaceBetween + paddingBottom)
    const newSourceHeight = svg.clientHeight - paddingBottom

    this.addValuesToTransform(textDescription, null, newDescriptionHeight)
    this.addValuesToTransform(textSource, null, newSourceHeight)
  }

  getUrlToTab(tab) {
    return this.context.linkTo('/tall-og-statistikk/steder/:region/:cardsPageName/:cardName/:tabName', {
      cardName: this.props.card.name,
      cardsPageName: this.props.cardsPageName,<commit_msg>remove bug when opening multiple cards<commit_after>//  addValuesToTransform, moveElementsIntoSVG and addDescriptionAndSourceBelowDiagram
//  are horrible and could break if some classname or other detail changes (e.g. h3 becomes h4)
//  unfortunately the alternative is to refactor the architecture around d3
//  (because d3 hijacks the 'this' keyword), which produces a tremendous amount of pains and would take crazy hours to fix.
//  if the diagram is buggy or not behaving properly - inspect this function, it's probably the cause.

//  to better understand how d3 is used throughout this code: http://alignedleft.com/tutorials/d3/binding-data

import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux'
import d3_save_svg from 'd3-save-svg'
import SvgText from 'svg-text'

import {CHARTS} from '../../config/chartTypes'
import {TABS} from '../../config/tabs'
import {findHeaderGroupForQuery} from '../../lib/queryUtil'
import UrlQuery from '../../lib/UrlQuery'
import {queryResultPresenter} from '../../lib/queryResultPresenter'
import TabBar from '../elements/TabBar'
import ToggleView from '../elements/ToggleView'
import ChartViewModeSelect from '../elements/ChartViewModeSelect'

import {queryToOptions, describeChart} from '../../lib/chartDescriber'
import {getHeaderKey} from '../../lib/regionUtil'
import '../../lib/element-closest'

import FilterBarContainer from './FilterBarContainer'
import CardMetadata from './CardMetadata'
import ChartDescriptionContainer from './ChartDescriptionContainer'
import ShareWidget from './ShareWidget'
import DownloadWidget from './DownloadWidget'

import {trackCronologicalTabOpen, trackBenchmarkTabOpen} from '../../actions/tracking'
import * as ImdiPropTypes from '../proptypes/ImdiPropTypes'

class Card extends Component {

  static propTypes = {
    dispatch: PropTypes.func,
    loading: PropTypes.bool,
    card: ImdiPropTypes.card.isRequired,
    region: ImdiPropTypes.region.isRequired,
    query: PropTypes.object,
    queryResult: PropTypes.array,
    cardsPage: PropTypes.object,
    currentTabName: PropTypes.string,
    headerGroups: PropTypes.array,
    table: PropTypes.object,
    cardsPageName: PropTypes.string.isRequired,
    activeTab: PropTypes.object,
    printable: PropTypes.bool,
    description: PropTypes.string,
    printView: PropTypes.bool,
    
    // really confusing; thisCard is 'this' for this class (Card).
    // access it by calling this.props.thisCard (from children)
    // why? Because d3 hijacks 'this' in child scope after mount.
    // so if we want to use 'this' for Card, we must use this.props.thisCard (in child components)
    thisCard: PropTypes.any
  };

  static contextTypes = {
    linkTo: PropTypes.func,
    goTo: PropTypes.func,
    navigate: PropTypes.func
  };

  constructor(props) {
    super()
    this.state = {
      chartViewMode: 'chart',
      screenshot: null,
      explicitView: false,
      description: null,
      printView: false,
      initialLoadComplete: false
    }

    this.setExplicitView = this.setExplicitView.bind(this)
    this.getUrlToTab = this.getUrlToTab.bind(this)
    this.getShareUrl = this.getShareUrl.bind(this)
    this.moveElementsIntoSVG = this.moveElementsIntoSVG.bind(this)
    this.addValuesToTransform = this.addValuesToTransform.bind(this)
    this.addDescriptionAndSourceBelowDiagram = this.addDescriptionAndSourceBelowDiagram.bind(this)
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps !== this.props || prevState !== this.state) {
      // add title and numbers above graph
      if (this.state.explicitView) {
        this.moveElementsIntoSVG()
      }
      this.addDescriptionAndSourceBelowDiagram()
    }
  }

  // takes a css transform: translate(500, 250) and adds to the X or/and Y value.
  // element is an element containing a transform attribute.
  // X and Y are the values you'd want to add to the existing X and Y.
  addValuesToTransform(element, addX, addY) {

    const transformValues = element.getAttribute('transform').split(',')

    const values = [transformValues[0].split('(')[1], transformValues[1].split(')')[0]]

    if (addX) values[0] = parseInt(values[0], 10) + addX
    if (addY) values[1] = parseInt(values[1], 10) + addY

    element.setAttribute('transform', `translate(${values[0].toString()}, ${values[1].toString()})`)
  }

  moveElementsIntoSVG() {
    let svg = this.toggleList
    if (!svg) return
    svg = svg.querySelector('.chart__svg')
    if (!svg) return

    //  extra height for the svg diagram
    const extraHeightDiagram = 80
    const extraHeightDiagramPyramid = 20

    //  if this chart is pyramidchart - use different padding for colored boxes below chart
    const pyramid = this.props.activeTab.chartKind == 'pyramid'

    //  get all svg elements
    const chart = svg.querySelector('.chart__d3-points')
    const colorExplanation = svg.querySelector('.chart__legend-wrapper')

    //  move chart and colored squares lower
    this.addValuesToTransform(chart, null, extraHeightDiagram)
    this.addValuesToTransform(colorExplanation, null, pyramid ? extraHeightDiagramPyramid : extraHeightDiagram)

    //  get the title
    const title = svg.closest('.toggle-list').querySelector('[data-graph-title]')

    //  add height
    const height = parseInt(svg.getAttribute('height'), 10) + extraHeightDiagram
    svg.setAttribute('height', height)

    const unit = this.props.query.unit[0]

    // Now `svgForAi` can be opened in Illustrator and the text element will render
    // correctly with Helvetica Bold.

    //  adds title above diagam
    const textContent = new SvgText({
      text: `${title.textContent} (${unit})`,
      element: svg,
      maxWidth: svg.clientWidth || 0,
      textOverflow: 'ellipsis',
      className: 'svg-text title'
    })
  }

  //  use refs from each card component that toggles the height.
  //  every class like this is a card. use refs.
  addDescriptionAndSourceBelowDiagram() {

    let svg = this.toggleList
    if (!svg) return
    svg = svg.querySelector('[data-chart]')

    //  extra height for the svg diagram
    const extraHeightSVG = 150
    const paddingBottom = 50
    const spaceBetween = 30

    //  add extra height to svg
    const height = parseInt(svg.getAttribute('height'), 10) + extraHeightSVG
    svg.setAttribute('height', height)

    const parent = svg.closest('[data-card]')
    const description = parent.querySelector('[data-chart-description]')
    const source = parent.querySelector('[data-chart-source]')

    //  adds description below diagram
    // eslint-disable-next-line no-unused-vars
    const descriptionSVGText = new SvgText({
      text: description.textContent,
      element: svg,
      maxWidth: svg.clientWidth || 0,
      textOverflow: 'ellipsis',
      className: 'svg-text text__description',
      verticalAlign: 'bottom'
    })

    //  adds source below diagram
    // eslint-disable-next-line no-unused-vars
    const sourceSVGText = new SvgText({
      text: source.textContent,
      element: svg,
      maxWidth: svg.clientWidth || 0,
      textOverflow: 'ellipsis',
      className: 'svg-text text__source',
      verticalAlign: 'bottom',
    })

    const textDescription = svg.querySelector('.text__description')
    const textSource = svg.querySelector('.text__source')

    const newDescriptionHeight = svg.clientHeight - (spaceBetween + paddingBottom)
    const newSourceHeight = svg.clientHeight - paddingBottom

    this.addValuesToTransform(textDescription, null, newDescriptionHeight)
    this.addValuesToTransform(textSource, null, newSourceHeight)
  }

  getUrlToTab(tab) {
    return this.context.linkTo('/tall-og-statistikk/steder/:region/:cardsPageName/:cardName/:tabName', {
      cardName: this.props.card.name,
      cardsPageName: this.props.cardsPageName,
{'fraction_non_alphanumeric': 0.11015501423600127, 'fraction_numerical': 0.006200569440050617, 'mean_word_length': 3.506986027944112, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '792607', 'n_tokens_mistral': 4668, 'n_tokens_neox': 4605, 'n_words': 1263}