<commit_before>const React = require('react')
const getDocument = require('./lpdfjs.js')
const AppState = require('./appstate.js')
const FetchErrorPromise = require('./fetcherrorpromise.jsx')
const PaperUtils = require('./paperutils.js')
const { client2view, pointDistance } = require('./pointutils.js')

class PaperViewer extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      paperFileId: null,
      dirs: null,
      loadError: null,
      pdfjsObjs: null,
      initialLoadTime: null,
      dirMenu: null
    }

    this.paperDirHitRegions = null // [{y1, y2, dir}, ...]
    this.pdfjsViewerInstance = null

    this.handleAppStateUpdate = this.handleAppStateUpdate.bind(this)
    this.handlePDFUserMove = this.handlePDFUserMove.bind(this)
    this.handlePDFJSViewerPostDraw = this.handlePDFJSViewerPostDraw.bind(this)
    this.handlePDFJSViewerDownEvent = this.handlePDFJSViewerDownEvent.bind(this)
  }

  loadPaper (fileId) {
    if (this.state.paperFileId === fileId && !this.state.loadError) return
    this.setState({loadError: null, initialLoadTime: Date.now(), dirMenu: null})
    this.paperDirHitRegions = null
    if (this.state.paperFileId) {
      if (this.state.pdfjsObjs) {
        for (let t of Object.keys(this.state.pdfjsObjs)) {
          if (this.state.pdfjsObjs[t].document) {
            this.state.pdfjsObjs[t].document.destroy()
          }
          delete this.state.pdfjsObjs[t]
        }
      }
      this.setState({
        dirs: null,
        pdfjsObjs: null
      })
    }

    this.setState({
      paperFileId: fileId
    })

    if (fileId) {
      fetch(`/dirs/batch/?docid=${encodeURIComponent(fileId)}&flattenEr=true`).then(FetchErrorPromise.then, FetchErrorPromise.error).then(res => res.json()).then(json => {
        if (this.state.paperFileId !== fileId) return

        try {
          let sortedTypeStrArr = Object.keys(json).sort(PaperUtils.funcSortType)
          this.setState({pdfjsObjs: {}})
          for (let type of sortedTypeStrArr) {
            let docid = json[type].docid
            if (Array.isArray(json[type].dirs)) {
              json[type].dirs = json[type].dirs.map((d, i) => Object.assign(d, {i}))
            }
            if (json[type].type !== 'blob') {
              this.loadPDF(type, docid)
            }
          }
          this.setState({dirs: json})
          if (AppState.getState().v2viewing.tCurrentType === null && (!sortedTypeStrArr.includes('qp') || json.qp.type !== 'questions' || json.qp.dirs.length === 0)) {
            AppState.dispatch({type: 'v2view-set-tCurrentType', tCurrentType: sortedTypeStrArr[0]})
          }
        } catch (e) {
          this.setState({loadError: e})
        }
      }, err => {
        if (this.state.paperFileId !== fileId) return

        this.setState({loadError: err})
      })
    }
  }

  loadPDF (type, docid) {
    if (!this.state.pdfjsObjs) throw new Error('this.state.pdfjsObjs is not an object.')
    return new Promise((resolve, reject) => {
      let obj = this.state.pdfjsObjs[type]
      if (!obj) {
        obj = {
          document: null,
          progress: 0,
          error: null
        }
        this.state.pdfjsObjs[type] = obj
        let xhr = new XMLHttpRequest()
        xhr.responseType = 'arraybuffer'
        xhr.addEventListener('progress', evt => {
          if (evt.lengthComputable) {
            obj.progress = evt.loaded / evt.total
            this.forceUpdate()
          }
        })
        xhr.addEventListener('error', evt => {
          obj.error = evt.error
          reject(obj.error)
          this.forceUpdate()
        })
        xhr.addEventListener('timeout', evt => {
          obj.error = new Error('Request has timed out.')
          reject(obj.error)
          this.forceUpdate()
        })
        xhr.addEventListener('load', evt => {
          obj.progress = 1
          this.forceUpdate()
          getDocument(xhr.response).then(pdf => {
            obj.document = pdf
            resolve()
            this.forceUpdate()
          }, err => {
            obj.error = err
            reject(err)
            this.forceUpdate()
          })<commit_msg>Cancel PDF loading xhr when switching paper<commit_after>const React = require('react')
const getDocument = require('./lpdfjs.js')
const AppState = require('./appstate.js')
const FetchErrorPromise = require('./fetcherrorpromise.jsx')
const PaperUtils = require('./paperutils.js')
const { client2view, pointDistance } = require('./pointutils.js')

class PaperViewer extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      paperFileId: null,
      dirs: null,
      loadError: null,
      pdfjsObjs: null,
      initialLoadTime: null,
      dirMenu: null
    }

    this.paperDirHitRegions = null // [{y1, y2, dir}, ...]
    this.pdfjsViewerInstance = null

    this.handleAppStateUpdate = this.handleAppStateUpdate.bind(this)
    this.handlePDFUserMove = this.handlePDFUserMove.bind(this)
    this.handlePDFJSViewerPostDraw = this.handlePDFJSViewerPostDraw.bind(this)
    this.handlePDFJSViewerDownEvent = this.handlePDFJSViewerDownEvent.bind(this)
  }

  loadPaper (fileId) {
    if (this.state.paperFileId === fileId && !this.state.loadError) return
    this.setState({loadError: null, initialLoadTime: Date.now(), dirMenu: null})
    this.paperDirHitRegions = null
    if (this.state.paperFileId) {
      if (this.state.pdfjsObjs) {
        for (let t of Object.keys(this.state.pdfjsObjs)) {
          let pdfjsobj = this.state.pdfjsObjs[t]
          if (pdfjsobj.document) {
            pdfjsobj.document.destroy()
          }
          if (pdfjsobj.xhr) {
            pdfjsobj.xhr.abort()
          }
          pdfjsobj = null
          delete this.state.pdfjsObjs[t]
        }
      }
      this.setState({
        dirs: null,
        pdfjsObjs: null
      })
    }

    this.setState({
      paperFileId: fileId
    })

    if (fileId) {
      fetch(`/dirs/batch/?docid=${encodeURIComponent(fileId)}&flattenEr=true`).then(FetchErrorPromise.then, FetchErrorPromise.error).then(res => res.json()).then(json => {
        if (this.state.paperFileId !== fileId) return

        try {
          let sortedTypeStrArr = Object.keys(json).sort(PaperUtils.funcSortType)
          this.setState({pdfjsObjs: {}})
          for (let type of sortedTypeStrArr) {
            let docid = json[type].docid
            if (Array.isArray(json[type].dirs)) {
              json[type].dirs = json[type].dirs.map((d, i) => Object.assign(d, {i}))
            }
            if (json[type].type !== 'blob') {
              this.loadPDF(type, docid)
            }
          }
          this.setState({dirs: json})
          if (AppState.getState().v2viewing.tCurrentType === null && (!sortedTypeStrArr.includes('qp') || json.qp.type !== 'questions' || json.qp.dirs.length === 0)) {
            AppState.dispatch({type: 'v2view-set-tCurrentType', tCurrentType: sortedTypeStrArr[0]})
          }
        } catch (e) {
          this.setState({loadError: e})
        }
      }, err => {
        if (this.state.paperFileId !== fileId) return

        this.setState({loadError: err})
      })
    }
  }

  loadPDF (type, docid) {
    if (!this.state.pdfjsObjs) throw new Error('this.state.pdfjsObjs is not an object.')
    return new Promise((resolve, reject) => {
      let obj = this.state.pdfjsObjs[type]
      if (!obj) {
        let xhr = new XMLHttpRequest()
        obj = {
          document: null,
          progress: 0,
          error: null,
          xhr
        }
        this.state.pdfjsObjs[type] = obj
        xhr.responseType = 'arraybuffer'
        xhr.addEventListener('progress', evt => {
          if (evt.lengthComputable) {
            obj.progress = evt.loaded / evt.total
            this.forceUpdate()
          }
        })
        xhr.addEventListener('error', evt => {
          obj.error = evt.error
          reject(obj.error)
          this.forceUpdate()
        })
        xhr.addEventListener('timeout', evt => {
          obj.error = new Error('Request has timed out.')
          reject(obj.error)
          this.forceUpdate()
        })
        xhr.addEventListener('load', evt => {
          obj.progress = 1
          this.forceUpdate()
          getDocument(xhr.response).then(pdf => {
            obj.document = pdf
            resolve()
            this.forceUpdate()
          }, err => {
            obj.error = err
            reject(err)
            this.forceUpdate()
          })
{'fraction_non_alphanumeric': 0.13444588083206016, 'fraction_numerical': 0.0021154072158890585, 'mean_word_length': 2.125229526257804, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 25, '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': '5133612', 'n_tokens_mistral': 2616, 'n_tokens_neox': 2628, 'n_words': 465}