<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import ResizeObserver from 'resize-observer-polyfill';

import ProgressBarDisplay from './ProgressBarDisplay';
import PurePropTypesComponent from './PurePropTypesComponent';
import convertToNumberWithinIntervalBounds from '../../utils/convertToNumberWithinIntervalBounds';

class ProgressBar extends PurePropTypesComponent {
  constructor (props) {
    super(props);

    this.progressContainer = null;
    this.progressBoundingRect = null;
    this.progressContainerResizeObserver = null;

    // bind methods fired on React events
    this.setProgressContainerRef = this.setProgressContainerRef.bind(this);
    this.handleAdjustProgress = this.handleAdjustProgress.bind(this);

    // bind listeners to add on mount and remove on unmount
    this.handleAdjustComplete = this.handleAdjustComplete.bind(this);
    this.fetchProgressBoundingRect = this.fetchProgressBoundingRect.bind(this);
  }

  componentDidMount () {
    // add event listeners bound outside the scope of our component
    window.addEventListener('mousemove', this.handleAdjustProgress);
    document.addEventListener('touchmove', this.handleAdjustProgress);
    window.addEventListener('mouseup', this.handleAdjustComplete);
    document.addEventListener('touchend', this.handleAdjustComplete);
    this.progressContainerResizeObserver = new ResizeObserver(
      this.fetchProgressBoundingRect
    );
    this.progressContainerResizeObserver.observe(this.progressContainer);
  }

  componentWillUnmount () {
    // remove event listeners bound outside the scope of our component
    window.removeEventListener('mousemove', this.handleAdjustProgress);
    document.removeEventListener('touchmove', this.handleAdjustProgress);
    window.removeEventListener('mouseup', this.handleAdjustComplete);
    document.removeEventListener('touchend', this.handleAdjustComplete);
    this.progressContainerResizeObserver.disconnect();
  }

  setProgressContainerRef (ref) {
    this.progressContainer = ref;
  };

  handleAdjustProgress (event) {
    const { readonly, adjusting, onAdjustProgress } = this.props;
    if (readonly) {
      return;
    }
    // make sure we don't select stuff in the background
    if (event.type === 'mousedown' || event.type === 'touchstart') {
      document.body.classList.add('rr_audio_player__noselect');
    } else if (!adjusting) {
      return;
    }
    /* we don't want mouse handlers to receive the event
     * after touch handlers, so prevent default
     */
    event.preventDefault();
    const isTouch = event.type.slice(0, 5) === 'touch';
    const pageX = isTouch ? event.targetTouches.item(0).pageX : event.pageX;
    const boundingRect = this.progressBoundingRect;
    const position = pageX - boundingRect.left - document.body.scrollLeft;
    const progress = position / boundingRect.width;
    const progressInBounds = convertToNumberWithinIntervalBounds(progress, 0, 1);
    onAdjustProgress(progressInBounds);
  }

  handleAdjustComplete (event) {
    const { adjusting, onAdjustComplete } = this.props;
    /* this function is activated when the user lets go of
     * the mouse, so if .rr_audio_player__noselect was applied
     * to the document body, get rid of it.
     */
    document.body.classList.remove('rr_audio_player__noselect');
    if (!adjusting) {
      return;
    }
    /* we don't want mouse handlers to receive the event
     * after touch handlers, so prevent default
     */
    event.preventDefault();
    if (typeof onAdjustComplete === 'function') {
      onAdjustComplete();
    }
  }

  fetchProgressBoundingRect () {
    this.progressBoundingRect = this.progressContainer.getBoundingClientRect();
  }

  render () {
    const { className, style, progress } = this.props;
    return (
      <ProgressBarDisplay
        progressBarRef={this.setProgressContainerRef}
        className={className}
        style={style}<commit_msg>ProgressBar always knows where its bounding rect is when needed.<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import ResizeObserver from 'resize-observer-polyfill';

import ProgressBarDisplay from './ProgressBarDisplay';
import PurePropTypesComponent from './PurePropTypesComponent';
import convertToNumberWithinIntervalBounds from '../../utils/convertToNumberWithinIntervalBounds';

class ProgressBar extends PurePropTypesComponent {
  constructor (props) {
    super(props);

    this.progressContainer = null;

    // bind methods fired on React events
    this.setProgressContainerRef = this.setProgressContainerRef.bind(this);
    this.handleAdjustProgress = this.handleAdjustProgress.bind(this);

    // bind listeners to add on mount and remove on unmount
    this.handleAdjustComplete = this.handleAdjustComplete.bind(this);
  }

  componentDidMount () {
    // add event listeners bound outside the scope of our component
    window.addEventListener('mousemove', this.handleAdjustProgress);
    document.addEventListener('touchmove', this.handleAdjustProgress);
    window.addEventListener('mouseup', this.handleAdjustComplete);
    document.addEventListener('touchend', this.handleAdjustComplete);
  }

  componentWillUnmount () {
    // remove event listeners bound outside the scope of our component
    window.removeEventListener('mousemove', this.handleAdjustProgress);
    document.removeEventListener('touchmove', this.handleAdjustProgress);
    window.removeEventListener('mouseup', this.handleAdjustComplete);
    document.removeEventListener('touchend', this.handleAdjustComplete);
  }

  setProgressContainerRef (ref) {
    this.progressContainer = ref;
  };

  handleAdjustProgress (event) {
    const { readonly, adjusting, onAdjustProgress } = this.props;
    if (readonly) {
      return;
    }
    // make sure we don't select stuff in the background
    if (event.type === 'mousedown' || event.type === 'touchstart') {
      document.body.classList.add('rr_audio_player__noselect');
    } else if (!adjusting) {
      return;
    }
    /* we don't want mouse handlers to receive the event
     * after touch handlers, so prevent default
     */
    event.preventDefault();
    const isTouch = event.type.slice(0, 5) === 'touch';
    const pageX = isTouch ? event.targetTouches.item(0).pageX : event.pageX;
    const boundingRect = this.progressContainer.getBoundingClientRect();
    const position = pageX - boundingRect.left - document.body.scrollLeft;
    const progress = position / boundingRect.width;
    const progressInBounds = convertToNumberWithinIntervalBounds(progress, 0, 1);
    onAdjustProgress(progressInBounds);
  }

  handleAdjustComplete (event) {
    const { adjusting, onAdjustComplete } = this.props;
    /* this function is activated when the user lets go of
     * the mouse, so if .rr_audio_player__noselect was applied
     * to the document body, get rid of it.
     */
    document.body.classList.remove('rr_audio_player__noselect');
    if (!adjusting) {
      return;
    }
    /* we don't want mouse handlers to receive the event
     * after touch handlers, so prevent default
     */
    event.preventDefault();
    if (typeof onAdjustComplete === 'function') {
      onAdjustComplete();
    }
  }

  render () {
    const { className, style, progress } = this.props;
    return (
      <ProgressBarDisplay
        progressBarRef={this.setProgressContainerRef}
        className={className}
        style={style}
{'fraction_non_alphanumeric': 0.09757079586632667, 'fraction_numerical': 0.0013421017313112334, 'mean_word_length': 3.9350993377483445, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, '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': '2118267', 'n_tokens_mistral': 1982, 'n_tokens_neox': 1924, 'n_words': 497}