<commit_before>import React from 'react';
import FalcorController from 'lib/falcor/FalcorController';
import Helmet from 'react-helmet';
import NotFound from 'components/main/NotFound';
import InteractiveArticleLoad from 'transitions/InteractiveArticleLoad';

export default class InteractiveArticle extends FalcorController {
  static getFalcorPathSets(params) {
    return [
      // Fetch article metadata
      ['articles', 'bySlug', params.articleSlug,
        ['title', 'teaser', 'slug', 'image', 'published_at']],
      // Fetch interactive article html/js/css
      // For now we only use the html part, the js and css parts are for further improvements
      ['articles', 'bySlug', params.articleSlug, 'interactiveData', ['html', 'js', 'css']],
    ];
  }

  render() {
    if (this.state.ready) {
      if (!this.state.data) {
        return <NotFound />;
      }
      const articleSlug = this.props.params.articleSlug;
      const publishDate = this.state.data.articles.bySlug[articleSlug].published_at;
      if (!publishDate) {
        return <NotFound />;
      }
      // Access data fetched via Falcor
      const articleData = this.state.data.articles.bySlug[articleSlug];
      const interactiveCode = articleData.interactiveData;
      // make sure article meta image has default
      const articleMetaImage = articleData.image || 'https://thegazelle.s3.amazonaws.com/gazelle/2016/02/saadiyat-reflection.jpg';
      const meta = [
        // Search results
        { name: 'description', content: articleData.teaser },

        // Social media sharing
        { property: 'og:title', content: `${articleData.title} | The Gazelle` },
        { property: 'og:type', content: 'article' },
        {
          property: 'og:url',
          content: `www.thegazelle.org/interactive/${articleData.slug}/`,
        },
        { property: 'og:image', content: articleMetaImage },
        { property: 'og:image:width', content: '540' }, // 1.8:1 ratio
        { property: 'og:image:height', content: '300' },
        { property: 'og:description', content: articleData.teaser },
        { property: 'og:site_name', content: 'The Gazelle' },
      ];
      const reactHtml = {
        __html: interactiveCode.html,
      };
      return (
        <div>
          <Helmet
            meta={meta}
            title={`${articleData.title} | The Gazelle`}
          />
          <div dangerouslySetInnerHTML={reactHtml} />
        </div>
      );
    }
    return <InteractiveArticleLoad />;
  }
}<commit_msg>Properly evaluate javascript in interactive articles (#286)<commit_after>import React from 'react';
import FalcorController from 'lib/falcor/FalcorController';
import Helmet from 'react-helmet';
import NotFound from 'components/main/NotFound';
import InteractiveArticleLoad from 'transitions/InteractiveArticleLoad';

export default class InteractiveArticle extends FalcorController {

  constructor(props) {
    super(props);
    this.safeSetState({
      didEval: false,
    });
    this.evaluateJavascript = this.evaluateJavascript.bind(this);
  }

  static getFalcorPathSets(params) {
    return [
      // Fetch article metadata
      ['articles', 'bySlug', params.articleSlug,
        ['title', 'teaser', 'slug', 'image', 'published_at']],
      // Fetch interactive article html/js/css
      // For now we only use the html part, the js and css parts are for further improvements
      ['articles', 'bySlug', params.articleSlug, 'interactiveData',
        ['html', 'js', 'css']],
    ];
  }

  evaluateJavascript() {
    const articleData = this.state.data.articles.bySlug[this.props.params.articleSlug];
    // eslint-disable-next-line no-eval
    eval(articleData.interactiveData.js || '');
    this.safeSetState({ didEval: true });
  }

  componentDidMount() {
    super.componentDidMount();
    if (this.state.ready && this.state.data && document.getElementById('interactive-root')) {
      this.evaluateJavascript();
    }
  }

  componentDidUpdate() {
    if (
      this.state.ready &&
      this.state.data &&
      !this.state.didEval &&
      document.getElementById('interactive-root')
    ) {
      this.evaluateJavascript();
    }
  }

  render() {
    if (this.state.ready) {
      if (!this.state.data) {
        return <NotFound />;
      }

      const articleSlug = this.props.params.articleSlug;
      const publishDate = this.state.data.articles.bySlug[articleSlug].published_at;
      if (!publishDate) {
        return <NotFound />;
      }
      // Access data fetched via Falcor
      const articleData = this.state.data.articles.bySlug[articleSlug];
      const interactiveCode = articleData.interactiveData;
      // make sure article meta image has default
      const articleMetaImage = articleData.image || 'https://thegazelle.s3.amazonaws.com/gazelle/2016/02/saadiyat-reflection.jpg';
      const meta = [
        // Search results
        { name: 'description', content: articleData.teaser },

        // Social media sharing
        { property: 'og:title', content: `${articleData.title} | The Gazelle` },
        { property: 'og:type', content: 'article' },
        {
          property: 'og:url',
          content: `www.thegazelle.org/interactive/${articleData.slug}/`,
        },
        { property: 'og:image', content: articleMetaImage },
        { property: 'og:image:width', content: '540' }, // 1.8:1 ratio
        { property: 'og:image:height', content: '300' },
        { property: 'og:description', content: articleData.teaser },
        { property: 'og:site_name', content: 'The Gazelle' },
      ];
      const reactHtml = {
        __html: `<div id="interactive-root">${interactiveCode.html}</div>`,
      };
      return (
        <div>
          <Helmet
            meta={meta}
            title={`${articleData.title} | The Gazelle`}
          />
          <div dangerouslySetInnerHTML={reactHtml} />
        </div>
      );
    }
    return <InteractiveArticleLoad />;
  }
}
{'fraction_non_alphanumeric': 0.1356986715991256, 'fraction_numerical': 0.005885320329577938, 'mean_word_length': 2.7128589263420726, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, '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': '5925556', 'n_tokens_mistral': 1788, 'n_tokens_neox': 1730, 'n_words': 380}