<commit_before>import BScroll from '../../index'
import propertiesConfig from './propertiesConfig'
import { getDistance, isUndef } from '../../util/lang'
import { offsetToBody, getRect, DOMRect } from '../../util/dom'
import { zoomConfig } from '../../Options'
import { style } from '../../util'

import Behavior from '../../scroller/Behavior'

interface Point {
  x: number
  y: number
}

export default class Zoom {
  static pluginName = 'zoom'
  origin: Point
  scale = 1
  private zoomOpt: Partial<zoomConfig>
  private startDistance: number
  private startScale: number
  private wrapper: HTMLElement
  private scaleElement: HTMLElement
  private wrapperSize: DOMRect
  private scaleElementInitSize: DOMRect
  private zooming: boolean
  constructor(public scroll: BScroll) {
    this.scroll.proxy(propertiesConfig)
    this.zoomOpt = this.scroll.options.zoom as Partial<zoomConfig>
    this.init()
  }
  init() {
    const scrollerIns = this.scroll.scroller
    this.wrapper = this.scroll.scroller.wrapper
    this.scaleElement = this.scroll.scroller.element
    this.scaleElement.style[style.transformOrigin as any] = '0 0'
    this.wrapperSize = getRect(this.wrapper)
    this.scaleElementInitSize = getRect(this.scaleElement)
    scrollerIns.hooks.on('beforeStart', (e: TouchEvent) => {
      if (e.touches && e.touches.length > 1) {
        this.zoomStart(e)
      }
    })
    scrollerIns.hooks.on('beforeMove', (e: TouchEvent) => {
      if (!e.touches || e.touches.length < 2) {
        return false
      }
      this.zoom(e)
      return true
    })
    scrollerIns.hooks.on('beforeEnd', (e: TouchEvent) => {
      if (!this.zooming) {
        return false
      }
      this.zoomEnd()
      return true
    })
  }
  zoomTo(scale: number, x: number, y: number) {
    let { left, top } = offsetToBody(this.wrapper)
    let originX = x + left - this.scroll.x
    let originY = y + top - this.scroll.y
    this.zooming = true
    this._zoomTo(scale, { x: originX, y: originY }, this.scale)
    this.zooming = false
  }
  private zoomStart(e: TouchEvent) {
    this.zooming = true
    const firstFinger = e.touches[0]
    const secondFinger = e.touches[1]
    this.startDistance = this.getFingerDistance(e)
    this.startScale = this.scale

    let { left, top } = offsetToBody(this.wrapper)

    this.origin = {
      x:
        Math.abs(firstFinger.pageX + secondFinger.pageX) / 2 +
        left -
        this.scroll.x,
      y:
        Math.abs(firstFinger.pageY + secondFinger.pageY) / 2 +
        top -
        this.scroll.y
    }
  }
  private zoom(e: TouchEvent) {
    const scrollerIns = this.scroll.scroller
    const currentDistance = this.getFingerDistance(e)
    let currentScale = (currentDistance / this.startDistance) * this.startScale
    this.scale = this.scaleCure(currentScale)
    const lastScale = this.scale / this.startScale
    const scrollBehaviorX = scrollerIns.scrollBehaviorX
    const scrollBehaviorY = scrollerIns.scrollBehaviorY
    const x = this.getNewPos(this.origin.x, lastScale, scrollBehaviorX)
    const y = this.getNewPos(this.origin.y, lastScale, scrollBehaviorY)
    this.resetBoundaries(this.scale, scrollBehaviorX, 'x', x)
    this.resetBoundaries(this.scale, scrollBehaviorY, 'y', y)
    scrollerIns.scrollTo(x, y, 0, undefined, {
      start: {
        scale: this.scale / this.startScale
      },
      end: {
        scale: this.scale
      }
    })
  }
  private getFingerDistance(e: TouchEvent): number {
    const firstFinger = e.touches[0]
    const secondFinger = e.touches[1]
    const deltaX = Math.abs(firstFinger.pageX - secondFinger.pageX)
    const deltaY = Math.abs(firstFinger.pageY - secondFinger.pageY)

    return getDistance(deltaX, deltaY)
  }
  private zoomEnd() {
    this._zoomTo(this.scale, this.origin, this.startScale || this.scale)
    this.zooming = false
  }
  private _zoomTo(scale: number, origin: Point, startScale: number) {
    const oldScale = this.scale
    this.scale = this.fixInScaleLimit(scale)
    const lastScale = this.scale / startScale
    const scrollerIns = this.scroll.scroller
    const scrollBehaviorX = scrollerIns.scrollBehaviorX
    const scrollBehaviorY = scrollerIns.scrollBehaviorY

    this.resetBoundaries(this.scale, scrollBehaviorX, 'x')
    this.resetBoundaries(this.scale, scrollBehaviorY, 'y')
    // resetPosition
    const newX = this.getNewPos(origin.x, lastScale, scrollBehaviorX, true)
    const newY = this.getNewPos(origin.y, lastScale, scrollBehaviorY, true)
    if (
      scrollBehaviorX.currentPos !== Math.round(newX) ||
      scrollBehaviorY.currentPos !== Math.round(newY) ||
      this.scale !== oldScale
    ) {
      scrollerIns.scrollTo(
        newX,
        newY,
        this.scroll.options.bounceTime,
        undefined,
        {
          start: {
            scale: oldScale
          },
          end: {
            scale: this.scale
          }
        },
        true
      )
    }
  }
  private resetBoundaries(
    scale: number,
    scrollBehavior: Behavior,
    direction: 'x' | 'y',
    extendValue?: number
  ) {
    let min
    let max
    let hasScroll
    if (scale < 1) {
      min = 0
      max = 0
      hasScroll = false
    } else {
      min = 0
      if (direction === 'x') {
        max = -(
          this.scaleElementInitSize.width * scale -
          this.wrapperSize.width
        )
      } else {
        max = -(
          this.scaleElementInitSize.height * scale -
          this.wrapperSize.height
        )
      }
      hasScroll = true
    }
    if (!isUndef(extendValue)) {
      max = Math.min(<number>extendValue, max)
      min = Math.max(<number>extendValue, min) // max & min & curValue is negative value
      hasScroll = !!(min || max)<commit_msg>zoom plugin: fix then bug of resetBoundaries function<commit_after>import BScroll from '../../index'
import propertiesConfig from './propertiesConfig'
import { getDistance, isUndef } from '../../util/lang'
import { offsetToBody, getRect, DOMRect } from '../../util/dom'
import { zoomConfig } from '../../Options'
import { style } from '../../util'

import Behavior from '../../scroller/Behavior'

interface Point {
  x: number
  y: number
}

interface ScrollBoundary {
  x: [number, number]
  y: [number, number]
}

export default class Zoom {
  static pluginName = 'zoom'
  origin: Point
  scale = 1
  private zoomOpt: Partial<zoomConfig>
  private startDistance: number
  private startScale: number
  private wrapper: HTMLElement
  private scaleElement: HTMLElement
  private scaleElementInitSize: DOMRect
  private initScrollBoundary: ScrollBoundary
  private zooming: boolean
  constructor(public scroll: BScroll) {
    this.scroll.proxy(propertiesConfig)
    this.zoomOpt = this.scroll.options.zoom as Partial<zoomConfig>
    this.init()
  }
  init() {
    const scrollerIns = this.scroll.scroller
    this.wrapper = this.scroll.scroller.wrapper
    this.scaleElement = this.scroll.scroller.element
    this.scaleElement.style[style.transformOrigin as any] = '0 0'
    this.scaleElementInitSize = getRect(this.scaleElement)
    const scrollBehaviorX = scrollerIns.scrollBehaviorX
    const scrollBehaviorY = scrollerIns.scrollBehaviorY
    this.initScrollBoundary = {
      x: [scrollBehaviorX.minScrollPos, scrollBehaviorX.maxScrollPos],
      y: [scrollBehaviorY.minScrollPos, scrollBehaviorY.maxScrollPos]
    }
    scrollerIns.hooks.on('beforeStart', (e: TouchEvent) => {
      if (e.touches && e.touches.length > 1) {
        this.zoomStart(e)
      }
    })
    scrollerIns.hooks.on('beforeMove', (e: TouchEvent) => {
      if (!e.touches || e.touches.length < 2) {
        return false
      }
      this.zoom(e)
      return true
    })
    scrollerIns.hooks.on('beforeEnd', (e: TouchEvent) => {
      if (!this.zooming) {
        return false
      }
      this.zoomEnd()
      return true
    })
  }
  zoomTo(scale: number, x: number, y: number) {
    let { left, top } = offsetToBody(this.wrapper)
    let originX = x + left - this.scroll.x
    let originY = y + top - this.scroll.y
    this.zooming = true
    this._zoomTo(scale, { x: originX, y: originY }, this.scale)
    this.zooming = false
  }
  private zoomStart(e: TouchEvent) {
    this.zooming = true
    const firstFinger = e.touches[0]
    const secondFinger = e.touches[1]
    this.startDistance = this.getFingerDistance(e)
    this.startScale = this.scale

    let { left, top } = offsetToBody(this.wrapper)

    this.origin = {
      x:
        Math.abs(firstFinger.pageX + secondFinger.pageX) / 2 +
        left -
        this.scroll.x,
      y:
        Math.abs(firstFinger.pageY + secondFinger.pageY) / 2 +
        top -
        this.scroll.y
    }
  }
  private zoom(e: TouchEvent) {
    const scrollerIns = this.scroll.scroller
    const currentDistance = this.getFingerDistance(e)
    let currentScale = (currentDistance / this.startDistance) * this.startScale
    this.scale = this.scaleCure(currentScale)
    const lastScale = this.scale / this.startScale
    const scrollBehaviorX = scrollerIns.scrollBehaviorX
    const scrollBehaviorY = scrollerIns.scrollBehaviorY
    const x = this.getNewPos(this.origin.x, lastScale, scrollBehaviorX)
    const y = this.getNewPos(this.origin.y, lastScale, scrollBehaviorY)
    this.resetBoundaries(this.scale, scrollBehaviorX, 'x', x)
    this.resetBoundaries(this.scale, scrollBehaviorY, 'y', y)
    scrollerIns.scrollTo(x, y, 0, undefined, {
      start: {
        scale: this.scale / this.startScale
      },
      end: {
        scale: this.scale
      }
    })
  }
  private getFingerDistance(e: TouchEvent): number {
    const firstFinger = e.touches[0]
    const secondFinger = e.touches[1]
    const deltaX = Math.abs(firstFinger.pageX - secondFinger.pageX)
    const deltaY = Math.abs(firstFinger.pageY - secondFinger.pageY)

    return getDistance(deltaX, deltaY)
  }
  private zoomEnd() {
    this._zoomTo(this.scale, this.origin, this.startScale || this.scale)
    this.zooming = false
  }
  private _zoomTo(scale: number, origin: Point, startScale: number) {
    const oldScale = this.scale
    this.scale = this.fixInScaleLimit(scale)
    const lastScale = this.scale / startScale
    const scrollerIns = this.scroll.scroller
    const scrollBehaviorX = scrollerIns.scrollBehaviorX
    const scrollBehaviorY = scrollerIns.scrollBehaviorY

    this.resetBoundaries(this.scale, scrollBehaviorX, 'x')
    this.resetBoundaries(this.scale, scrollBehaviorY, 'y')
    // resetPosition
    const newX = this.getNewPos(origin.x, lastScale, scrollBehaviorX, true)
    const newY = this.getNewPos(origin.y, lastScale, scrollBehaviorY, true)
    if (
      scrollBehaviorX.currentPos !== Math.round(newX) ||
      scrollBehaviorY.currentPos !== Math.round(newY) ||
      this.scale !== oldScale
    ) {
      scrollerIns.scrollTo(
        newX,
        newY,
        this.scroll.options.bounceTime,
        undefined,
        {
          start: {
            scale: oldScale
          },
          end: {
            scale: this.scale
          }
        },
        true
      )
    }
  }
  private resetBoundaries(
    scale: number,
    scrollBehavior: Behavior,
    direction: 'x' | 'y',
    extendValue?: number
  ) {
    let min = this.initScrollBoundary[direction][0]
    let max = this.initScrollBoundary[direction][1]
    let hasScroll = false
    if (scale > 1) {
      let sideName = direction === 'x' ? 'width' : 'height'
      max =
        -this.scaleElementInitSize[sideName] * (scale - 1) -
        this.initScrollBoundary[direction][1]
      hasScroll = true
    }
    if (!isUndef(extendValue)) {
      max = Math.min(<number>extendValue, max)
      min = Math.max(<number>extendValue, min) // max & min & curValue is negative value
      hasScroll = !!(min || max)
{'fraction_non_alphanumeric': 0.1142905808209845, 'fraction_numerical': 0.002810424118548799, 'mean_word_length': 2.6916064130776487, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 20, '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': '3594750', 'n_tokens_mistral': 3714, 'n_tokens_neox': 3694, 'n_words': 812}