<commit_before>/* eslint no-unused-expressions: ["error", { "allowTernary": true }] */
import React from 'react';

const PanZoom = React.createClass({

  propTypes: {
    children: React.PropTypes.node,
    enabled: React.PropTypes.bool,
    frameDimensions: React.PropTypes.shape({
      height: React.PropTypes.number,
      width: React.PropTypes.number
    })
  },

  getDefaultProps() {
    return {
      enabled: false,
      frameDimensions: {
        height: 0,
        width: 0
      }
    };
  },

  getInitialState() {
    return {
      panEnabled: false,
      viewBoxDimensions: {
        x: 0,
        y: 0,
        width: 0,
        height: 0
      },
      zooming: false,
      zoomingTimeoutId: null,
      rotation: 0,
      transform: ''
    };
  },

  componentDidMount() {
    // these events enable a user to navigate an image using arrows, +, and - keys,
    // while the user is in pan and zoom mode.
    if (this.props.enabled) {
      addEventListener('keydown', this.frameKeyPan);
      addEventListener('wheel', this.wheelZoom);
    }
  },

  componentWillUpdate(newProps) {
    if (newProps.frameDimensions === this.props.frameDimensions) return;
    this.setState({
      viewBoxDimensions: {
        width: newProps.frameDimensions.width,
        height: newProps.frameDimensions.height,
        x: 0,
        y: 0
      }
    });
  },

  componentWillUnmount() {
    removeEventListener('keydown', this.frameKeyPan);
    removeEventListener('wheel', this.wheelZoom);
  },

  render() {
    const children = React.Children.map(this.props.children, (child) => {
      return React.cloneElement(child, {
        viewBoxDimensions: this.state.viewBoxDimensions,
        panByDrag: this.panByDrag,
        panEnabled: this.state.panEnabled,
        transform: this.state.transform,
        rotation: this.state.rotation
      });
    });
    return (
      <div>
        {children}
        {this.props.enabled ?
          <div className="pan-zoom-controls" >
            <div className="draw-pan-toggle" >
              <div className={this.state.panEnabled ? '' : 'active'} >
                <button title="annotate" className="fa fa-mouse-pointer" onClick={this.togglePanOff} />
              </div>
              <div className={this.state.panEnabled ? 'active' : ''}>
                <button title="pan" ref="pan" className="fa fa-arrows" onClick={this.handleFocus.bind(this, 'pan')} onFocus={this.togglePanOn} onBlur={this.togglePanOff} />
              </div>
            </div>
            <div>
              <button
                title="zoom out"
                ref="zoomOut"
                className={`zoom-out fa fa-minus ${this.cannotZoomOut() ? 'disabled' : ''}`}
                onMouseDown={this.continuousZoom.bind(this, 1.1)}
                onMouseUp={this.stopZoom}
                onKeyDown={this.keyDownZoomButton.bind(this, 1.1)}
                onKeyUp={this.stopZoom}
                onFocus={this.togglePanOn}
                onBlur={this.togglePanOff}
                onClick={this.handleFocus.bind(this, 'zoomOut')}
              />
            </div>
            <div>
              <button
                title="zoom in"
                ref="zoomIn"
                className="zoom-in fa fa-plus"
                onMouseDown={this.continuousZoom.bind(this, 0.9)}
                onMouseUp={this.stopZoom}
                onKeyDown={this.keyDownZoomButton.bind(this, 0.9)}
                onKeyUp={this.stopZoom}
                onFocus={this.togglePanOn}
                onBlur={this.togglePanOff}
                onClick={this.handleFocus.bind(this, 'zoomIn')}
              />
            </div>
            <div>
              <button title="rotate" className={'rotate fa fa-repeat'} onClick={this.rotateClockwise} />
            </div>
            <div>
              <button title="reset zoom levels" className={'reset fa fa-refresh' + (this.cannotResetZoomRotate() ? ' disabled' : '')} onClick={this.zoomReset} />
            </div>
          </div>
          : ''
        }
      </div>
    );
  },

  handleFocus(ref) {
    this.refs[ref].focus();
    this.togglePanOn();
  },

  cannotZoomOut() {
    return this.props.frameDimensions.width === this.state.viewBoxDimensions.width && this.props.frameDimensions.height === this.state.viewBoxDimensions.height;
  },

  cannotResetZoomRotate() {
    return this.cannotZoomOut() && this.state.rotation === 0
  },

  continuousZoom(change) {
    this.clearZoomingTimeout();
    if (change === 0) return;
    this.setState({ zooming: true }, () => {
      let zoomNow = () => {
        // if !this.state.zooming, we don't want to continuously call setTimeout.
        // !this.state.zooming will be the case after a user creates a mouseup event.
        if (!this.state.zooming) return;
        this.zoom(change);
        this.clearZoomingTimeout();
        this.setState({ zoomingTimeoutId: setTimeout(zoomNow, 200) });
      };
      zoomNow();
    });
  },

  clearZoomingTimeout() {
    if (this.state.zoomingTimeoutId) {
      clearTimeout(this.state.zoomingTimeoutId);
    }
  },

  zoom(change) {
    this.clearZoomingTimeout();
    if (!this.state.zooming) return;
    const newNaturalWidth = this.state.viewBoxDimensions.width * change;
    const newNaturalHeight = this.state.viewBoxDimensions.height * change;

    const newNaturalX = this.state.viewBoxDimensions.x - ((newNaturalWidth - this.state.viewBoxDimensions.width) / 2);
    const newNaturalY = this.state.viewBoxDimensions.y - ((newNaturalHeight - this.state.viewBoxDimensions.height) / 2);

    if ((newNaturalWidth > this.props.frameDimensions.width) || (newNaturalHeight * change > this.props.frameDimensions.height)) {
      this.zoomReset();
    } else {
      this.setState({
        viewBoxDimensions: {
          width: newNaturalWidth,
          height: newNaturalHeight,
          x: newNaturalX,
          y: newNaturalY
        }
      });
    }
  },

  keyDownZoomButton(change, e) {
    // only zoom if a user presses enter on the zoom button.
    if (e.which === 13) {
      this.setState({ zooming: true }, () => {
        this.zoom(change);
      });
    }
  },

  stopZoom(e) {
    e.stopPropagation();
    this.setState({ zooming: false });
    this.continuousZoom(0);
  },

  zoomReset() {
    this.setState({
      viewBoxDimensions: {
        width: this.props.frameDimensions.width,
        height: this.props.frameDimensions.height,
        x: 0,
        y: 0
      },
      rotation: 0,
      transform: `rotate(${0} ${this.props.frameDimensions.width / 2} ${this.props.frameDimensions.height / 2})`
    });
  },

  togglePanOn() {
    if (!this.state.panEnabled) this.setState({ panEnabled: true });
  },

  togglePanOff() {
    this.setState({ panEnabled: false });
  },

  toggleKeyPanZoom() {
    this.setState({ keyPanZoomEnabled: !this.state.keyPanZoomEnabled });
  },

  panByDrag(e, d) {
    if (!this.state.panEnabled) return;

    const maximumX = (this.props.frameDimensions.width - this.state.viewBoxDimensions.width) + (this.props.frameDimensions.width * 0.6);
    const minumumX = -(this.props.frameDimensions.width * 0.6);
    const changedX = this.state.viewBoxDimensions.x -= d.x;

    const maximumY = (this.props.frameDimensions.height - this.state.viewBoxDimensions.height) + (this.props.frameDimensions.height * 0.6);
    const minimumY = -(this.props.frameDimensions.height * 0.6);
    const changedY = this.state.viewBoxDimensions.y -= d.y;

    this.setState({
      viewBoxDimensions: {
        x: Math.max(minumumX, Math.min(changedX, maximumX)),
        y: Math.max(minimumY, Math.min(changedY, maximumY)),
        width: this.state.viewBoxDimensions.width,
        height: this.state.viewBoxDimensions.height
      }
    });
  },

  frameKeyPan(e) {
    if (!this.state.panEnabled) return;
    const keypress = e.which;
    switch (keypress) {
      // left
      case 37:
        e.preventDefault();
        this.panHorizontal(-20);
        break;
      // up
      case 38:
        e.preventDefault();
        this.panVertical(-20);
        break;
      // right
      case 39:
        e.preventDefault();
        this.panHorizontal(20);
        break;
      // down
      case 40:
        e.preventDefault();
        this.panVertical(20);
        break;
      // zoom out - Chrome(187), Firefox(61)
      case 187:
      case 61:
        e.preventDefault();
        this.setState({ zooming: true });
        this.zoom(0.9);
        break;
      // zoom in - Chrome(189), Firefox(173)
      case 189:
      case 173:
        e.preventDefault();
        this.setState({ zooming: true });
        this.zoom(1.1);
        break;
      // no default
    }
  },

  wheelZoom(e) {
    if (!this.state.panEnabled) return;
    e.preventDefault();
    this.setState({ zooming: true });
    (e.deltaY > 0) ? this.zoom(1.1) : this.zoom(0.9);
  },

  panHorizontal(direction) {
    const maximumX = (this.props.frameDimensions.width - this.state.viewBoxDimensions.width) + (this.props.frameDimensions.width * 0.6);
    const minumumX = -(this.props.frameDimensions.width * 0.6);
    const changedX = this.state.viewBoxDimensions.x + direction;
    this.setState({
      viewBoxDimensions: {
        x: Math.max(minumumX, Math.min(changedX, maximumX)),
        y: this.state.viewBoxDimensions.y,
        width: this.state.viewBoxDimensions.width,
        height: this.state.viewBoxDimensions.height
      }
    });
  },

  panVertical(direction) {
    const maximumY = (this.props.frameDimensions.height - this.state.viewBoxDimensions.height) + (this.props.frameDimensions.height * 0.6);
    const minimumY = -(this.props.frameDimensions.height * 0.6);
    const changedY = this.state.viewBoxDimensions.y + direction;
    this.setState({
      viewBoxDimensions: {
        x: this.state.viewBoxDimensions.x,
        y: Math.max(minimumY, Math.min(changedY, maximumY)),
        width: this.state.viewBoxDimensions.width,
        height: this.state.viewBoxDimensions.height
      }
    });
  },

  rotateClockwise() {
    const newRotation = this.state.rotation + 90;
    this.setState({
      rotation: newRotation,
      transform: `rotate(${newRotation} ${this.props.frameDimensions.width / 2} ${this.props.frameDimensions.height / 2})`
    });
  }
});

export default PanZoom;
<commit_msg>Remove string refs from PanZoom<commit_after>/* eslint no-unused-expressions: ["error", { "allowTernary": true }] */
import React from 'react';

const PanZoom = React.createClass({

  propTypes: {
    children: React.PropTypes.node,
    enabled: React.PropTypes.bool,
    frameDimensions: React.PropTypes.shape({
      height: React.PropTypes.number,
      width: React.PropTypes.number
    })
  },

  getDefaultProps() {
    return {
      enabled: false,
      frameDimensions: {
        height: 0,
        width: 0
      }
    };
  },

  getInitialState() {
    return {
      panEnabled: false,
      viewBoxDimensions: {
        x: 0,
        y: 0,
        width: 0,
        height: 0
      },
      zooming: false,
      zoomingTimeoutId: null,
      rotation: 0,
      transform: ''
    };
  },

  componentDidMount() {
    // these events enable a user to navigate an image using arrows, +, and - keys,
    // while the user is in pan and zoom mode.
    if (this.props.enabled) {
      addEventListener('keydown', this.frameKeyPan);
      addEventListener('wheel', this.wheelZoom);
    }
  },

  componentWillUpdate(newProps) {
    if (newProps.frameDimensions === this.props.frameDimensions) return;
    this.setState({
      viewBoxDimensions: {
        width: newProps.frameDimensions.width,
        height: newProps.frameDimensions.height,
        x: 0,
        y: 0
      }
    });
  },

  componentWillUnmount() {
    removeEventListener('keydown', this.frameKeyPan);
    removeEventListener('wheel', this.wheelZoom);
  },

  render() {
    const children = React.Children.map(this.props.children, (child) => {
      return React.cloneElement(child, {
        viewBoxDimensions: this.state.viewBoxDimensions,
        panByDrag: this.panByDrag,
        panEnabled: this.state.panEnabled,
        transform: this.state.transform,
        rotation: this.state.rotation
      });
    });
    return (
      <div>
        {children}
        {this.props.enabled ?
          <div className="pan-zoom-controls" >
            <div className="draw-pan-toggle" >
              <div className={this.state.panEnabled ? '' : 'active'} >
                <button title="annotate" className="fa fa-mouse-pointer" onClick={this.togglePanOff} />
              </div>
              <div className={this.state.panEnabled ? 'active' : ''}>
                <button
                  title="pan"
                  ref={(element) => { this.pan = element; }}
                  className="fa fa-arrows"
                  onClick={this.handleFocus.bind(this, 'pan')}
                  onFocus={this.togglePanOn} onBlur={this.togglePanOff}
                />
              </div>
            </div>
            <div>
              <button
                title="zoom out"
                ref={(element) => { this.zoomOut = element; }}
                className={`zoom-out fa fa-minus ${this.cannotZoomOut() ? 'disabled' : ''}`}
                onMouseDown={this.continuousZoom.bind(this, 1.1)}
                onMouseUp={this.stopZoom}
                onKeyDown={this.keyDownZoomButton.bind(this, 1.1)}
                onKeyUp={this.stopZoom}
                onFocus={this.togglePanOn}
                onBlur={this.togglePanOff}
                onClick={this.handleFocus.bind(this, 'zoomOut')}
              />
            </div>
            <div>
              <button
                title="zoom in"
                ref={(element) => { this.zoomIn = element; }}
                className="zoom-in fa fa-plus"
                onMouseDown={this.continuousZoom.bind(this, 0.9)}
                onMouseUp={this.stopZoom}
                onKeyDown={this.keyDownZoomButton.bind(this, 0.9)}
                onKeyUp={this.stopZoom}
                onFocus={this.togglePanOn}
                onBlur={this.togglePanOff}
                onClick={this.handleFocus.bind(this, 'zoomIn')}
              />
            </div>
            <div>
              <button title="rotate" className={'rotate fa fa-repeat'} onClick={this.rotateClockwise} />
            </div>
            <div>
              <button title="reset zoom levels" className={'reset fa fa-refresh' + (this.cannotResetZoomRotate() ? ' disabled' : '')} onClick={this.zoomReset} />
            </div>
          </div>
          : ''
        }
      </div>
    );
  },

  handleFocus(ref) {
    this[ref].focus();
    this.togglePanOn();
  },

  cannotZoomOut() {
    return this.props.frameDimensions.width === this.state.viewBoxDimensions.width && this.props.frameDimensions.height === this.state.viewBoxDimensions.height;
  },

  cannotResetZoomRotate() {
    return this.cannotZoomOut() && this.state.rotation === 0
  },

  continuousZoom(change) {
    this.clearZoomingTimeout();
    if (change === 0) return;
    this.setState({ zooming: true }, () => {
      let zoomNow = () => {
        // if !this.state.zooming, we don't want to continuously call setTimeout.
        // !this.state.zooming will be the case after a user creates a mouseup event.
        if (!this.state.zooming) return;
        this.zoom(change);
        this.clearZoomingTimeout();
        this.setState({ zoomingTimeoutId: setTimeout(zoomNow, 200) });
      };
      zoomNow();
    });
  },

  clearZoomingTimeout() {
    if (this.state.zoomingTimeoutId) {
      clearTimeout(this.state.zoomingTimeoutId);
    }
  },

  zoom(change) {
    this.clearZoomingTimeout();
    if (!this.state.zooming) return;
    const newNaturalWidth = this.state.viewBoxDimensions.width * change;
    const newNaturalHeight = this.state.viewBoxDimensions.height * change;

    const newNaturalX = this.state.viewBoxDimensions.x - ((newNaturalWidth - this.state.viewBoxDimensions.width) / 2);
    const newNaturalY = this.state.viewBoxDimensions.y - ((newNaturalHeight - this.state.viewBoxDimensions.height) / 2);

    if ((newNaturalWidth > this.props.frameDimensions.width) || (newNaturalHeight * change > this.props.frameDimensions.height)) {
      this.zoomReset();
    } else {
      this.setState({
        viewBoxDimensions: {
          width: newNaturalWidth,
          height: newNaturalHeight,
          x: newNaturalX,
          y: newNaturalY
        }
      });
    }
  },

  keyDownZoomButton(change, e) {
    // only zoom if a user presses enter on the zoom button.
    if (e.which === 13) {
      this.setState({ zooming: true }, () => {
        this.zoom(change);
      });
    }
  },

  stopZoom(e) {
    e.stopPropagation();
    this.setState({ zooming: false });
    this.continuousZoom(0);
  },

  zoomReset() {
    this.setState({
      viewBoxDimensions: {
        width: this.props.frameDimensions.width,
        height: this.props.frameDimensions.height,
        x: 0,
        y: 0
      },
      rotation: 0,
      transform: `rotate(${0} ${this.props.frameDimensions.width / 2} ${this.props.frameDimensions.height / 2})`
    });
  },

  togglePanOn() {
    if (!this.state.panEnabled) this.setState({ panEnabled: true });
  },

  togglePanOff() {
    this.setState({ panEnabled: false });
  },

  toggleKeyPanZoom() {
    this.setState({ keyPanZoomEnabled: !this.state.keyPanZoomEnabled });
  },

  panByDrag(e, d) {
    if (!this.state.panEnabled) return;

    const maximumX = (this.props.frameDimensions.width - this.state.viewBoxDimensions.width) + (this.props.frameDimensions.width * 0.6);
    const minumumX = -(this.props.frameDimensions.width * 0.6);
    const changedX = this.state.viewBoxDimensions.x -= d.x;

    const maximumY = (this.props.frameDimensions.height - this.state.viewBoxDimensions.height) + (this.props.frameDimensions.height * 0.6);
    const minimumY = -(this.props.frameDimensions.height * 0.6);
    const changedY = this.state.viewBoxDimensions.y -= d.y;

    this.setState({
      viewBoxDimensions: {
        x: Math.max(minumumX, Math.min(changedX, maximumX)),
        y: Math.max(minimumY, Math.min(changedY, maximumY)),
        width: this.state.viewBoxDimensions.width,
        height: this.state.viewBoxDimensions.height
      }
    });
  },

  frameKeyPan(e) {
    if (!this.state.panEnabled) return;
    const keypress = e.which;
    switch (keypress) {
      // left
      case 37:
        e.preventDefault();
        this.panHorizontal(-20);
        break;
      // up
      case 38:
        e.preventDefault();
        this.panVertical(-20);
        break;
      // right
      case 39:
        e.preventDefault();
        this.panHorizontal(20);
        break;
      // down
      case 40:
        e.preventDefault();
        this.panVertical(20);
        break;
      // zoom out - Chrome(187), Firefox(61)
      case 187:
      case 61:
        e.preventDefault();
        this.setState({ zooming: true });
        this.zoom(0.9);
        break;
      // zoom in - Chrome(189), Firefox(173)
      case 189:
      case 173:
        e.preventDefault();
        this.setState({ zooming: true });
        this.zoom(1.1);
        break;
      // no default
    }
  },

  wheelZoom(e) {
    if (!this.state.panEnabled) return;
    e.preventDefault();
    this.setState({ zooming: true });
    (e.deltaY > 0) ? this.zoom(1.1) : this.zoom(0.9);
  },

  panHorizontal(direction) {
    const maximumX = (this.props.frameDimensions.width - this.state.viewBoxDimensions.width) + (this.props.frameDimensions.width * 0.6);
    const minumumX = -(this.props.frameDimensions.width * 0.6);
    const changedX = this.state.viewBoxDimensions.x + direction;
    this.setState({
      viewBoxDimensions: {
        x: Math.max(minumumX, Math.min(changedX, maximumX)),
        y: this.state.viewBoxDimensions.y,
        width: this.state.viewBoxDimensions.width,
        height: this.state.viewBoxDimensions.height
      }
    });
  },

  panVertical(direction) {
    const maximumY = (this.props.frameDimensions.height - this.state.viewBoxDimensions.height) + (this.props.frameDimensions.height * 0.6);
    const minimumY = -(this.props.frameDimensions.height * 0.6);
    const changedY = this.state.viewBoxDimensions.y + direction;
    this.setState({
      viewBoxDimensions: {
        x: this.state.viewBoxDimensions.x,
        y: Math.max(minimumY, Math.min(changedY, maximumY)),
        width: this.state.viewBoxDimensions.width,
        height: this.state.viewBoxDimensions.height
      }
    });
  },

  rotateClockwise() {
    const newRotation = this.state.rotation + 90;
    this.setState({
      rotation: newRotation,
      transform: `rotate(${newRotation} ${this.props.frameDimensions.width / 2} ${this.props.frameDimensions.height / 2})`
    });
  }
});

export default PanZoom;

{'fraction_non_alphanumeric': 0.13702176622878512, 'fraction_numerical': 0.009588647041902388, 'mean_word_length': 2.2174919018972696, 'pattern_counts': {'":': 2, '<': 51, '<?xml version=': 0, '>': 68, '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': '4197868', 'n_tokens_mistral': 6612, 'n_tokens_neox': 6339, 'n_words': 1144}