<commit_before>import React, { PropTypes } from 'react'
import VbsReactPlayer from './react-player/VbsReactPlayer'
import { ButtonGroup, Button, Popover, OverlayTrigger } from 'react-bootstrap'
import Spinner from '../Spinner';
import VolumeSlider from './VolumeSlider';
import PlayerSpeakers from './PlayerSpeakers';
import PlayerDetection from './PlayerDetection';
import { parseTime } from '../../common/Common';

export class Player extends React.Component {
  static propTypes = {
    mediaId: PropTypes.string.isRequired,
    playerType: PropTypes.string.isRequired,
    mediaState: PropTypes.object,
    markersState: PropTypes.object,
    playerState: PropTypes.object.isRequired,
    hasNextKeywordButton: PropTypes.bool.isRequired,
    hasDownloadButton: PropTypes.bool.isRequired,
    isShowDetection: PropTypes.bool.isRequired,
    isShowKeywordsMarkers: PropTypes.bool.isRequired,
    actions: PropTypes.object.isRequired
  };

  constructor(props) {
    super(props);
    this.state = { // moving slider; state is faster than redux
      seeking: false,
      seekValue: 0,
      volume: 100
    };
  }

  componentWillMount() {
    if (!this.props.mediaState) {
      return false;
    }
    let duration = this.props.mediaState.data.metadata.duration;
    if (duration) {
      this.onDuration(duration);
    }
  }

  componentDidUpdate() {
    let markersState = this.props.markersState;
    if (markersState && markersState.justCreated) {
      this.seekToFirstMarker(markersState);
    }
  }

  togglePlay() {
    (this.props.playerState.playing) ? this.onPause() : this.onPlay();
  }

  onDuration(duration) {
    if (duration && duration > 0) {
      this.props.actions.setDuration(this.props.mediaId, duration);
    }
  }

  onPlay() {
    this.props.actions.play(this.props.mediaId);
  }

  onPause() {
    this.props.actions.pause(this.props.mediaId);
  }

  onProgress({played, loaded}) {
    if (played && this.props.playerState.played !== played) {
      this.props.actions.setPosition(this.props.mediaId, played);
    }
    if (loaded && this.props.playerState.loaded !== loaded) {
      this.props.actions.setBuffered(this.props.mediaId, loaded);
    }
  }

  calcPosition(event) {
    let timelineLeftOffset = this.refs.timeline.getBoundingClientRect().left;
    let clickLeftOffset = event.pageX;
    let timelineWidth = event.currentTarget.clientWidth;
    return ((clickLeftOffset - timelineLeftOffset)) / timelineWidth;
  }

  calcTimeOffset(time) {
    let timelineWidth = this.refs.timeline && this.refs.timeline.clientWidth || 0;
    let duration = this.props.playerState.duration;
    if (!duration) return 0;

    return ((time * timelineWidth) / duration);
  }

  onSeekMouseDown(event) {
    let position = this.calcPosition(event);
    this.setState({
      seeking: true,
      seekValue: position
    });
  }

  onSeekMouseMove(event) {
    if (!this.state.seeking) {
      return false;
    }
    let position = this.calcPosition(event);
    this.setState({
      seekValue: position
    });
  };

  onSeekMouseUp(event) {
    this.setState({ seeking: false });
    let position = this.calcPosition(event);
    this.seekOnPosition(position);
  }

  onSeekMarker(markerTime) {
    this.seekOnPosition(markerTime / this.props.playerState.duration);
  }

  seekToFirstMarker(markersState) {
    let firstMarker = markersState.markers[markersState.markerIds[0]];
    this.props.actions.clearJustCreatedMarkers(this.props.mediaId);
    setTimeout(() => {
      this.onSeekMarker(firstMarker.time);
    }, 100);
  }

  seekToNextMarker() {
    let isLastMarker = true;
    let markersState = this.props.markersState;
    for (let markerId of markersState.markerIds) {
      let marker = markersState.markers[markerId];
      const ONE_SECOND = 1;
      let markerPosition = (marker.time - ONE_SECOND) / this.props.playerState.duration;
      if (markerPosition > this.props.playerState.played) {
        this.seekOnPosition(markerPosition);
        isLastMarker = false;
        return false;
      }
    }
    if (isLastMarker) {
      let firstMarker = markersState.markers[markersState.markerIds[0]];
      this.onSeekMarker(firstMarker.time);
    }
  }

  /*
  * position is a percent value / 100
  * */
  seekOnPosition(position) {
    this.refs.player.seekTo(parseFloat(position));
    this.props.actions.setPosition(this.props.mediaId, position);
    if (this.props.playerType === 'JwPlayer') {
      this.onPlay();
    }
  }

  onVolumeChange(value) {
    this.setState({
      volume: value
    });
  }

  getSlider() {
    let id = 'volume-slider' + this.props.mediaId;
    return (
      <Popover id={id} className="volume-slider">
        <VolumeSlider orientation="vertical"
                      value={this.state.volume}
                      onChange={this.onVolumeChange.bind(this)}/>
      </Popover>
    )
  }

  getMarkers() {
    let markersState = this.props.markersState;
    if (!markersState) return null;

    return markersState.markerIds.map(markerId => {
      let marker = markersState.markers[markerId];
      let position = this.calcTimeOffset(marker.time);
      let offsetStyle = {left: position + 'px'};
      return (
        <a href="#"
           key={'marker-' + markerId}
           className="player__keywords__marker"
           style={offsetStyle}
           onClick={this.onSeekMarker.bind(this, marker.time)} />
      )
    })
  }

  render () {
    let playerState = this.props.playerState;
    if (!playerState || playerState.loading) {
      return (
        <div className="vbs-player"><Spinner/></div>
      );
    }

    let mediaState = this.props.mediaState;
    let duration = this.props.playerState.duration;
    let timePercentValue = (!this.state.seeking) ? (playerState.played) : (this.state.seekValue)
    let sliderValue = timePercentValue * 100;
    let sliderStyles = {left: sliderValue + '%'};
    let progressStyles = {width: sliderValue + '%'};
    let bufferStyles = {width: (playerState.loaded * 100) + '%'};

    let parsedDuration = (duration) ? parseTime(duration) : '--:--';
    let parsedPlayedTime = parseTime(duration * timePercentValue);

    let volume = (this.props.playerType === 'JwPlayer') ? this.state.volume : this.state.volume / 100;

    let speakers = null;
    let transcriptSpeakers = null;
    if ((mediaState && mediaState.transcriptSpeakers.length > 0)) {
      speakers = mediaState.speakers;
      transcriptSpeakers = mediaState.transcriptSpeakers;
    }

    let utterances = mediaState.utterances;

    return (
      <div className="vbs-player">
        <div className="player">
          <ButtonGroup className="player__buttons">
            <Button bsStyle="primary" onClick={this.togglePlay.bind(this)}>
              {playerState.playing && <i className="fa fa-fw fa-pause" />}
              {!playerState.playing && <i className="fa fa-fw fa-play" />}
            </Button>
            {
              this.props.hasNextKeywordButton &&
              <Button onClick={this.seekToNextMarker.bind(this)}>
                <i className="fa fa-fw fa-play fa-rotate-270" />
                <i className="fa fa-share fa-topleft" />
              </Button>
            }
          </ButtonGroup>

          <div className="player__timeline show-keywords">
            <div onMouseDown={this.onSeekMouseDown.bind(this)}
                 onMouseMove={this.onSeekMouseMove.bind(this)}
                 onMouseUp={this.onSeekMouseUp.bind(this)}>
              <div className="player__timeline__slider" style={sliderStyles}></div>
              <div ref="timeline" className="player__timeline__progress">
                <div className="player__timeline__progress__bg">
                  <div className="player__timeline__buffer" style={bufferStyles}></div>
                  {
                    !speakers &&
                    <div className="player__timeline__progress__fg" style={progressStyles}></div>
                  }
                  {
                    speakers &&
                    <PlayerSpeakers speakers={speakers}
                                    transcriptSpeakers={transcriptSpeakers}
                                    calcTimeOffset={this.calcTimeOffset.bind(this)}
                                    duration={duration}
                    />
                  }
                </div>
              </div>
            </div>

            {<commit_msg>ui changes: Collapsed items are now a light grey; Expanded items are white; Tabs are outlined; Keyword Topics are separated by a line<commit_after>import React, { PropTypes } from 'react'
import VbsReactPlayer from './react-player/VbsReactPlayer'
import { ButtonGroup, Button, Popover, OverlayTrigger } from 'react-bootstrap'
import Spinner from '../Spinner';
import VolumeSlider from './VolumeSlider';
import PlayerSpeakers from './PlayerSpeakers';
import PlayerDetection from './PlayerDetection';
import { parseTime } from '../../common/Common';

export class Player extends React.Component {
  static propTypes = {
    mediaId: PropTypes.string.isRequired,
    playerType: PropTypes.string.isRequired,
    mediaState: PropTypes.object,
    markersState: PropTypes.object,
    playerState: PropTypes.object.isRequired,
    hasNextKeywordButton: PropTypes.bool.isRequired,
    hasDownloadButton: PropTypes.bool.isRequired,
    isShowDetection: PropTypes.bool.isRequired,
    isShowKeywordsMarkers: PropTypes.bool.isRequired,
    actions: PropTypes.object.isRequired
  };

  constructor(props) {
    super(props);
    this.state = { // moving slider; state is faster than redux
      seeking: false,
      seekValue: 0,
      volume: 100
    };
  }

  componentWillMount() {
    if (!this.props.mediaState) {
      return false;
    }
    let duration = this.props.mediaState.data.metadata.duration;
    if (duration) {
      this.onDuration(duration);
    }
  }

  componentDidUpdate() {
    let markersState = this.props.markersState;
    if (markersState && markersState.justCreated) {
      this.seekToFirstMarker(markersState);
    }
  }

  togglePlay() {
    (this.props.playerState.playing) ? this.onPause() : this.onPlay();
  }

  onDuration(duration) {
    if (duration && duration > 0) {
      this.props.actions.setDuration(this.props.mediaId, duration);
    }
  }

  onPlay() {
    this.props.actions.play(this.props.mediaId);
  }

  onPause() {
    this.props.actions.pause(this.props.mediaId);
  }

  onProgress({played, loaded}) {
    if (played && this.props.playerState.played !== played) {
      this.props.actions.setPosition(this.props.mediaId, played);
    }
    if (loaded && this.props.playerState.loaded !== loaded) {
      this.props.actions.setBuffered(this.props.mediaId, loaded);
    }
  }

  calcPosition(event) {
    let timelineLeftOffset = this.refs.timeline.getBoundingClientRect().left;
    let clickLeftOffset = event.pageX;
    let timelineWidth = event.currentTarget.clientWidth;
    return ((clickLeftOffset - timelineLeftOffset)) / timelineWidth;
  }

  calcTimeOffset(time) {
    let timelineWidth = this.refs.timeline && this.refs.timeline.clientWidth || 0;
    let duration = this.props.playerState.duration;
    if (!duration) return 0;

    return ((time * timelineWidth) / duration);
  }

  onSeekMouseDown(event) {
    let position = this.calcPosition(event);
    this.setState({
      seeking: true,
      seekValue: position
    });
  }

  onSeekMouseMove(event) {
    if (!this.state.seeking) {
      return false;
    }
    let position = this.calcPosition(event);
    this.setState({
      seekValue: position
    });
  };

  onSeekMouseUp(event) {
    this.setState({ seeking: false });
    let position = this.calcPosition(event);
    this.seekOnPosition(position);
  }

  onSeekMarker(markerTime) {
    this.seekOnPosition(markerTime / this.props.playerState.duration);
  }

  seekToFirstMarker(markersState) {
    let firstMarker = markersState.markers[markersState.markerIds[0]];
    this.props.actions.clearJustCreatedMarkers(this.props.mediaId);
    setTimeout(() => {
      this.onSeekMarker(firstMarker.time);
    }, 100);
  }

  seekToNextMarker() {
    let isLastMarker = true;
    let markersState = this.props.markersState;
    for (let markerId of markersState.markerIds) {
      let marker = markersState.markers[markerId];
      const ONE_SECOND = 1;
      let markerPosition = (marker.time - ONE_SECOND) / this.props.playerState.duration;
      if (markerPosition > this.props.playerState.played) {
        this.seekOnPosition(markerPosition);
        isLastMarker = false;
        return false;
      }
    }
    if (isLastMarker) {
      let firstMarker = markersState.markers[markersState.markerIds[0]];
      this.onSeekMarker(firstMarker.time);
    }
  }

  /*
  * position is a percent value / 100
  * */
  seekOnPosition(position) {
    this.refs.player.seekTo(parseFloat(position));
    this.props.actions.setPosition(this.props.mediaId, position);
    if (this.props.playerType === 'JwPlayer') {
      this.onPlay();
    }
  }

  onVolumeChange(value) {
    this.setState({
      volume: value
    });
  }

  getSlider() {
    let id = 'volume-slider' + this.props.mediaId;
    return (
      <Popover id={id} className="volume-slider">
        <VolumeSlider orientation="vertical"
                      value={this.state.volume}
                      onChange={this.onVolumeChange.bind(this)}/>
      </Popover>
    )
  }

  getMarkers() {
    let markersState = this.props.markersState;
    if (!markersState) return null;

    return markersState.markerIds.map(markerId => {
      let marker = markersState.markers[markerId];
      let position = this.calcTimeOffset(marker.time);
      let offsetStyle = {left: position + 'px'};
      return (
        <a href="#"
           key={'marker-' + markerId}
           className="player__keywords__marker"
           style={offsetStyle}
           onClick={this.onSeekMarker.bind(this, marker.time)} />
      )
    })
  }

  render () {
    let playerState = this.props.playerState;
    if (!playerState || playerState.loading) {
      return (
        <div className="vbs-player"><Spinner/></div>
      );
    }

    let mediaState = this.props.mediaState;
    let duration = this.props.playerState.duration;
    let timePercentValue = (!this.state.seeking) ? (playerState.played) : (this.state.seekValue)
    let sliderValue = timePercentValue * 100;
    let sliderStyles = {left: sliderValue + '%'};
    let progressStyles = {width: sliderValue + '%'};
    let bufferStyles = {width: (playerState.loaded * 100) + '%'};

    let parsedDuration = (duration) ? parseTime(duration) : '--:--';
    let parsedPlayedTime = parseTime(duration * timePercentValue);

    let volume = (this.props.playerType === 'JwPlayer') ? this.state.volume : this.state.volume / 100;

    let speakers = null;
    let transcriptSpeakers = null;
    if ((mediaState && mediaState.transcriptSpeakers.length > 0)) {
      speakers = mediaState.speakers;
      transcriptSpeakers = mediaState.transcriptSpeakers;
    }

    let hasMarkers = this.props.markersState && this.props.markersState.markerIds && this.props.markersState.markerIds.length > 0;

    let utterances = mediaState.utterances;

    return (
      <div className="vbs-player">
        <div className="player">
          <ButtonGroup className="player__buttons">
            <Button bsStyle="primary" onClick={this.togglePlay.bind(this)}>
              {playerState.playing && <i className="fa fa-fw fa-pause" />}
              {!playerState.playing && <i className="fa fa-fw fa-play" />}
            </Button>
            {
              this.props.hasNextKeywordButton &&
              <Button onClick={this.seekToNextMarker.bind(this)} disabled={!hasMarkers} >
                <i className="fa fa-fw fa-play fa-rotate-270" />
                <i className="fa fa-share fa-topleft" />
              </Button>
            }
          </ButtonGroup>

          <div className="player__timeline show-keywords">
            <div onMouseDown={this.onSeekMouseDown.bind(this)}
                 onMouseMove={this.onSeekMouseMove.bind(this)}
                 onMouseUp={this.onSeekMouseUp.bind(this)}>
              <div className="player__timeline__slider" style={sliderStyles}></div>
              <div ref="timeline" className="player__timeline__progress">
                <div className="player__timeline__progress__bg">
                  <div className="player__timeline__buffer" style={bufferStyles}></div>
                  {
                    !speakers &&
                    <div className="player__timeline__progress__fg" style={progressStyles}></div>
                  }
                  {
                    speakers &&
                    <PlayerSpeakers speakers={speakers}
                                    transcriptSpeakers={transcriptSpeakers}
                                    calcTimeOffset={this.calcTimeOffset.bind(this)}
                                    duration={duration}
                    />
                  }
                </div>
              </div>
            </div>

            {
{'fraction_non_alphanumeric': 0.1224561403508772, 'fraction_numerical': 0.0034502923976608185, 'mean_word_length': 2.5768667642752563, 'pattern_counts': {'":': 0, '<': 69, '<?xml version=': 0, '>': 80, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3080444', 'n_tokens_mistral': 5120, 'n_tokens_neox': 4945, 'n_words': 893}