<commit_before>import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import Card from 'material-ui/lib/card/card';
import CardActions from 'material-ui/lib/card/card-actions';
import CardHeader from 'material-ui/lib/card/card-header';
import CardMedia from 'material-ui/lib/card/card-media';
import CardTitle from 'material-ui/lib/card/card-title';
import FlatButton from 'material-ui/lib/flat-button';
import CardText from 'material-ui/lib/card/card-text';
import FloatingActionButton from 'material-ui/lib/floating-action-button';
import Slider from 'material-ui/lib/slider';
import List from 'material-ui/lib/lists/list';
import ListItem from 'material-ui/lib/lists/list-item';
import Avatar from 'material-ui/lib/avatar';
import { Link } from 'react-router';
import styles from './Viewer.module.css';
import TagLabels from './TagLabels';
import DummyScreen from './DummyScreen';

// Constants
const Keys = {
  ENTER: 13,
  ESCAPE: 27,
  LEFT_ARROW: 37,
  UP_ARROW: 38,
  RIGHT_ARROW: 39,
  DOWN_ARROW: 40,
  SPACE: 32,
  B: 66
};

const STEP = 10;

class Viewer extends Component {

  constructor(props, context) {
    super(props, context)
    this.state = { showBar: true, playing: true, duration: 10, currentTime: 0, thumbnail: false, sliderPos: 0, fullscreen: false }
    _.bindAll(this, 'onMouseMove', 'hideActionBar',
      'stepBackward', 'play', 'stop', 'stepForward',
      'handleKeyDown', 'handleFocus', 'handleMouseOver',
      'handleMouseOut', 'handleSlider', 'handleSliderChange', 'createThumbnail',
      'goBookmark'
    )
  }

  componentDidMount() {
    document.addEventListener('keydown', this.handleKeyDown);
    this.videoContainer = ReactDOM.findDOMNode(this.refs.videocard)
    let track = ReactDOM.findDOMNode(this.refs.slider.refs.track)
    this.trackLeft = this.refs.slider._getTrackLeft()
    this.trackWidth = track.clientWidth;
    this.trackY = track.getBoundingClientRect().top;
    this.hideActionBar();
    this.refs.video.addEventListener("loadedmetadata", () => {
      this.setState({ duration: this.refs.video.duration })
    });
    this.refs.video.addEventListener("timeupdate", () => {
      this.setState({ currentTime: Math.floor(this.refs.video.currentTime) })
    });
    this.state = {
      file: this.props.location.state.file,
      howBar: true,
      playing: true,
      duration: 10,
      currentTime: 0,
      thumbnail: false,
      thumbnailPos: null,
      fullscreen: false
    }
  }

  componentWillUnmount() {
    document.removeEventListener('keydown', this.handleKeyDown);
    clearTimeout(this.hiddenTimer);
  }

  onMouseMove() {
    this.setState({ showBar: true })
    this.hideActionBar();
  }

  // hide action bar after 3 sec from last operation
  hideActionBar() {
    clearTimeout(this.hiddenTimer);
    this.hiddenTimer = setTimeout( () => {
      this.setState({ showBar: false, thumbnail: false })
    }, 3000);
  }

  stepBackward() {
    this.refs.video.currentTime -= STEP
  };

  play() {
    this.refs.video.play();
    this.setState({ playing: true })
  };

  stop() {
    this.refs.video.pause();
    this.setState({ playing: false })
  };

  stepForward() {
    this.refs.video.currentTime += STEP
  };

  fullscreen() {
    this.videoContainer.webkitRequestFullscreen();
    this.setState({ fullscreen: true })
  }
  exitFullscreen() {
    document.webkitExitFullscreen();
    this.setState({ fullscreen: false })
  }
  evacuate() {
    if (this.state.evacuate) {
      this.setState({ evacuate: false })
    } else {
      this.stop()
      this.exitFullscreen();
      this.setState({ evacuate: true })
    }
  }

  handleKeyDown(e) {
    if (this.state.evacuate && e.keyCode !== Keys.SPACE) {
      return;
    }
    const eventDispatcher = {
      [Keys.LEFT_ARROW]:  () => this.stepBackward(),
      [Keys.RIGHT_ARROW]: () => this.stepForward(),
      [Keys.UP_ARROW]:    () => this.handleNextBookmark(),
      [Keys.DOWN_ARROW]:  () => this.handlePrevBookmark(),
      [Keys.ENTER]:       () => this.state.playing ? this.stop() : this.play(),
      [Keys.ESCAPE]:      () => this.exitFullscreen(),
      [Keys.SPACE]:       () => this.evacuate(),
      [Keys.B]:           () => this.handleAddBookmark()
    }

    let dispatcher = eventDispatcher[e.keyCode]

    if (!dispatcher) { return; }

    e.preventDefault();
    dispatcher();
  }

  handleFocus(e) {
    console.log("+++ handleFocus")
  }
  handleMouseOver(e) {
    this.setState({ thumbnail: true })
  }
  handleMouseOut(e) {
    this.setState({ thumbnail: false })
  }
  handleSlider(e) {
    let pos = e.clientX - this.trackLeft
    if (pos < 0) pos = 0; else if (pos > this.trackWidth) pos = this.trackWidth;
    let percent = Math.round(pos * 100 / this.trackWidth) / 100
    this.refs.preview.currentTime = this.refs.video.duration * percent
    this.setState({ thumbnailPos: e.clientX })
  }
  handleSliderChange(e, value) {
    this.refs.video.currentTime = Math.floor(value)
  }

  handleAddBookmark(e) {
    this.props.addBookmark(this.state.file, this.refs.video.currentTime)
  }

  handlePrevBookmark(e) {
    let bookmarks = this.state.file.bookmarks
    let currentTime = this.refs.video.currentTime
    let bookmark = _.findLast(bookmarks, (bookmark) => bookmark < currentTime )
    if (!bookmark) { return }
    this.goBookmark(bookmark)
  }

  handleNextBookmark(e) {
    let bookmarks = this.state.file.bookmarks
    let currentTime = this.refs.video.currentTime
    let bookmark = _.find(bookmarks, (bookmark) => bookmark > currentTime )
    if (!bookmark) { return }
    this.goBookmark(bookmark)
  }

  goBookmark(bookmark) {
    this.refs.video.currentTime = bookmark
  }

  removeBookmark(e, bookmark) {
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
    this.props.removeBookmark(this.state.file, bookmark);
  }

  mmss(sec) {
    const pad = (val) => { return ("0" + val).slice(-2); };
    let ss = sec % 60 || 0;
    let mm = (sec - ss) / 60 || 0;
    return (pad(mm) + ":" + pad(ss))
  }

  createSlider() {
    return (
      <div>
        <Slider
          ref="slider"
          max={this.state.duration}
          value={this.state.currentTime}
          onChange={this.handleSliderChange}
          onFocus={this.handleFocus}
          onMouseOver={this.handleMouseOver}
          onMouseOut={this.handleMouseOut}
          onMouseMove={this.handleSlider}
        />
      </div>
    )
  }

  createThumbnail() {
    let pos = this.state.thumbnailPos || 0
    const width = 213;
    const height = 120;
    const style = {
      display: this.state.thumbnail ? 'block' : 'none',
      width: width + 'px',
      height: height + 'px',
      position: 'absolute',
      left: (pos - width / 2) + 'px',
      top: (this.trackY - height / 2 - 20) + 'px',
      zIndex: 1000
    };
    let filePath = this.props.dirPath + "/" + this.props.params.filename
    return (
      <div style={style}>
        <video src={filePath} ref="preview" width="213px" height="120px" />
      </div>
    )
  }

  controlButtons() {
    let btn = (icon, shortcut, handler) => {
      return (
        <FlatButton primary={true} onClick={handler.bind(this)} className="video-btn">
          <i className={`fa fa-${icon}`} /><br />
          <span className="video-btn-shortcut">{shortcut}</span><br />
        </FlatButton>
      )
    }
    return (
      <table className="video-control-panel">
        <tbody>
        <tr>
          <td width="40%">
            { btn("step-backward", "←", this.stepBackward) }
            { this.state.playing ?
              btn("stop", "ENT", this.stop) :
              btn("play", "ENT", this.play) }
            { btn("step-forward", "→", this.stepForward) }
            { btn("bookmark", "B", this.handleAddBookmark) }
            { btn("fast-backward", "↓", this.handlePrevBookmark) }
            { btn("fast-forward", "↑", this.handleNextBookmark) }
            { this.state.fullscreen ?
              btn("compress", "ESC", this.exitFullscreen) :
              btn("expand", "F", this.fullscreen) }
          </td>
          <td width="50%">
            { this.createSlider() }
          </td>
          <td>
            <span className="video-current-time">&nbsp;&nbsp;{this.mmss(this.state.currentTime)}</span><commit_msg>Exchange shortcut key for bookmark prev and next<commit_after>import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import Card from 'material-ui/lib/card/card';
import CardActions from 'material-ui/lib/card/card-actions';
import CardHeader from 'material-ui/lib/card/card-header';
import CardMedia from 'material-ui/lib/card/card-media';
import CardTitle from 'material-ui/lib/card/card-title';
import FlatButton from 'material-ui/lib/flat-button';
import CardText from 'material-ui/lib/card/card-text';
import FloatingActionButton from 'material-ui/lib/floating-action-button';
import Slider from 'material-ui/lib/slider';
import List from 'material-ui/lib/lists/list';
import ListItem from 'material-ui/lib/lists/list-item';
import Avatar from 'material-ui/lib/avatar';
import { Link } from 'react-router';
import styles from './Viewer.module.css';
import TagLabels from './TagLabels';
import DummyScreen from './DummyScreen';

// Constants
const Keys = {
  ENTER: 13,
  ESCAPE: 27,
  LEFT_ARROW: 37,
  UP_ARROW: 38,
  RIGHT_ARROW: 39,
  DOWN_ARROW: 40,
  SPACE: 32,
  B: 66
};

const STEP = 10;

class Viewer extends Component {

  constructor(props, context) {
    super(props, context)
    this.state = { showBar: true, playing: true, duration: 10, currentTime: 0, thumbnail: false, sliderPos: 0, fullscreen: false }
    _.bindAll(this, 'onMouseMove', 'hideActionBar',
      'stepBackward', 'play', 'stop', 'stepForward',
      'handleKeyDown', 'handleFocus', 'handleMouseOver',
      'handleMouseOut', 'handleSlider', 'handleSliderChange', 'createThumbnail',
      'goBookmark'
    )
  }

  componentDidMount() {
    document.addEventListener('keydown', this.handleKeyDown);
    this.videoContainer = ReactDOM.findDOMNode(this.refs.videocard)
    let track = ReactDOM.findDOMNode(this.refs.slider.refs.track)
    this.trackLeft = this.refs.slider._getTrackLeft()
    this.trackWidth = track.clientWidth;
    this.trackY = track.getBoundingClientRect().top;
    this.hideActionBar();
    this.refs.video.addEventListener("loadedmetadata", () => {
      this.setState({ duration: this.refs.video.duration })
    });
    this.refs.video.addEventListener("timeupdate", () => {
      this.setState({ currentTime: Math.floor(this.refs.video.currentTime) })
    });
    this.state = {
      file: this.props.location.state.file,
      howBar: true,
      playing: true,
      duration: 10,
      currentTime: 0,
      thumbnail: false,
      thumbnailPos: null,
      fullscreen: false
    }
  }

  componentWillUnmount() {
    document.removeEventListener('keydown', this.handleKeyDown);
    clearTimeout(this.hiddenTimer);
  }

  onMouseMove() {
    this.setState({ showBar: true })
    this.hideActionBar();
  }

  // hide action bar after 3 sec from last operation
  hideActionBar() {
    clearTimeout(this.hiddenTimer);
    this.hiddenTimer = setTimeout( () => {
      this.setState({ showBar: false, thumbnail: false })
    }, 3000);
  }

  stepBackward() {
    this.refs.video.currentTime -= STEP
  };

  play() {
    this.refs.video.play();
    this.setState({ playing: true })
  };

  stop() {
    this.refs.video.pause();
    this.setState({ playing: false })
  };

  stepForward() {
    this.refs.video.currentTime += STEP
  };

  fullscreen() {
    this.videoContainer.webkitRequestFullscreen();
    this.setState({ fullscreen: true })
  }
  exitFullscreen() {
    document.webkitExitFullscreen();
    this.setState({ fullscreen: false })
  }
  evacuate() {
    if (this.state.evacuate) {
      this.setState({ evacuate: false })
    } else {
      this.stop()
      this.exitFullscreen();
      this.setState({ evacuate: true })
    }
  }

  handleKeyDown(e) {
    if (this.state.evacuate && e.keyCode !== Keys.SPACE) {
      return;
    }
    const eventDispatcher = {
      [Keys.LEFT_ARROW]:  () => this.stepBackward(),
      [Keys.RIGHT_ARROW]: () => this.stepForward(),
      [Keys.UP_ARROW]:    () => this.handlePrevBookmark(),
      [Keys.DOWN_ARROW]:  () => this.handleNextBookmark(),
      [Keys.ENTER]:       () => this.state.playing ? this.stop() : this.play(),
      [Keys.ESCAPE]:      () => this.exitFullscreen(),
      [Keys.SPACE]:       () => this.evacuate(),
      [Keys.B]:           () => this.handleAddBookmark()
    }

    let dispatcher = eventDispatcher[e.keyCode]

    if (!dispatcher) { return; }

    e.preventDefault();
    dispatcher();
  }

  handleFocus(e) {
    console.log("+++ handleFocus")
  }
  handleMouseOver(e) {
    this.setState({ thumbnail: true })
  }
  handleMouseOut(e) {
    this.setState({ thumbnail: false })
  }
  handleSlider(e) {
    let pos = e.clientX - this.trackLeft
    if (pos < 0) pos = 0; else if (pos > this.trackWidth) pos = this.trackWidth;
    let percent = Math.round(pos * 100 / this.trackWidth) / 100
    this.refs.preview.currentTime = this.refs.video.duration * percent
    this.setState({ thumbnailPos: e.clientX })
  }
  handleSliderChange(e, value) {
    this.refs.video.currentTime = Math.floor(value)
  }

  handleAddBookmark(e) {
    this.props.addBookmark(this.state.file, this.refs.video.currentTime)
  }

  handlePrevBookmark(e) {
    let bookmarks = this.state.file.bookmarks
    let currentTime = this.refs.video.currentTime
    let bookmark = _.findLast(bookmarks, (bookmark) => bookmark < currentTime )
    if (!bookmark) { return }
    this.goBookmark(bookmark)
  }

  handleNextBookmark(e) {
    let bookmarks = this.state.file.bookmarks
    let currentTime = this.refs.video.currentTime
    let bookmark = _.find(bookmarks, (bookmark) => bookmark > currentTime )
    if (!bookmark) { return }
    this.goBookmark(bookmark)
  }

  goBookmark(bookmark) {
    this.refs.video.currentTime = bookmark
  }

  removeBookmark(e, bookmark) {
    e.stopPropagation();
    e.nativeEvent.stopImmediatePropagation();
    this.props.removeBookmark(this.state.file, bookmark);
  }

  mmss(sec) {
    const pad = (val) => { return ("0" + val).slice(-2); };
    let ss = sec % 60 || 0;
    let mm = (sec - ss) / 60 || 0;
    return (pad(mm) + ":" + pad(ss))
  }

  createSlider() {
    return (
      <div>
        <Slider
          ref="slider"
          max={this.state.duration}
          value={this.state.currentTime}
          onChange={this.handleSliderChange}
          onFocus={this.handleFocus}
          onMouseOver={this.handleMouseOver}
          onMouseOut={this.handleMouseOut}
          onMouseMove={this.handleSlider}
        />
      </div>
    )
  }

  createThumbnail() {
    let pos = this.state.thumbnailPos || 0
    const width = 213;
    const height = 120;
    const style = {
      display: this.state.thumbnail ? 'block' : 'none',
      width: width + 'px',
      height: height + 'px',
      position: 'absolute',
      left: (pos - width / 2) + 'px',
      top: (this.trackY - height / 2 - 20) + 'px',
      zIndex: 1000
    };
    let filePath = this.props.dirPath + "/" + this.props.params.filename
    return (
      <div style={style}>
        <video src={filePath} ref="preview" width="213px" height="120px" />
      </div>
    )
  }

  controlButtons() {
    let btn = (icon, shortcut, handler) => {
      return (
        <FlatButton primary={true} onClick={handler.bind(this)} className="video-btn">
          <i className={`fa fa-${icon}`} /><br />
          <span className="video-btn-shortcut">{shortcut}</span><br />
        </FlatButton>
      )
    }
    return (
      <table className="video-control-panel">
        <tbody>
        <tr>
          <td width="40%">
            { btn("step-backward", "←", this.stepBackward) }
            { this.state.playing ?
              btn("stop", "ENT", this.stop) :
              btn("play", "ENT", this.play) }
            { btn("step-forward", "→", this.stepForward) }
            { btn("bookmark", "B", this.handleAddBookmark) }
            { btn("fast-backward", "↑", this.handlePrevBookmark) }
            { btn("fast-forward", "↓", this.handleNextBookmark) }
            { this.state.fullscreen ?
              btn("compress", "ESC", this.exitFullscreen) :
              btn("expand", "F", this.fullscreen) }
          </td>
          <td width="50%">
            { this.createSlider() }
          </td>
          <td>
            <span className="video-current-time">&nbsp;&nbsp;{this.mmss(this.state.currentTime)}</span>
{'fraction_non_alphanumeric': 0.15277527752775277, 'fraction_numerical': 0.008520852085208521, 'mean_word_length': 2.786006360745116, 'pattern_counts': {'":': 2, '<': 53, '<?xml version=': 0, '>': 83, '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': '2192477', 'n_tokens_mistral': 5568, 'n_tokens_neox': 5383, 'n_words': 1032}