{"version":3,"file":"panZoom.mjs","names":[],"sources":["../../../../package/common/panZoom/panZoom.tsx"],"sourcesContent":["import {\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type PointerEvent as ReactPointerEvent,\n} from 'react';\nimport cx from 'classnames';\nimport { faMagnifyingGlassMinus, faMagnifyingGlassPlus, faRotateLeft } from '@fortawesome/free-solid-svg-icons';\n\nimport ActionIcon from '../actionIcon';\nimport { ButtonStyleType } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { isInteractiveTarget } from '../utils/interactiveTarget';\nimport { canPan, EPSILON, IDENTITY, settleTransform, zoomAt } from './panZoom.geometry';\nimport type { Box, PanZoomTransform, Point, Size } from './panZoom.geometry';\nimport type { PanZoomLabels, PanZoomProps } from './panZoom.types';\nimport styles from './panZoom.module.pcss';\n\n// Opaque, as the buttons sit over the content. Carousel's arrows do the same.\nconst CONTROL_COLOR = 'var(--background-accent)';\n\nconst ZOOM_STEP = 1.5;\nconst DOUBLE_TAP_SCALE = 2;\n/** Carousel's swipe lock distance. */\nconst TAP_SLOP = 10;\nconst DOUBLE_TAP_MS = 300;\nconst DOUBLE_TAP_DISTANCE = 30;\nconst KEY_PAN_RATIO = 0.1;\n\n/** For content with no pixel size of its own, such as an SVG. */\nconst DEFAULT_MAX_SCALE = 4;\n/** So an image already shown at its own size still zooms. */\nconst MIN_IMAGE_MAX_SCALE = 2;\n\n// Calendar's flick window. A burst of samples within one frame says nothing about speed.\nconst VELOCITY_WINDOW_MS = 100;\nconst MIN_VELOCITY_SPAN_MS = 20;\n/** px/ms. */\nconst GLIDE_MIN_SPEED = 0.3;\nconst GLIDE_STOP_SPEED = 0.02;\n/** Speed kept per 16ms frame. */\nconst GLIDE_FRICTION = 0.95;\n\nconst ARROWS: Record<string, Point> = {\n  ArrowLeft: { x: -1, y: 0 },\n  ArrowRight: { x: 1, y: 0 },\n  ArrowUp: { x: 0, y: -1 },\n  ArrowDown: { x: 0, y: 1 },\n};\n\nconst DEFAULT_LABELS: Required<PanZoomLabels> = {\n  zoomIn: 'Zoom in',\n  zoomOut: 'Zoom out',\n  reset: 'Reset zoom',\n};\n\ninterface Geometry {\n  viewport: Size;\n  content: Box;\n  /** The viewport's top left corner, in client coordinates. */\n  origin: Point;\n}\n\ntype Gesture =\n  | { kind: 'pan'; start: Point; from: PanZoomTransform; geometry: Geometry; captured: boolean }\n  | { kind: 'pinch'; distance: number; midpoint: Point; from: PanZoomTransform; geometry: Geometry };\n\nfunction PanZoom(props: PanZoomProps) {\n  const {\n    children,\n    maxScale,\n    scrollToZoom = false,\n    showControls = true,\n    disabled = false,\n    onScaleChange,\n    onTap,\n    ref,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    labels,\n    onPointerDown,\n    onPointerMove,\n    onPointerUp,\n    onPointerCancel,\n    onKeyDown,\n    onDragStart,\n    ...rest\n  } = useComponentDefaults('PanZoom', props);\n  const text = useLabels('PanZoom', labels, DEFAULT_LABELS);\n\n  const rootRef = useRef<HTMLDivElement>(null);\n  const contentRef = useRef<HTMLDivElement>(null);\n  const controlsRef = useRef<HTMLDivElement>(null);\n\n  const [transform, setTransform] = useState<PanZoomTransform>(IDENTITY);\n  const [animated, setAnimated] = useState(false);\n  // Two move events can arrive before React renders.\n  const transformRef = useRef(transform);\n\n  const pointers = useRef(new Map<number, Point>());\n  const gesture = useRef<Gesture | null>(null);\n  const press = useRef<{ start: Point; moved: boolean } | null>(null);\n  const lastTap = useRef<{ point: Point; time: number } | null>(null);\n  const tapTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n  const panSamples = useRef<{ point: Point; time: number }[]>([]);\n  const glideFrame = useRef<number | null>(null);\n\n  const [contentInfo, setContentInfo] = useState<ContentInfo>({ imageScale: null, isLoading: false });\n  // Nothing to zoom while an image loads, not even its thumbnail: zoomed in, that shows only mush.\n  const highestOf = (info: ContentInfo) =>\n    info.isLoading ? 1 : Math.max(maxScale ?? info.imageScale ?? DEFAULT_MAX_SCALE, 1);\n  const highestScale = highestOf(contentInfo);\n  const clampScale = (scale: number) => Math.min(Math.max(scale, 1), highestScale);\n\n  const commit = useCallback((next: PanZoomTransform, animate = false) => {\n    transformRef.current = next;\n    setTransform(next);\n    setAnimated(animate);\n  }, []);\n\n  /** `justLoaded`: an Image still marks it busy until it renders again. */\n  const updateContent = useCallback(\n    (justLoaded: EventTarget | null = null) => {\n      const content = contentRef.current;\n      if (!content) return;\n      const info = readContent(content, justLoaded);\n      setContentInfo((current) =>\n        current.imageScale === info.imageScale && current.isLoading === info.isLoading ? current : info,\n      );\n      // A new source has nothing to stay zoomed into.\n      if (info.isLoading && transformRef.current !== IDENTITY) commit(IDENTITY);\n    },\n    [commit],\n  );\n\n  // Every render: a new `src` comes with one, and no event says so.\n  useLayoutEffect(() => updateContent());\n\n  function settle(next: PanZoomTransform, geometry: Geometry, animate = false) {\n    commit(settleTransform(next, geometry.viewport, geometry.content), animate);\n  }\n\n  function stopGlide() {\n    if (glideFrame.current !== null) cancelAnimationFrame(glideFrame.current);\n    glideFrame.current = null;\n  }\n\n  function glide(releasedAt: number, geometry: Geometry) {\n    const recent = panSamples.current.filter((sample) => sample.time >= releasedAt - VELOCITY_WINDOW_MS);\n    const first = recent[0];\n    const last = recent[recent.length - 1];\n    if (!first || last.time - first.time < MIN_VELOCITY_SPAN_MS) return;\n    if (window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return;\n\n    const span = last.time - first.time;\n    let velocity = { x: (last.point.x - first.point.x) / span, y: (last.point.y - first.point.y) / span };\n    if (Math.hypot(velocity.x, velocity.y) < GLIDE_MIN_SPEED) return;\n\n    let previous = performance.now();\n    const step = (now: number) => {\n      const elapsed = now - previous;\n      previous = now;\n      const kept = GLIDE_FRICTION ** (elapsed / 16);\n      velocity = { x: velocity.x * kept, y: velocity.y * kept };\n      const current = transformRef.current;\n      const next = settleTransform(\n        { x: current.x + velocity.x * elapsed, y: current.y + velocity.y * elapsed, scale: current.scale },\n        geometry.viewport,\n        geometry.content,\n      );\n      // An edge stops the axis it reaches.\n      if (next.x === current.x) velocity.x = 0;\n      if (next.y === current.y) velocity.y = 0;\n      commit(next);\n      glideFrame.current = Math.hypot(velocity.x, velocity.y) > GLIDE_STOP_SPEED ? requestAnimationFrame(step) : null;\n    };\n    glideFrame.current = requestAnimationFrame(step);\n  }\n\n  /** Zooms around `clientPoint`, or around the middle of the viewport. */\n  function zoomTo(scale: number, clientPoint?: Point, animate = true) {\n    stopGlide();\n    const geometry = measure(rootRef.current, contentRef.current);\n    const focus = clientPoint\n      ? toLocal(clientPoint, geometry)\n      : { x: geometry.viewport.width / 2, y: geometry.viewport.height / 2 };\n    settle(zoomAt(transformRef.current, clampScale(scale), focus), geometry, animate);\n  }\n\n  function reset() {\n    stopGlide();\n    commit(IDENTITY, true);\n  }\n\n  function toggle(clientPoint?: Point) {\n    if (transformRef.current.scale > 1 + EPSILON) reset();\n    else zoomTo(DOUBLE_TAP_SCALE, clientPoint);\n  }\n\n  useImperativeHandle(ref, () => {\n    const unlessDisabled = (action: () => void) => () => {\n      if (!disabled) action();\n    };\n    return {\n      zoomIn: unlessDisabled(() => zoomTo(transformRef.current.scale * ZOOM_STEP)),\n      zoomOut: unlessDisabled(() => zoomTo(transformRef.current.scale / ZOOM_STEP)),\n      reset: unlessDisabled(reset),\n      toggle: unlessDisabled(() => toggle()),\n    };\n  });\n\n  const reportedScale = useRef(1);\n  useEffect(() => {\n    if (transform.scale === reportedScale.current) return;\n    reportedScale.current = transform.scale;\n    onScaleChange?.(transform.scale);\n  }, [transform.scale, onScaleChange]);\n\n  // Animated, so an image a Lightbox leaves zooms out as it slides off rather than jumping.\n  useEffect(() => {\n    if (!disabled) return;\n    pointers.current.clear();\n    gesture.current = null;\n    if (glideFrame.current !== null) cancelAnimationFrame(glideFrame.current);\n    glideFrame.current = null;\n    if (transformRef.current !== IDENTITY) commit(IDENTITY, true);\n  }, [disabled, commit]);\n\n  useEffect(\n    () => () => {\n      if (glideFrame.current !== null) cancelAnimationFrame(glideFrame.current);\n      clearTimeout(tapTimer.current);\n    },\n    [],\n  );\n\n  useEffect(() => {\n    const root = rootRef.current;\n    const content = contentRef.current;\n    if (!root || !content) return;\n    // Caught on the way down, as neither bubbles. A failed image is read before an Image replaces\n    // it with its fallback, and so leaves nothing to zoom.\n    const onLoad = (event: Event) => updateContent(event.target);\n    const onError = () => updateContent();\n    content.addEventListener('load', onLoad, true);\n    content.addEventListener('error', onError, true);\n    const observer = new ResizeObserver(() => {\n      updateContent();\n      const geometry = measure(root, content);\n      const next = settleTransform(transformRef.current, geometry.viewport, geometry.content);\n      const current = transformRef.current;\n      if (next.x !== current.x || next.y !== current.y || next.scale !== current.scale) commit(next);\n    });\n    observer.observe(root);\n    return () => {\n      observer.disconnect();\n      content.removeEventListener('load', onLoad, true);\n      content.removeEventListener('error', onError, true);\n    };\n  }, [commit, updateContent]);\n\n  // React's `wheel` is passive, and could not stop the page scrolling or the browser zooming.\n  const wheelHandlerRef = useRef<(event: WheelEvent) => void>(() => {});\n  useEffect(() => {\n    wheelHandlerRef.current = handleWheel;\n  });\n  useEffect(() => {\n    const root = rootRef.current;\n    if (!root) return;\n    const listener = (event: WheelEvent) => wheelHandlerRef.current(event);\n    root.addEventListener('wheel', listener, { passive: false });\n    return () => root.removeEventListener('wheel', listener);\n  }, []);\n\n  function handleWheel(event: WheelEvent) {\n    // A trackpad pinch arrives as a wheel event with `ctrlKey` set, in Safari too.\n    if (disabled || (!event.ctrlKey && !scrollToZoom)) return;\n    event.preventDefault();\n    // d3-zoom's scaling, for pixel, line and page deltas, and a pinch's far smaller steps.\n    const delta =\n      -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);\n    // Capped, or one notch of Ctrl + mouse wheel zooms 4x.\n    const factor = 2 ** Math.max(-0.5, Math.min(0.5, delta));\n    zoomTo(transformRef.current.scale * factor, { x: event.clientX, y: event.clientY }, false);\n  }\n\n  function pinchFrom(geometry: Geometry): Gesture {\n    const [a, b] = [...pointers.current.values()];\n    return {\n      kind: 'pinch',\n      distance: Math.hypot(b.x - a.x, b.y - a.y) || 1,\n      midpoint: toLocal(midpointOf(a, b), geometry),\n      from: transformRef.current,\n      geometry,\n    };\n  }\n\n  function panFrom(start: Point, geometry: Geometry, captured: boolean): Gesture | null {\n    if (!canPan(transformRef.current.scale, geometry.viewport, geometry.content)) return null;\n    return { kind: 'pan', start, from: transformRef.current, geometry, captured };\n  }\n\n  function handlePointerDown(event: ReactPointerEvent<HTMLDivElement>) {\n    onPointerDown?.(event);\n    if (disabled || controlsRef.current?.contains(event.target as Node)) return;\n    if (event.pointerType === 'mouse' && event.button !== 0) return;\n    // A press catches a glide, the way a finger stops a scrolling list.\n    stopGlide();\n    panSamples.current = [];\n\n    // No other finger is down, whatever was missed: one lifted outside uncaptured never said so.\n    if (event.isPrimary) pointers.current.clear();\n    const point = { x: event.clientX, y: event.clientY };\n    pointers.current.set(event.pointerId, point);\n    const geometry = measure(rootRef.current, contentRef.current);\n\n    if (pointers.current.size === 1) {\n      press.current = { start: point, moved: false };\n      gesture.current = panFrom(point, geometry, false);\n    } else {\n      // The first finger is a parent's swipe, which captured it, so its position here is stale.\n      if (press.current?.moved && !gesture.current) {\n        pointers.current.delete(event.pointerId);\n        return;\n      }\n      press.current = null;\n      gesture.current = pointers.current.size === 2 ? pinchFrom(geometry) : gesture.current;\n      for (const id of pointers.current.keys()) capture(event.currentTarget, id);\n    }\n\n    // Left alone at the fitted size, so a Carousel around this still gets its swipe.\n    if (gesture.current) {\n      event.stopPropagation();\n      setAnimated(false);\n    }\n  }\n\n  function handlePointerMove(event: ReactPointerEvent<HTMLDivElement>) {\n    onPointerMove?.(event);\n    if (!pointers.current.has(event.pointerId)) return;\n    const point = { x: event.clientX, y: event.clientY };\n    pointers.current.set(event.pointerId, point);\n\n    const current = press.current;\n    // `>=`, because Carousel locks a swipe at exactly this distance.\n    if (current && !current.moved && Math.hypot(point.x - current.start.x, point.y - current.start.y) >= TAP_SLOP) {\n      current.moved = true;\n    }\n\n    const active = gesture.current;\n    if (!active) return;\n    event.stopPropagation();\n\n    if (active.kind === 'pan') {\n      // Not on pointerdown: a captured pointer's click goes to the capture target, not the content.\n      if (!active.captured && current?.moved) {\n        capture(event.currentTarget, event.pointerId);\n        active.captured = true;\n      }\n      const { from, start } = active;\n      settle({ x: from.x + point.x - start.x, y: from.y + point.y - start.y, scale: from.scale }, active.geometry);\n      panSamples.current.push({ point, time: event.timeStamp });\n      if (panSamples.current.length > 20) panSamples.current.shift();\n      return;\n    }\n\n    const [a, b] = [...pointers.current.values()];\n    const scale = clampScale((active.from.scale * Math.hypot(b.x - a.x, b.y - a.y)) / active.distance);\n    const midpoint = toLocal(midpointOf(a, b), active.geometry);\n    const zoomed = zoomAt(active.from, scale, active.midpoint);\n    settle(\n      { x: zoomed.x + midpoint.x - active.midpoint.x, y: zoomed.y + midpoint.y - active.midpoint.y, scale },\n      active.geometry,\n    );\n  }\n\n  function handlePointerEnd(event: ReactPointerEvent<HTMLDivElement>) {\n    if (!pointers.current.delete(event.pointerId)) return;\n    const remaining = pointers.current.size;\n\n    if (remaining > 0) {\n      const geometry = measure(rootRef.current, contentRef.current);\n      const [next] = pointers.current.values();\n      gesture.current = remaining >= 2 ? pinchFrom(geometry) : panFrom(next, geometry, true);\n      return;\n    }\n\n    const ended = gesture.current;\n    gesture.current = null;\n    if (ended?.kind === 'pan' && event.type === 'pointerup') glide(event.timeStamp, ended.geometry);\n    const finished = press.current;\n    press.current = null;\n    if (event.type !== 'pointerup' || !finished || finished.moved) return;\n    if (isInteractiveTarget(event.target, rootRef.current)) return;\n\n    const tap = { point: { x: event.clientX, y: event.clientY }, time: event.timeStamp };\n    const previous = lastTap.current;\n    const isDouble =\n      previous &&\n      tap.time - previous.time < DOUBLE_TAP_MS &&\n      Math.hypot(tap.point.x - previous.point.x, tap.point.y - previous.point.y) < DOUBLE_TAP_DISTANCE;\n    lastTap.current = isDouble ? null : tap;\n    clearTimeout(tapTimer.current);\n    if (!isDouble) {\n      // Reported, it can no longer be half of a double tap: a tap at the edge of the window would do both.\n      if (onTap) {\n        tapTimer.current = setTimeout(() => {\n          lastTap.current = null;\n          onTap(event);\n        }, DOUBLE_TAP_MS);\n      }\n      return;\n    }\n\n    toggle(tap.point);\n  }\n\n  function handleKeyDown(event: ReactKeyboardEvent<HTMLDivElement>) {\n    onKeyDown?.(event);\n    if (disabled || event.defaultPrevented || event.ctrlKey || event.metaKey || event.altKey) return;\n    if (isInteractiveTarget(event.target, rootRef.current)) return;\n\n    stopGlide();\n    const current = transformRef.current;\n    if (event.key === '+' || event.key === '=') {\n      zoomTo(current.scale * ZOOM_STEP);\n    } else if (event.key === '-' || event.key === '_') {\n      zoomTo(current.scale / ZOOM_STEP);\n    } else if (event.key === '0') {\n      reset();\n    } else {\n      const direction = ARROWS[event.key];\n      if (!direction) return;\n      const geometry = measure(rootRef.current, contentRef.current);\n      // Left to a Lightbox around this, while there is nothing to pan.\n      if (!canPan(current.scale, geometry.viewport, geometry.content)) return;\n      const step = { x: geometry.viewport.width * KEY_PAN_RATIO, y: geometry.viewport.height * KEY_PAN_RATIO };\n      settle(\n        { x: current.x - direction.x * step.x, y: current.y - direction.y * step.y, scale: current.scale },\n        geometry,\n        true,\n      );\n    }\n    event.preventDefault();\n  }\n\n  const isZoomed = transform.scale > 1 + EPSILON;\n  const isAtMax = transform.scale >= highestScale - EPSILON;\n  const isIdentity = transform.x === 0 && transform.y === 0 && transform.scale === 1;\n\n  return (\n    <div\n      {...rest}\n      ref={rootRef}\n      tabIndex={disabled ? undefined : (rest.tabIndex ?? 0)}\n      className={cx(\n        styles.panZoom,\n        isZoomed && styles.panZoomZoomed,\n        (isZoomed || scrollToZoom) && styles.panZoomCapture,\n        className,\n        classNames?.root,\n      )}\n      style={{ ...style, ...slotStyles?.root }}\n      onPointerDown={handlePointerDown}\n      onPointerMove={handlePointerMove}\n      onPointerUp={(event) => {\n        onPointerUp?.(event);\n        handlePointerEnd(event);\n      }}\n      onPointerCancel={(event) => {\n        onPointerCancel?.(event);\n        handlePointerEnd(event);\n      }}\n      onKeyDown={handleKeyDown}\n      // The browser's image drag would take the pointer mid-pan.\n      onDragStart={(event) => {\n        onDragStart?.(event);\n        event.preventDefault();\n      }}\n    >\n      <div\n        ref={contentRef}\n        className={cx(styles.panZoomContent, animated && styles.panZoomContentAnimated, classNames?.content)}\n        style={{\n          transform: isIdentity ? undefined : `translate(${transform.x}px, ${transform.y}px) scale(${transform.scale})`,\n          ...slotStyles?.content,\n        }}\n      >\n        {children}\n      </div>\n\n      {showControls && !disabled && (\n        <div\n          ref={controlsRef}\n          className={cx(styles.panZoomControls, classNames?.controls)}\n          style={slotStyles?.controls}\n        >\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={CONTROL_COLOR}\n            icon={faMagnifyingGlassMinus}\n            title={text.zoomOut}\n            aria-label={text.zoomOut}\n            aria-disabled={!isZoomed || undefined}\n            onClick={() => {\n              if (isZoomed) zoomTo(transformRef.current.scale / ZOOM_STEP);\n            }}\n            className={cx(styles.panZoomControl, !isZoomed && styles.panZoomControlDisabled, classNames?.zoomOutButton)}\n            style={slotStyles?.zoomOutButton}\n          />\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={CONTROL_COLOR}\n            icon={faMagnifyingGlassPlus}\n            title={text.zoomIn}\n            aria-label={text.zoomIn}\n            aria-disabled={isAtMax || undefined}\n            onClick={() => {\n              if (!isAtMax) zoomTo(transformRef.current.scale * ZOOM_STEP);\n            }}\n            className={cx(styles.panZoomControl, isAtMax && styles.panZoomControlDisabled, classNames?.zoomInButton)}\n            style={slotStyles?.zoomInButton}\n          />\n          <ActionIcon\n            styleType={ButtonStyleType.Filled}\n            color={CONTROL_COLOR}\n            icon={faRotateLeft}\n            title={text.reset}\n            aria-label={text.reset}\n            aria-disabled={isIdentity || undefined}\n            onClick={() => {\n              if (!isIdentity) reset();\n            }}\n            className={cx(styles.panZoomControl, isIdentity && styles.panZoomControlDisabled, classNames?.resetButton)}\n            style={slotStyles?.resetButton}\n          />\n        </div>\n      )}\n    </div>\n  );\n}\n\n/**\n * All before any transform. The children's rects are divided by the rendered scale, not the one in\n * state, which halfway through an animated zoom differs.\n */\nfunction measure(root: HTMLElement | null, content: HTMLElement | null): Geometry {\n  if (!root || !content) return { viewport: { width: 0, height: 0 }, content: EMPTY_BOX, origin: { x: 0, y: 0 } };\n\n  const rootRect = root.getBoundingClientRect();\n  const origin = { x: rootRect.left + root.clientLeft, y: rootRect.top + root.clientTop };\n  const viewport = { width: content.offsetWidth, height: content.offsetHeight };\n  const contentRect = content.getBoundingClientRect();\n  const scale = viewport.width > 0 && contentRect.width > 0 ? contentRect.width / viewport.width : 1;\n\n  let left = Infinity;\n  let top = Infinity;\n  let right = -Infinity;\n  let bottom = -Infinity;\n  for (const child of content.children) {\n    const rect = child.getBoundingClientRect();\n    if (rect.width === 0 && rect.height === 0) continue;\n    left = Math.min(left, (rect.left - contentRect.left) / scale);\n    top = Math.min(top, (rect.top - contentRect.top) / scale);\n    right = Math.max(right, (rect.right - contentRect.left) / scale);\n    bottom = Math.max(bottom, (rect.bottom - contentRect.top) / scale);\n  }\n\n  // Nothing measurable, such as bare text: the whole viewport stands in for the content.\n  const box =\n    left === Infinity ? { left: 0, top: 0, ...viewport } : { left, top, width: right - left, height: bottom - top };\n  return { viewport, content: box, origin };\n}\n\nconst EMPTY_BOX: Box = { left: 0, top: 0, width: 0, height: 0 };\n\ninterface ContentInfo {\n  /** From the natural size: the rendered one is still the thumbnail's while a Lightbox image loads. */\n  imageScale: number | null;\n  isLoading: boolean;\n}\n\nfunction readContent(content: HTMLElement, justLoaded: EventTarget | null): ContentInfo {\n  const width = content.offsetWidth;\n  const height = content.offsetHeight;\n  let own = 0;\n  let isLoading = false;\n  for (const child of content.children) {\n    // By tag, not `instanceof`, which can fail across realms.\n    if (child.tagName !== 'IMG') continue;\n    const image = child as HTMLImageElement;\n    const busy = image !== justLoaded && image.getAttribute('aria-busy') === 'true';\n    if (busy || !image.complete || !image.naturalWidth) isLoading = true;\n    else if (width && height) own = Math.max(own, image.naturalWidth / width, image.naturalHeight / height);\n  }\n  return { imageScale: own ? Math.max(MIN_IMAGE_MAX_SCALE, own) : null, isLoading };\n}\n\nfunction capture(element: Element, pointerId: number) {\n  try {\n    element.setPointerCapture?.(pointerId);\n  } catch {\n    // The pointer is already gone.\n  }\n}\n\nfunction midpointOf(a: Point, b: Point): Point {\n  return { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n}\n\nfunction toLocal(clientPoint: Point, geometry: Geometry): Point {\n  return { x: clientPoint.x - geometry.origin.x, y: clientPoint.y - geometry.origin.y };\n}\n\nexport default PanZoom;\n"],"mappings":"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"}