<commit_before>import _                        from 'lodash';
import $                        from 'jquery';
import React                    from 'react';
import { connect }              from 'react-redux';
import Modal                    from './upload_modal/editor_upload_modal';
import TinyWrapper              from './tiny_wrapper';
import guid                     from '../../../utils/guid';
import * as AssetActions        from '../../../actions/qbank/assets';
import { types as GenusTypes }  from '../../../constants/genus_types';

function select(state) {
  return {
    mediaTypes: state.media,
    img: state.media.image,
    video: state.media.video,
    audio: state.media.audio,
    loadingMedia: state.media.loading,
    uploadedAssets: state.uploadedAssets
  };
}

// TODO: figure out how to localize this
export class OeaEditor extends React.Component {
  static propTypes = {
    onBlur: React.PropTypes.func.isRequired,
    bankId: React.PropTypes.string.isRequired,
    placeholder: React.PropTypes.string,
    text: React.PropTypes.string,
    uploadMedia: React.PropTypes.func.isRequired,
    uploadedAssets: React.PropTypes.shape({}),
    fileIds: React.PropTypes.shape({}),
    textSize: React.PropTypes.string,
    error: React.PropTypes.string,
    loadingMedia: React.PropTypes.bool,
  };

  constructor(props) {
    super(props);
    this.state = {
      fileGuids: {},
      focused: false,
      editor: null,
      modalOpen: false,
      mediaType: null,
      mediaGuid: null,
      newText: this.props.text,
    };
  }

  componentWillReceiveProps(nextProps) {
    if (!_.get(this.props.uploadedAssets, this.state.mediaGuid) &&
      _.get(nextProps.uploadedAssets, this.state.mediaGuid)
    ) {
      const media = nextProps.uploadedAssets[this.state.mediaGuid];
      const editorContent = this.getEditorContent(media);

      this.state.editor.insertContent(editorContent);
      this.closeModal();
    }
  }

  onBlur(editorText, isChanged) {
    this.setState({ focused: false, newText: editorText });
    if (!isChanged) return;

    let text = editorText;
    const fileIds = {};

    // we don't want jquery to auto play or make any requests
    text = text.replace(/autoplay/g, 'autoplay-placeholder');
    text = text.replace(/src="/g, 'src-placeholder="');

    const doc = $(`<div>${text}</div>`);
    $('img, source, track', doc).each((i, el) => {
      const media = $(el);
      const match = /.*\/(.*)\/stream$/.exec(media.attr('src-placeholder'));
      if (match) {
        const assetContentId = match[1];
        const mediaGuid = this.findMediaGuid(assetContentId);
        if (mediaGuid) {
          media.attr('src-placeholder', `AssetContent:${mediaGuid}`);
        }
      }
    });

    // Insert transcript tags before sending question text to qbank
    $('source', doc).each((i, el) => {
      const media = $(el);
      const assetContentGuid = media.attr('src-placeholder');
      if (assetContentGuid) {
        const match = assetContentGuid.match('AssetContent:(.+)');
        if (match) {
          const assetContentId = match[1];
          const transcriptGenus = GenusTypes.assets.transcript.transcript;
          const transcriptGuids =
            this.findMetaGuids(assetContentId)
            .filter(file =>
              file.assetContentTypeId === transcriptGenus ||
              file.genusTypeId === transcriptGenus
            );
            // Transcript tags need to be inserted after <audio> and <video> elements
            //  if we have any transcript files that match the video/audio asset
          if (!_.isEmpty(transcriptGuids)) {
            media.parent().after(`<transcript src="AssetContent:${transcriptGuids[0].guid}" />`);
          }
        }
      }
    });


    _.each(this.state.fileGuids, (file, mediaGuid) => {
      // we either uploaded it, or selected it in the modal. Check both places.
      const media = this.props.uploadedAssets[mediaGuid] || this.state.fileGuids[mediaGuid];
      if (media && !media.error) {
        const type = media.type && media.extension
          ? GenusTypes.assets[media.type][media.extension]
          : media.genusTypeId;

        fileIds[mediaGuid] = {
          assetId: media.id,
          assetContentId: media.assetContentId,
          assetContentTypeId: type,
        };
      }
    });

    text = doc.html();
    text = text.replace(/src-placeholder/g, 'src');
    text = text.replace(/autoplay-placeholder/g, 'autoplay');

    this.props.onBlur(text, fileIds);
    this.setState({ fileGuids: {} });
  }

  getEditorContent(media) {
    let editorContent = `<video><source src="${media.url}" /></video>`;
    const alt = _.isEmpty(media.altText) ? '' : media.altText.text;
    const autoPlay = media.autoPlay ? 'autoplay' : '';
    switch (this.state.mediaType) {
      case 'img':
        editorContent = `<img src="${media.url}" alt="${alt}">`;
        break;

      case 'audio':
        editorContent = `<audio ${autoPlay} name="media" controls>` +
        `<source src="${media.url}" type="${this.state.mediaType}/${media.extension}">` +
        '</audio>';
        break;
      case 'video':
        {
          const track = _.isEmpty(media.vtt) ? '' : `<track src="${_.get(media, 'vtt.url')}" srclang="en">`;
          editorContent = `<video ${autoPlay} name="media" controls>` +
            `<source src="${media.url}" type="${this.state.mediaType}/${media.extension}">` +
            `${track}'</video>`;
        }
        break;

      default:
        break;
    }

    return editorContent;
  }

  // Find all assets whose assetId match the asset with assetGuid
  findMetaGuids(assetGuid) {
    const { fileIds, uploadedAssets } = this.props;
    const { fileGuids } = this.state;
    const allAssets = {
      ...fileIds,
      ...uploadedAssets,<commit_msg>fix new media bug (#358)<commit_after>import _                        from 'lodash';
import $                        from 'jquery';
import React                    from 'react';
import { connect }              from 'react-redux';
import Modal                    from './upload_modal/editor_upload_modal';
import TinyWrapper              from './tiny_wrapper';
import guid                     from '../../../utils/guid';
import * as AssetActions        from '../../../actions/qbank/assets';
import { types as GenusTypes }  from '../../../constants/genus_types';

function select(state) {
  return {
    mediaTypes: state.media,
    img: state.media.image,
    video: state.media.video,
    audio: state.media.audio,
    loadingMedia: state.media.loading,
    uploadedAssets: state.uploadedAssets
  };
}

// TODO: figure out how to localize this
export class OeaEditor extends React.Component {
  static propTypes = {
    onBlur: React.PropTypes.func.isRequired,
    bankId: React.PropTypes.string.isRequired,
    placeholder: React.PropTypes.string,
    text: React.PropTypes.string,
    uploadMedia: React.PropTypes.func.isRequired,
    uploadedAssets: React.PropTypes.shape({}),
    fileIds: React.PropTypes.shape({}),
    textSize: React.PropTypes.string,
    error: React.PropTypes.string,
    loadingMedia: React.PropTypes.bool,
  };

  constructor(props) {
    super(props);
    this.state = {
      fileGuids: {},
      focused: false,
      editor: null,
      modalOpen: false,
      mediaType: null,
      mediaGuid: null,
      newText: this.props.text,
    };
  }

  componentWillReceiveProps(nextProps) {
    if (!_.get(this.props.uploadedAssets, this.state.mediaGuid) &&
      _.get(nextProps.uploadedAssets, this.state.mediaGuid)
    ) {
      const media = nextProps.uploadedAssets[this.state.mediaGuid];
      const editorContent = this.getEditorContent(media);

      this.state.editor.insertContent(editorContent);
      this.closeModal();
    }
  }

  onBlur(editorText, isChanged) {
    this.setState({ focused: false, newText: editorText });
    if (!isChanged) return;

    let text = editorText;
    const fileIds = {};

    // we don't want jquery to auto play or make any requests
    text = text.replace(/autoplay/g, 'autoplay-placeholder');
    text = text.replace(/src="/g, 'src-placeholder="');

    const doc = $(`<div>${text}</div>`);
    $('img, source, track', doc).each((i, el) => {
      const media = $(el);
      const match = /.*\/(.*)\/stream$/.exec(media.attr('src-placeholder'));
      if (match) {
        const assetContentId = match[1];
        const mediaGuid = this.findMediaGuid(assetContentId);
        if (mediaGuid) {
          media.attr('src-placeholder', `AssetContent:${mediaGuid}`);
        }
      }
    });

    // Insert transcript tags before sending question text to qbank
    $('source', doc).each((i, el) => {
      const media = $(el);
      const assetContentGuid = media.attr('src-placeholder');
      if (assetContentGuid) {
        const match = assetContentGuid.match('AssetContent:(.+)');
        if (match) {
          const assetContentId = match[1];
          const transcriptGenus = GenusTypes.assets.transcript.transcript;
          const transcriptGuids =
            this.findMetaGuids(assetContentId)
            .filter(file =>
              file.assetContentTypeId === transcriptGenus ||
              file.genusTypeId === transcriptGenus
            );
            // Transcript tags need to be inserted after <audio> and <video> elements
            //  if we have any transcript files that match the video/audio asset
          if (!_.isEmpty(transcriptGuids)) {
            media.parent().after(`<transcript src="AssetContent:${transcriptGuids[0].guid}" />`);
          }
        }
      }
    });


    _.each(this.state.fileGuids, (file, mediaGuid) => {
      // we either uploaded it, or selected it in the modal. Check both places.
      const media = this.props.uploadedAssets[mediaGuid] || this.state.fileGuids[mediaGuid];
      if (media && !media.error) {
        const type = media.type && media.extension
          ? GenusTypes.assets[media.type][media.extension]
          : media.genusTypeId;

        fileIds[mediaGuid] = {
          assetId: media.id,
          assetContentId: media.assetContentId,
          assetContentTypeId: type,
        };
      }
    });

    text = doc.html();
    text = text.replace(/src-placeholder/g, 'src');
    text = text.replace(/autoplay-placeholder/g, 'autoplay');

    this.props.onBlur(text, fileIds);
    this.setState({ fileGuids: {} });
  }

  getEditorContent(media) {
    let editorContent = `<video><source src="${media.url}" /></video>`;
    const alt = _.isEmpty(media.altText) ? '' : media.altText.text;
    const autoPlay = media.autoPlay ? 'autoplay' : '';
    switch (this.state.mediaType) {
      case 'img':
        editorContent = `<img src="${media.url}" alt="${alt}">`;
        break;

      case 'audio':
        editorContent = `<audio ${autoPlay} name="media" controls>` +
        `<source src="${media.url}" type="${this.state.mediaType}/${media.extension}">` +
        '</audio>';
        break;
      case 'video':
        {
          const track = _.isEmpty(media.vtt) ? '' : `<track src="${_.get(media, 'vtt.url')}" srclang="en">`;
          editorContent = `<video ${autoPlay} name="media" controls>` +
            `<source src="${media.url}" type="${this.state.mediaType}/${media.extension}">` +
            `${track}</video>`;
        }
        break;

      default:
        break;
    }

    return editorContent;
  }

  // Find all assets whose assetId match the asset with assetGuid
  findMetaGuids(assetGuid) {
    const { fileIds, uploadedAssets } = this.props;
    const { fileGuids } = this.state;
    const allAssets = {
      ...fileIds,
      ...uploadedAssets,
{'fraction_non_alphanumeric': 0.1457975986277873, 'fraction_numerical': 0.0007718696397941681, 'mean_word_length': 2.4327347659699736, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 43, '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': '4147069', 'n_tokens_mistral': 3495, 'n_tokens_neox': 3410, 'n_words': 737}