{"version":3,"file":"index.d.ts","sources":["../src/elements/EdgeLine.ts","../src/controllers/GraphController.ts","../src/controllers/ForceDirectedGraphController.ts","../src/controllers/DendrogramController.ts","../src/controllers/TreeController.ts"],"sourcesContent":["import {\n  ChartType,\n  LineElement,\n  LineOptions,\n  PointElement,\n  ScriptableAndArrayOptions,\n  ScriptableContext,\n} from 'chart.js';\n\nfunction horizontal(from: { x: number }, to: { x: number }, options: { tension: number }) {\n  return {\n    fx: (to.x - from.x) * options.tension,\n    fy: 0,\n    tx: (from.x - to.x) * options.tension,\n    ty: 0,\n  };\n}\n\nfunction vertical(from: { y: number }, to: { y: number }, options: { tension: number }) {\n  return {\n    fx: 0,\n    fy: (to.y - from.y) * options.tension,\n    tx: 0,\n    ty: (from.y - to.y) * options.tension,\n  };\n}\n\nfunction radial(\n  from: { x: number; angle?: number; y: number },\n  to: { x: number; angle?: number; y: number },\n  options: { tension: number }\n) {\n  const angleHelper = Math.hypot(to.x - from.x, to.y - from.y) * options.tension;\n  return {\n    fx: Number.isNaN(from.angle) ? 0 : Math.cos(from.angle || 0) * angleHelper,\n    fy: Number.isNaN(from.angle) ? 0 : Math.sin(from.angle || 0) * -angleHelper,\n    tx: Number.isNaN(to.angle) ? 0 : Math.cos(to.angle || 0) * -angleHelper,\n    ty: Number.isNaN(to.angle) ? 0 : Math.sin(to.angle || 0) * angleHelper,\n  };\n}\n\nexport interface IEdgeLineOptions extends LineOptions {\n  directed: boolean;\n  arrowHeadSize: number;\n  arrowHeadOffset: number;\n}\n\nexport interface IEdgeLineProps extends LineOptions {\n  points: { x: number; y: number }[];\n}\n\nexport class EdgeLine extends LineElement {\n  /**\n   * @hidden\n   */\n  declare _orientation: 'vertical' | 'radial' | 'horizontal';\n\n  /**\n   * @hidden\n   */\n  declare source: PointElement;\n\n  /**\n   * @hidden\n   */\n  declare target: PointElement;\n\n  /**\n   * @hidden\n   */\n  declare options: IEdgeLineOptions;\n\n  /**\n   * @hidden\n   */\n  draw(ctx: CanvasRenderingContext2D): void {\n    const { options } = this;\n\n    ctx.save();\n\n    // Stroke Line Options\n    ctx.lineCap = options.borderCapStyle;\n    ctx.setLineDash(options.borderDash || []);\n    ctx.lineDashOffset = options.borderDashOffset;\n    ctx.lineJoin = options.borderJoinStyle;\n    ctx.lineWidth = options.borderWidth;\n    ctx.strokeStyle = options.borderColor;\n\n    const orientations = {\n      horizontal,\n      vertical,\n      radial,\n    };\n    const layout = orientations[this._orientation] || orientations.horizontal;\n\n    const renderLine = (\n      from: { x: number; y: number; angle?: number },\n      to: { x: number; y: number; angle?: number }\n    ) => {\n      const shift = layout(from, to, options);\n\n      const fromX = {\n        cpx: from.x + shift.fx,\n        cpy: from.y + shift.fy,\n      };\n      const toX = {\n        cpx: to.x + shift.tx,\n        cpy: to.y + shift.ty,\n      };\n\n      // Line to next point\n      if (options.stepped === 'middle') {\n        const midpoint = (from.x + to.x) / 2.0;\n        ctx.lineTo(midpoint, from.y);\n        ctx.lineTo(midpoint, to.y);\n        ctx.lineTo(to.x, to.y);\n      } else if (options.stepped === 'after') {\n        ctx.lineTo(from.x, to.y);\n        ctx.lineTo(to.x, to.y);\n      } else if (options.stepped) {\n        ctx.lineTo(to.x, from.y);\n        ctx.lineTo(to.x, to.y);\n      } else if (options.tension) {\n        ctx.bezierCurveTo(fromX.cpx, fromX.cpy, toX.cpx, toX.cpy, to.x, to.y);\n      } else {\n        ctx.lineTo(to.x, to.y);\n      }\n      return to;\n    };\n\n    const source = this.source.getProps(['x', 'y', 'angle']) as { x: number; y: number; angle?: number };\n    const target = this.target.getProps(['x', 'y', 'angle']) as { x: number; y: number; angle?: number };\n    const points = (this.getProps(['points'] as any) as any).points as {\n      x: number;\n      y: number;\n      angle: number;\n    }[];\n\n    // Stroke Line\n    ctx.beginPath();\n\n    let from = source;\n    ctx.moveTo(from.x, from.y);\n    if (points && points.length > 0) {\n      from = points.reduce(renderLine, from);\n    }\n    renderLine(from, target);\n\n    ctx.stroke();\n\n    if (options.directed) {\n      const to = target;\n      // compute the rotation based on from and to\n      const shift = layout(from, to, options);\n      const s = options.arrowHeadSize;\n      const offset = options.arrowHeadOffset;\n      ctx.save();\n      ctx.translate(to.x, target.y);\n      if (options.stepped === 'middle') {\n        const midpoint = (from.x + to.x) / 2.0;\n        ctx.rotate(Math.atan2(to.y - to.y, to.x - midpoint));\n      } else if (options.stepped === 'after') {\n        ctx.rotate(Math.atan2(to.y - to.y, to.x - from.x));\n      } else if (options.stepped) {\n        ctx.rotate(Math.atan2(to.y - from.y, to.x - to.x));\n      } else if (options.tension) {\n        const toX = {\n          x: to.x + shift.tx,\n          y: to.y + shift.ty,\n        };\n        const f = 0.1;\n        ctx.rotate(Math.atan2(to.y - toX.y * (1 - f) - from.y * f, to.x - toX.x * (1 - f) - from.x * f));\n      } else {\n        ctx.rotate(Math.atan2(to.y - from.y, to.x - from.x));\n      }\n      ctx.translate(-offset, 0);\n      ctx.beginPath();\n\n      ctx.moveTo(0, 0);\n      ctx.lineTo(-s, -s / 2);\n      ctx.lineTo(-s * 0.9, 0);\n      ctx.lineTo(-s, s / 2);\n      ctx.closePath();\n      ctx.fillStyle = ctx.strokeStyle;\n      ctx.fill();\n\n      ctx.restore();\n    }\n\n    ctx.restore();\n\n    // point helper\n    // ctx.save();\n    // ctx.strokeStyle = 'blue';\n    // ctx.beginPath();\n    // ctx.moveTo(from.x, from.y);\n    // ctx.lineTo(from.x + shift.fx, from.y + shift.fy, 3, 3);\n    // ctx.stroke();\n    // ctx.strokeStyle = 'red';\n    // ctx.beginPath();\n    // ctx.moveTo(to.x, to.y);\n    // ctx.lineTo(to.x + shift.tx, to.y + shift.ty, 3, 3);\n    // ctx.stroke();\n    // ctx.restore();\n  }\n\n  static readonly id = 'edgeLine';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ {\n    ...LineElement.defaults,\n    tension: 0,\n    directed: false,\n    arrowHeadSize: 15,\n    arrowHeadOffset: 5,\n  };\n\n  /**\n   * @hidden\n   */\n  static readonly defaultRoutes = LineElement.defaultRoutes;\n\n  /**\n   * @hidden\n   */\n  static readonly descriptors = /* #__PURE__ */ {\n    _scriptable: true,\n    _indexable: (name: keyof IEdgeLineOptions): boolean => name !== 'borderDash',\n  };\n}\n\ndeclare module 'chart.js' {\n  export interface ElementOptionsByType<TType extends ChartType> {\n    edgeLine: ScriptableAndArrayOptions<IEdgeLineOptions, ScriptableContext<TType>>;\n  }\n}\n","import {\n  defaults,\n  Chart,\n  ScatterController,\n  registry,\n  LinearScale,\n  PointElement,\n  UpdateMode,\n  TooltipItem,\n  ChartItem,\n  ChartConfiguration,\n  ControllerDatasetOptions,\n  ScriptableAndArrayOptions,\n  LineHoverOptions,\n  PointPrefixedOptions,\n  PointPrefixedHoverOptions,\n  ScriptableContext,\n  Element,\n  CartesianScaleTypeRegistry,\n  CoreChartOptions,\n} from 'chart.js';\nimport { merge, clipArea, unclipArea, listenArrayEvents, unlistenArrayEvents } from 'chart.js/helpers';\nimport { EdgeLine, IEdgeLineOptions } from '../elements';\nimport interpolatePoints from './interpolatePoints';\nimport patchController from './patchController';\n\nexport type AnyObject = Record<string, unknown>;\n\nexport interface IExtendedChartMeta {\n  edges: EdgeLine[];\n  _parsedEdges: ITreeEdge[];\n}\n\nexport interface ITreeNode extends IGraphDataPoint {\n  x: number;\n  y: number;\n  index?: number;\n}\n\nexport interface ITreeEdge {\n  source: number;\n  target: number;\n  points?: { x: number; y: number }[];\n}\n\nexport class GraphController extends ScatterController {\n  /**\n   * @hidden\n   */\n  declare _ctx: CanvasRenderingContext2D;\n\n  /**\n   * @hidden\n   */\n  declare _cachedDataOpts: any;\n\n  /**\n   * @hidden\n   */\n  declare _type: string;\n\n  /**\n   * @hidden\n   */\n  declare _data: any[];\n\n  /**\n   * @hidden\n   */\n  declare _edges: any[];\n\n  /**\n   * @hidden\n   */\n  declare _sharedOptions: any;\n\n  /**\n   * @hidden\n   */\n  declare _edgeSharedOptions: any;\n\n  /**\n   * @hidden\n   */\n  declare dataElementType: any;\n\n  /**\n   * @hidden\n   */\n  private _scheduleResyncLayoutId = -1;\n\n  /**\n   * @hidden\n   */\n  edgeElementType: any;\n\n  /**\n   * @hidden\n   */\n  private readonly _edgeListener = {\n    _onDataPush: (...args: any[]) => {\n      const count = args.length;\n      const start = (this.getDataset() as any).edges.length - count;\n      const parsed = (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges;\n      args.forEach((edge) => {\n        parsed.push(this._parseDefinedEdge(edge));\n      });\n      this._insertEdgeElements(start, count);\n    },\n    _onDataPop: () => {\n      (this._cachedMeta as unknown as IExtendedChartMeta).edges.pop();\n      (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges.pop();\n      this._scheduleResyncLayout();\n    },\n    _onDataShift: () => {\n      (this._cachedMeta as unknown as IExtendedChartMeta).edges.shift();\n      (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges.shift();\n      this._scheduleResyncLayout();\n    },\n    _onDataSplice: (start: number, count: number, ...args: any[]) => {\n      (this._cachedMeta as unknown as IExtendedChartMeta).edges.splice(start, count);\n      (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges.splice(start, count);\n      if (args.length > 0) {\n        const parsed = (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges;\n        parsed.splice(start, 0, ...args.map((edge) => this._parseDefinedEdge(edge)));\n        this._insertEdgeElements(start, args.length);\n      } else {\n        this._scheduleResyncLayout();\n      }\n    },\n    _onDataUnshift: (...args: any[]) => {\n      const parsed = (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges;\n      parsed.unshift(...args.map((edge) => this._parseDefinedEdge(edge)));\n      this._insertEdgeElements(0, args.length);\n    },\n  };\n\n  /**\n   * @hidden\n   */\n  initialize(): void {\n    const type = this._type;\n    const defaultConfig = defaults.datasets[type as 'graph'] as any;\n    this.edgeElementType = registry.getElement(defaultConfig.edgeElementType as string);\n    super.initialize();\n    this.enableOptionSharing = true;\n    this._scheduleResyncLayout();\n  }\n\n  /**\n   * @hidden\n   */\n  parse(start: number, count: number): void {\n    const meta = this._cachedMeta;\n    const data = this._data;\n    const { iScale, vScale } = meta;\n    for (let i = 0; i < count; i += 1) {\n      const index = i + start;\n      const d = data[index];\n      const v = (meta._parsed[index] || {}) as { x: number; y: number };\n      if (d && typeof d.x === 'number') {\n        v.x = d.x;\n      }\n      if (d && typeof d.y === 'number') {\n        v.y = d.y;\n      }\n      meta._parsed[index] = v;\n    }\n    if (meta._parsed.length > data.length) {\n      meta._parsed.splice(data.length, meta._parsed.length - data.length);\n    }\n    this._cachedMeta._sorted = false;\n    (iScale as any)._dataLimitsCached = false;\n    (vScale as any)._dataLimitsCached = false;\n\n    this._parseEdges();\n  }\n\n  /**\n   * @hidden\n   */\n  reset(): void {\n    this.resetLayout();\n    super.reset();\n  }\n\n  /**\n   * @hidden\n   */\n  update(mode: UpdateMode): void {\n    super.update(mode);\n\n    const meta = this._cachedMeta as unknown as IExtendedChartMeta;\n    const edges = meta.edges || [];\n\n    this.updateEdgeElements(edges, 0, mode);\n  }\n\n  /**\n   * @hidden\n   */\n  _destroy(): void {\n    (ScatterController.prototype as any)._destroy.call(this);\n    if (this._edges) {\n      unlistenArrayEvents(this._edges, this._edgeListener);\n    }\n    this.stopLayout();\n  }\n\n  declare getContext: (index: number, active: boolean, mode: UpdateMode) => unknown;\n\n  /**\n   * @hidden\n   */\n  updateEdgeElements(edges: EdgeLine[], start: number, mode: UpdateMode): void {\n    const bak = {\n      _cachedDataOpts: this._cachedDataOpts,\n      dataElementType: this.dataElementType,\n      _sharedOptions: this._sharedOptions,\n      // getDataset: this.getDataset,\n      // getParsed: this.getParsed,\n    };\n    this._cachedDataOpts = {};\n    this.dataElementType = this.edgeElementType;\n    this._sharedOptions = this._edgeSharedOptions;\n\n    const dataset = this.getDataset();\n    const meta = this._cachedMeta;\n    const nodeElements = meta.data;\n    const data = (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges;\n\n    // get generic context to prefill cache\n    this.getContext(-1, false, mode);\n    this.getDataset = () => {\n      return new Proxy(dataset, {\n        get(obj: any, prop: string) {\n          return prop === 'data' ? (obj.edges ?? []) : obj[prop];\n        },\n      });\n    };\n    this.getParsed = (index: number) => {\n      return data[index] as any;\n    };\n    // patch meta to store edges\n    meta.data = (meta as any).edges;\n\n    const reset = mode === 'reset';\n\n    const firstOpts = this.resolveDataElementOptions(start, mode);\n    const dummyShared = {};\n    const sharedOptions = this.getSharedOptions(firstOpts) ?? dummyShared;\n    const includeOptions = this.includeOptions(mode, sharedOptions);\n\n    const { xScale, yScale } = meta;\n\n    const base = {\n      x: xScale?.getBasePixel() ?? 0,\n      y: yScale?.getBasePixel() ?? 0,\n    };\n\n    function copyPoint(point: { x: number; y: number; angle?: number }) {\n      const x = reset ? base.x : (xScale?.getPixelForValue(point.x, 0) ?? 0);\n      const y = reset ? base.y : (yScale?.getPixelForValue(point.y, 0) ?? 0);\n      return {\n        x,\n        y,\n        angle: point.angle,\n      };\n    }\n\n    for (let i = 0; i < edges.length; i += 1) {\n      const edge = edges[i];\n      const index = start + i;\n      const parsed = data[index];\n\n      const properties: any = {\n        source: nodeElements[parsed.source],\n        target: nodeElements[parsed.target],\n        points: Array.isArray(parsed.points) ? parsed.points.map((p) => copyPoint(p)) : [],\n      };\n      properties.points._source = nodeElements[parsed.source];\n      if (includeOptions) {\n        if (sharedOptions !== dummyShared) {\n          properties.options = sharedOptions;\n        } else {\n          properties.options = this.resolveDataElementOptions(index, mode);\n        }\n      }\n      this.updateEdgeElement(edge, index, properties, mode);\n    }\n    this.updateSharedOptions(sharedOptions, mode, firstOpts);\n\n    this._edgeSharedOptions = this._sharedOptions;\n    Object.assign(this, bak);\n    delete (this as any).getDataset;\n    delete (this as any).getParsed;\n    // patch meta to store edges\n    meta.data = nodeElements;\n  }\n\n  /**\n   * @hidden\n   */\n\n  updateEdgeElement(edge: EdgeLine, index: number, properties: any, mode: UpdateMode): void {\n    super.updateElement(edge as unknown as Element<AnyObject, AnyObject>, index, properties, mode);\n  }\n\n  /**\n   * @hidden\n   */\n\n  updateElement(point: Element<AnyObject, AnyObject>, index: number, properties: any, mode: UpdateMode): void {\n    if (mode === 'reset') {\n      // start in center also in x\n      const { xScale } = this._cachedMeta;\n\n      properties.x = xScale?.getBasePixel() ?? 0;\n    }\n    super.updateElement(point, index, properties, mode);\n  }\n\n  /**\n   * @hidden\n   */\n  resolveNodeIndex(nodes: any[], ref: string | number | any): number {\n    if (typeof ref === 'number') {\n      // index\n      return ref;\n    }\n    if (typeof ref === 'string') {\n      // label\n      const labels = this.chart.data.labels as string[];\n      return labels.indexOf(ref);\n    }\n    const nIndex = nodes.indexOf(ref);\n    if (nIndex >= 0) {\n      // hit\n      return nIndex;\n    }\n\n    const data = this.getDataset().data as any[];\n    const index = data.indexOf(ref);\n    if (index >= 0) {\n      return index;\n    }\n\n    console.warn('cannot resolve edge ref', ref);\n    return -1;\n  }\n\n  /**\n   * @hidden\n   */\n  buildOrUpdateElements(): void {\n    const dataset = this.getDataset() as any;\n    const edges = dataset.edges || [];\n\n    // In order to correctly handle data addition/deletion animation (an thus simulate\n    // real-time charts), we need to monitor these data modifications and synchronize\n    // the internal meta data accordingly.\n    if (this._edges !== edges) {\n      if (this._edges) {\n        // This case happens when the user replaced the data array instance.\n        unlistenArrayEvents(this._edges, this._edgeListener);\n      }\n\n      if (edges && Object.isExtensible(edges)) {\n        listenArrayEvents(edges, this._edgeListener);\n      }\n      this._edges = edges;\n    }\n    super.buildOrUpdateElements();\n  }\n\n  /**\n   * @hidden\n   */\n  draw(): void {\n    const meta = this._cachedMeta;\n    const edges = (this._cachedMeta as unknown as IExtendedChartMeta).edges || [];\n    const elements = (meta.data || []) as unknown[] as PointElement[];\n\n    const area = this.chart.chartArea;\n    const ctx = this._ctx;\n\n    if (edges.length > 0) {\n      clipArea(ctx, area);\n      edges.forEach((edge) => (edge.draw.call as any)(edge, ctx, area));\n      unclipArea(ctx);\n    }\n\n    elements.forEach((elem) => (elem.draw.call as any)(elem, ctx, area));\n  }\n\n  protected _resyncElements(): void {\n    (ScatterController.prototype as any)._resyncElements.call(this);\n\n    const meta = this._cachedMeta as unknown as IExtendedChartMeta;\n    const edges = meta._parsedEdges;\n    const metaEdges = meta.edges || (meta.edges = []);\n    const numMeta = metaEdges.length;\n    const numData = edges.length;\n\n    if (numData < numMeta) {\n      metaEdges.splice(numData, numMeta - numData);\n      this._scheduleResyncLayout();\n    } else if (numData > numMeta) {\n      this._insertEdgeElements(numMeta, numData - numMeta);\n    }\n  }\n\n  getTreeRootIndex(): number {\n    const ds = this.getDataset() as any;\n    const nodes = ds.data as any[];\n    if (ds.derivedEdges) {\n      // find the one with no parent\n      return nodes.findIndex((d) => d.parent == null);\n    }\n    // find the one with no edge\n    const edges = (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges || [];\n    const nodeIndices = new Set(nodes.map((_, i) => i));\n    edges.forEach((edge) => {\n      nodeIndices.delete(edge.target);\n    });\n    return Array.from(nodeIndices)[0];\n  }\n\n  getTreeRoot(): ITreeNode {\n    const index = this.getTreeRootIndex();\n    const p = this.getParsed(index) as ITreeNode;\n    p.index = index;\n    return p;\n  }\n\n  getTreeChildren(node: { index?: number }): ITreeNode[] {\n    const edges = (this._cachedMeta as unknown as IExtendedChartMeta)._parsedEdges;\n    const index = node.index ?? 0;\n    return edges\n      .filter((d) => d.source === index)\n      .map((d) => {\n        const p = this.getParsed(d.target) as ITreeNode;\n        p.index = d.target;\n        return p;\n      });\n  }\n\n  /**\n   * @hidden\n   */\n  _parseDefinedEdge(edge: { source: number; target: number }): ITreeEdge {\n    const ds = this.getDataset();\n    const { data } = ds;\n    return {\n      source: this.resolveNodeIndex(data, edge.source),\n      target: this.resolveNodeIndex(data, edge.target),\n      points: [],\n    };\n  }\n\n  /**\n   * @hidden\n   */\n  _parseEdges(): ITreeEdge[] {\n    const ds = this.getDataset() as any;\n    const data = ds.data as { parent?: number }[];\n    const meta = this._cachedMeta as unknown as IExtendedChartMeta;\n    if (ds.edges) {\n      const edges = ds.edges.map((edge: any) => this._parseDefinedEdge(edge));\n      meta._parsedEdges = edges;\n      return edges;\n    }\n\n    const edges: ITreeEdge[] = [];\n    meta._parsedEdges = edges as any;\n    // try to derive edges via parent links\n    data.forEach((node, i) => {\n      if (node.parent != null) {\n        // tree edge\n        const parent = this.resolveNodeIndex(data, node.parent);\n        edges.push({\n          source: parent,\n          target: i,\n          points: [],\n        });\n      }\n    });\n    return edges;\n  }\n\n  /**\n   * @hidden\n   */\n  addElements(): void {\n    super.addElements();\n\n    const meta = this._cachedMeta as unknown as IExtendedChartMeta;\n    const edges = this._parseEdges();\n    const metaData = new Array(edges.length);\n    meta.edges = metaData;\n\n    for (let i = 0; i < edges.length; i += 1) {\n      metaData[i] = new this.edgeElementType();\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  _resyncEdgeElements(): void {\n    const meta = this._cachedMeta as unknown as IExtendedChartMeta;\n    const edges = this._parseEdges();\n    const metaData = meta.edges || (meta.edges = []);\n\n    for (let i = 0; i < edges.length; i += 1) {\n      metaData[i] = metaData[i] || new this.edgeElementType();\n    }\n    if (edges.length < metaData.length) {\n      metaData.splice(edges.length, metaData.length);\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  _insertElements(start: number, count: number): void {\n    (ScatterController.prototype as any)._insertElements.call(this, start, count);\n    if (count > 0) {\n      this._resyncEdgeElements();\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  _removeElements(start: number, count: number): void {\n    (ScatterController.prototype as any)._removeElements.call(this, start, count);\n    if (count > 0) {\n      this._resyncEdgeElements();\n    }\n  }\n\n  /**\n   * @hidden\n   */\n  _insertEdgeElements(start: number, count: number): void {\n    const elements = [];\n    for (let i = 0; i < count; i += 1) {\n      elements.push(new this.edgeElementType());\n    }\n    (this._cachedMeta as unknown as IExtendedChartMeta).edges.splice(start, 0, ...elements);\n    this.updateEdgeElements(elements, start, 'reset');\n    this._scheduleResyncLayout();\n  }\n\n  reLayout(): void {\n    // hook\n  }\n\n  resetLayout(): void {\n    // hook\n  }\n\n  stopLayout(): void {\n    // hook\n  }\n\n  /**\n   * @hidden\n   */\n  _scheduleResyncLayout(): void {\n    if (this._scheduleResyncLayoutId != null && this._scheduleResyncLayoutId >= 0) {\n      return;\n    }\n    this._scheduleResyncLayoutId = requestAnimationFrame(() => {\n      this._scheduleResyncLayoutId = -1;\n      this.resyncLayout();\n    });\n  }\n\n  resyncLayout(): void {\n    // hook\n  }\n\n  static readonly id: string = 'graph';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    ScatterController.defaults,\n    {\n      clip: 10, // some space in combination with padding\n      animations: {\n        points: {\n          fn: interpolatePoints,\n          properties: ['points'],\n        },\n      },\n      edgeElementType: EdgeLine.id,\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ merge({}, [\n    (ScatterController as any).overrides,\n    {\n      layout: {\n        padding: 10,\n      },\n      scales: {\n        x: {\n          display: false,\n          ticks: {\n            maxTicksLimit: 2,\n            precision: 100,\n            minRotation: 0,\n            maxRotation: 0,\n          },\n        },\n        y: {\n          display: false,\n          ticks: {\n            maxTicksLimit: 2,\n            precision: 100,\n            minRotation: 0,\n            maxRotation: 0,\n          },\n        },\n      },\n      plugins: {\n        tooltip: {\n          callbacks: {\n            label(item: TooltipItem<'graph'>) {\n              return item.chart.data?.labels?.[item.dataIndex];\n            },\n          },\n        },\n      },\n    },\n  ]);\n}\n\nexport interface IGraphDataPoint {\n  parent?: number;\n}\n\nexport interface IGraphEdgeDataPoint {\n  source: number | string;\n  target: number | string;\n}\n\nexport interface IGraphChartControllerDatasetOptions\n  extends ControllerDatasetOptions,\n    ScriptableAndArrayOptions<PointPrefixedOptions, ScriptableContext<'graph'>>,\n    ScriptableAndArrayOptions<PointPrefixedHoverOptions, ScriptableContext<'graph'>>,\n    ScriptableAndArrayOptions<IEdgeLineOptions, ScriptableContext<'graph'>>,\n    ScriptableAndArrayOptions<LineHoverOptions, ScriptableContext<'graph'>> {\n  edges: IGraphEdgeDataPoint[];\n}\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    graph: {\n      chartOptions: CoreChartOptions<'graph'>;\n      datasetOptions: IGraphChartControllerDatasetOptions;\n      defaultDataPoint: IGraphDataPoint;\n      metaExtensions: Record<string, never>;\n      parsedDataType: ITreeNode;\n      scales: keyof CartesianScaleTypeRegistry;\n    };\n  }\n}\n\nexport class GraphChart<DATA extends unknown[] = IGraphDataPoint[], LABEL = string> extends Chart<\n  'graph',\n  DATA,\n  LABEL\n> {\n  static id = GraphController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'graph', DATA, LABEL>, 'type'>) {\n    super(item, patchController('graph', config, GraphController, [EdgeLine, PointElement], LinearScale));\n  }\n}\n","import {\n  Chart,\n  ChartItem,\n  ChartConfiguration,\n  LinearScale,\n  PointElement,\n  CoreChartOptions,\n  CartesianScaleTypeRegistry,\n} from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport {\n  forceCenter,\n  forceCollide,\n  forceLink,\n  ForceLink,\n  forceManyBody,\n  forceRadial,\n  forceSimulation,\n  forceX,\n  forceY,\n  Simulation,\n  SimulationLinkDatum,\n  SimulationNodeDatum,\n} from 'd3-force';\nimport { EdgeLine } from '../elements';\nimport {\n  GraphController,\n  IGraphChartControllerDatasetOptions,\n  IGraphDataPoint,\n  ITreeNode,\n  IExtendedChartMeta,\n} from './GraphController';\nimport patchController from './patchController';\n\nexport interface ITreeSimNode extends ITreeNode {\n  _sim: { x?: number; y?: number; vx?: number; vy?: number; index?: number };\n  reset?: boolean;\n}\n\nexport interface IForceDirectedControllerOptions {\n  simulation: {\n    /**\n     * auto restarts the simulation upon dataset change, one can manually restart by calling: `chart.getDatasetMeta(0).controller.reLayout();`\n     *\n     * @default true\n     */\n    autoRestart: boolean;\n\n    initialIterations: number;\n\n    forces: {\n      /**\n       * center force\n       * https://github.com/d3/d3-force/#centering\n       *\n       * @default true\n       */\n      center: boolean | ICenterForce;\n\n      /**\n       * collision between nodes\n       * https://github.com/d3/d3-force/#collision\n       *\n       * @default false\n       */\n      collide: boolean | ICollideForce;\n\n      /**\n       * link force\n       * https://github.com/d3/d3-force/#links\n       *\n       * @default true\n       */\n      link: boolean | ILinkForce;\n\n      /**\n       * link force\n       * https://github.com/d3/d3-force/#many-body\n       *\n       * @default true\n       */\n      manyBody: boolean | IManyBodyForce;\n\n      /**\n       * x positioning force\n       * https://github.com/d3/d3-force/#forceX\n       *\n       * @default false\n       */\n      x: boolean | IForceXForce;\n\n      /**\n       * y positioning force\n       * https://github.com/d3/d3-force/#forceY\n       *\n       * @default false\n       */\n      y: boolean | IForceYForce;\n\n      /**\n       * radial positioning force\n       * https://github.com/d3/d3-force/#forceRadial\n       *\n       * @default false\n       */\n      radial: boolean | IRadialForce;\n    };\n  };\n}\n\nexport declare type ID3NodeCallback = (d: any, i: number) => number;\nexport declare type ID3EdgeCallback = (d: any, i: number) => number;\n\nexport interface ICenterForce {\n  x?: number;\n  y?: number;\n}\n\nexport interface ICollideForce {\n  radius?: number | ID3NodeCallback;\n  strength?: number | ID3NodeCallback;\n}\n\nexport interface ILinkForce {\n  id?: (d: { source: any; target: any }) => string | number;\n  distance?: number | ID3EdgeCallback;\n  strength?: number | ID3EdgeCallback;\n}\n\nexport interface IManyBodyForce {\n  strength?: number | ID3NodeCallback;\n  theta?: number;\n  distanceMin?: number;\n  distanceMax?: number;\n}\n\nexport interface IForceXForce {\n  x?: number;\n  strength?: number;\n}\n\nexport interface IForceYForce {\n  y?: number;\n  strength?: number;\n}\n\nexport interface IRadialForce {\n  x?: number;\n  y?: number;\n  radius?: number;\n  strength?: number;\n}\n\nexport class ForceDirectedGraphController extends GraphController {\n  /**\n   * @hidden\n   */\n  declare options: IForceDirectedControllerOptions;\n\n  /**\n   * @hidden\n   */\n  private readonly _simulation: Simulation<SimulationNodeDatum, undefined>;\n\n  private _animTimer: number = -1;\n\n  constructor(chart: Chart, datasetIndex: number) {\n    super(chart, datasetIndex);\n    this._simulation = forceSimulation()\n      .on('tick', () => {\n        if (this.chart.canvas && this._animTimer !== -2) {\n          this._copyPosition();\n          this.chart.render();\n        } else {\n          this._simulation.stop();\n        }\n      })\n      .on('end', () => {\n        if (this.chart.canvas && this._animTimer !== -2) {\n          this._copyPosition();\n          this.chart.render();\n          // trigger a full update\n          this.chart.update('default');\n        }\n      });\n    const sim = this.options.simulation;\n\n    const fs = {\n      center: forceCenter,\n      collide: forceCollide,\n      link: forceLink,\n      manyBody: forceManyBody,\n      x: forceX,\n      y: forceY,\n      radial: forceRadial,\n    };\n\n    (Object.keys(fs) as (keyof typeof fs)[]).forEach((key) => {\n      const options = sim.forces[key] as any;\n      if (!options) {\n        return;\n      }\n      const f = (fs[key] as any)();\n      if (typeof options !== 'boolean') {\n        Object.keys(options).forEach((attr) => {\n          f[attr](options[attr]);\n        });\n      }\n      this._simulation.force(key, f);\n    });\n    this._simulation.stop();\n  }\n\n  _destroy() {\n    if (this._animTimer >= 0) {\n      cancelAnimationFrame(this._animTimer);\n    }\n    this._animTimer = -2;\n    return super._destroy();\n  }\n\n  /**\n   * @hidden\n   */\n  _copyPosition(): void {\n    const nodes = this._cachedMeta._parsed as ITreeSimNode[];\n\n    const minmax = nodes.reduce(\n      (acc, v) => {\n        const s = v._sim;\n        if (!s || s.x == null || s.y == null) {\n          return acc;\n        }\n        if (s.x < acc.minX) {\n          acc.minX = s.x;\n        }\n        if (s.x > acc.maxX) {\n          acc.maxX = s.x;\n        }\n        if (s.y < acc.minY) {\n          acc.minY = s.y;\n        }\n        if (s.y > acc.maxY) {\n          acc.maxY = s.y;\n        }\n        return acc;\n      },\n      {\n        minX: Number.POSITIVE_INFINITY,\n        maxX: Number.NEGATIVE_INFINITY,\n        minY: Number.POSITIVE_INFINITY,\n        maxY: Number.NEGATIVE_INFINITY,\n      }\n    );\n\n    const rescaleX = (v: number) => ((v - minmax.minX) / (minmax.maxX - minmax.minX)) * 2 - 1;\n    const rescaleY = (v: number) => ((v - minmax.minY) / (minmax.maxY - minmax.minY)) * 2 - 1;\n\n    nodes.forEach((node) => {\n      if (node._sim) {\n        node.x = rescaleX(node._sim.x ?? 0);\n\n        node.y = rescaleY(node._sim.y ?? 0);\n      }\n    });\n\n    const { xScale, yScale } = this._cachedMeta;\n    const elems = this._cachedMeta.data;\n    elems.forEach((elem, i) => {\n      const parsed = nodes[i];\n      Object.assign(elem, {\n        x: xScale?.getPixelForValue(parsed.x, i) ?? 0,\n        y: yScale?.getPixelForValue(parsed.y, i) ?? 0,\n        skip: false,\n      });\n    });\n  }\n\n  resetLayout(): void {\n    super.resetLayout();\n    this._simulation.stop();\n\n    const nodes = (this._cachedMeta._parsed as ITreeSimNode[]).map((node, i) => {\n      const simNode: ITreeSimNode['_sim'] = { ...node };\n      simNode.index = i;\n\n      node._sim = simNode;\n      if (!node.reset) {\n        return simNode;\n      }\n      delete simNode.x;\n      delete simNode.y;\n      delete simNode.vx;\n      delete simNode.vy;\n      return simNode;\n    });\n    this._simulation.nodes(nodes);\n    this._simulation.alpha(1).restart();\n  }\n\n  resyncLayout(): void {\n    super.resyncLayout();\n    this._simulation.stop();\n\n    const meta = this._cachedMeta;\n\n    const nodes = (meta._parsed as ITreeSimNode[]).map((node, i) => {\n      const simNode: ITreeSimNode['_sim'] = { ...node };\n      simNode.index = i;\n\n      node._sim = simNode;\n      if (simNode.x === null) {\n        delete simNode.x;\n      }\n      if (simNode.y === null) {\n        delete simNode.y;\n      }\n      if (simNode.x == null && simNode.y == null) {\n        node.reset = true;\n      }\n      return simNode;\n    });\n    const link =\n      this._simulation.force<ForceLink<SimulationNodeDatum, SimulationLinkDatum<SimulationNodeDatum>>>('link');\n    if (link) {\n      link.links([]);\n    }\n    this._simulation.nodes(nodes);\n    if (link) {\n      // console.assert(ds.edges.length === meta.edges.length);\n      // work on copy to avoid change\n      link.links(((meta as unknown as IExtendedChartMeta)._parsedEdges || []).map((l) => ({ ...l })));\n    }\n\n    if (this.options.simulation.initialIterations > 0) {\n      this._simulation.alpha(1);\n      this._simulation.tick(this.options.simulation.initialIterations);\n      this._copyPosition();\n      if (this.options.simulation.autoRestart) {\n        this._simulation.restart();\n      } else if (this.chart.canvas != null && this._animTimer !== -2) {\n        const chart = this.chart;\n        this._animTimer = requestAnimationFrame(() => {\n          if (chart.canvas) {\n            chart.update();\n          }\n        });\n      }\n    } else if (this.options.simulation.autoRestart && this.chart.canvas != null && this._animTimer !== -2) {\n      this._simulation.alpha(1).restart();\n    }\n  }\n\n  reLayout(): void {\n    this._simulation.alpha(1).restart();\n  }\n\n  stopLayout(): void {\n    super.stopLayout();\n    this._simulation.stop();\n  }\n\n  static readonly id = 'forceDirectedGraph';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    GraphController.defaults,\n    {\n      animation: false,\n      simulation: {\n        initialIterations: 0,\n        autoRestart: true,\n        forces: {\n          center: true,\n          collide: false,\n          link: true,\n          manyBody: true,\n          x: false,\n          y: false,\n          radial: false,\n        },\n      },\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ merge({}, [\n    GraphController.overrides,\n    {\n      scales: {\n        x: {\n          min: -1,\n          max: 1,\n        },\n        y: {\n          min: -1,\n          max: 1,\n        },\n      },\n    },\n  ]);\n}\n\nexport interface IForceDirectedGraphChartControllerDatasetOptions\n  extends IGraphChartControllerDatasetOptions,\n    IForceDirectedControllerOptions {}\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    forceDirectedGraph: {\n      chartOptions: CoreChartOptions<'forceDirectedGraph'> & IForceDirectedControllerOptions;\n      datasetOptions: IForceDirectedGraphChartControllerDatasetOptions;\n      defaultDataPoint: IGraphDataPoint & Record<string, unknown>;\n      metaExtensions: Record<string, never>;\n      parsedDataType: ITreeSimNode;\n      scales: keyof CartesianScaleTypeRegistry;\n    };\n  }\n}\n\nexport class ForceDirectedGraphChart<DATA extends unknown[] = IGraphDataPoint[], LABEL = string> extends Chart<\n  'forceDirectedGraph',\n  DATA,\n  LABEL\n> {\n  static id = ForceDirectedGraphController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'forceDirectedGraph', DATA, LABEL>, 'type'>) {\n    super(\n      item,\n      patchController('forceDirectedGraph', config, ForceDirectedGraphController, [EdgeLine, PointElement], LinearScale)\n    );\n  }\n}\n","import {\n  Chart,\n  ChartItem,\n  ChartConfiguration,\n  LinearScale,\n  PointElement,\n  UpdateMode,\n  Element,\n  CartesianScaleTypeRegistry,\n  CoreChartOptions,\n} from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport { cluster, hierarchy, HierarchyNode, tree } from 'd3-hierarchy';\nimport { EdgeLine } from '../elements';\nimport {\n  GraphController,\n  IGraphChartControllerDatasetOptions,\n  IGraphDataPoint,\n  ITreeNode,\n  AnyObject,\n} from './GraphController';\nimport patchController from './patchController';\n\nexport interface ITreeOptions {\n  /**\n   * tree (cluster) or dendrogram layout default depends on the chart type\n   */\n  mode: 'dendrogram' | 'tree' | 'dendrogram';\n  /**\n   * orientation of the tree layout\n   * @default horizontal\n   */\n  orientation: 'horizontal' | 'vertical' | 'radial';\n}\n\nexport class DendrogramController extends GraphController {\n  /**\n   * @hidden\n   */\n  declare options: { tree: ITreeOptions };\n\n  private _animTimer: number = -1;\n\n  /**\n   * @hidden\n   */\n\n  updateEdgeElement(line: EdgeLine, index: number, properties: any, mode: UpdateMode): void {\n    properties._orientation = this.options.tree.orientation;\n    super.updateEdgeElement(line, index, properties, mode);\n  }\n\n  _destroy() {\n    if (this._animTimer >= 0) {\n      cancelAnimationFrame(this._animTimer);\n    }\n    this._animTimer = -2;\n    return super._destroy();\n  }\n\n  /**\n   * @hidden\n   */\n\n  updateElement(point: Element<AnyObject, AnyObject>, index: number, properties: any, mode: UpdateMode): void {\n    if (index != null) {\n      properties.angle = (this.getParsed(index) as { angle: number }).angle;\n    }\n    super.updateElement(point, index, properties, mode);\n  }\n\n  resyncLayout(): void {\n    const meta = this._cachedMeta as any;\n\n    meta.root = hierarchy(this.getTreeRoot(), (d) => this.getTreeChildren(d))\n      .count()\n      .sort((a, b) => b.height - a.height || (b.data.index ?? 0) - (a.data.index ?? 0));\n\n    this.doLayout(meta.root);\n\n    super.resyncLayout();\n  }\n\n  reLayout(newOptions: Partial<ITreeOptions> = {}): void {\n    if (newOptions) {\n      Object.assign(this.options.tree, newOptions);\n      const ds = this.getDataset() as any;\n      if (ds.tree) {\n        Object.assign(ds.tree, newOptions);\n      } else {\n        ds.tree = newOptions;\n      }\n    }\n    this.doLayout((this._cachedMeta as any).root);\n  }\n\n  doLayout(root: HierarchyNode<{ x: number; y: number; angle?: number }>): void {\n    const options = this.options.tree;\n\n    const layout =\n      options.mode === 'tree'\n        ? tree<{ x: number; y: number; angle?: number }>()\n        : cluster<{ x: number; y: number; angle?: number }>();\n\n    if (options.orientation === 'radial') {\n      layout.size([Math.PI * 2, 1]);\n    } else {\n      layout.size([2, 2]);\n    }\n\n    const orientation = {\n      horizontal: (d: { x: number; y: number; data: { x: number; y: number } }) => {\n        d.data.x = d.y - 1;\n\n        d.data.y = -d.x + 1;\n      },\n      vertical: (d: { x: number; y: number; data: { x: number; y: number } }) => {\n        d.data.x = d.x - 1;\n\n        d.data.y = -d.y + 1;\n      },\n      radial: (d: { x: number; y: number; data: { x: number; y: number; angle?: number } }) => {\n        d.data.x = Math.cos(d.x) * d.y;\n\n        d.data.y = Math.sin(d.x) * d.y;\n\n        d.data.angle = d.y === 0 ? Number.NaN : d.x;\n      },\n    };\n\n    layout(root).each((orientation[options.orientation] || orientation.horizontal) as any);\n\n    const chart = this.chart;\n    if (this._animTimer !== -2) {\n      this._animTimer = requestAnimationFrame(() => {\n        if (chart.canvas) {\n          chart.update();\n        }\n      });\n    }\n  }\n\n  static readonly id: string = 'dendrogram';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    GraphController.defaults,\n    {\n      tree: {\n        mode: 'dendrogram', // dendrogram, tree\n        orientation: 'horizontal', // vertical, horizontal, radial\n      },\n      animations: {\n        numbers: {\n          type: 'number',\n          properties: ['x', 'y', 'angle', 'radius', 'rotation', 'borderWidth'],\n        },\n      },\n      tension: 0.4,\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ merge({}, [\n    GraphController.overrides,\n    {\n      scales: {\n        x: {\n          min: -1,\n          max: 1,\n        },\n        y: {\n          min: -1,\n          max: 1,\n        },\n      },\n    },\n  ]);\n}\n\nexport interface IDendrogramChartControllerDatasetOptions extends IGraphChartControllerDatasetOptions {\n  tree: ITreeOptions;\n}\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    dendogram: {\n      chartOptions: CoreChartOptions<'dendrogram'> & { tree: ITreeOptions };\n      datasetOptions: IDendrogramChartControllerDatasetOptions;\n      defaultDataPoint: IGraphDataPoint & Record<string, unknown>;\n      metaExtensions: Record<string, never>;\n      parsedDataType: ITreeNode & { angle?: number };\n      scales: keyof CartesianScaleTypeRegistry;\n    };\n    dendrogram: {\n      chartOptions: CoreChartOptions<'dendrogram'> & { tree: ITreeOptions };\n      datasetOptions: IDendrogramChartControllerDatasetOptions;\n      defaultDataPoint: IGraphDataPoint & Record<string, unknown>;\n      metaExtensions: Record<string, never>;\n      parsedDataType: ITreeNode & { angle?: number };\n      scales: keyof CartesianScaleTypeRegistry;\n    };\n  }\n}\n\nexport class DendrogramChart<DATA extends unknown[] = IGraphDataPoint[], LABEL = string> extends Chart<\n  'dendrogram',\n  DATA,\n  LABEL\n> {\n  static id = DendrogramController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'dendrogram', DATA, LABEL>, 'type'>) {\n    super(item, patchController('dendrogram', config, DendrogramController, [EdgeLine, PointElement], LinearScale));\n  }\n}\n\nexport class DendogramController extends DendrogramController {\n  static readonly id: string = 'dendogram';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    DendrogramController.defaults,\n    {\n      tree: {\n        mode: 'dendrogram', // dendrogram, tree\n      },\n    },\n  ]);\n}\n\nexport const DendogramChart = DendrogramChart;\n","import {\n  CartesianScaleTypeRegistry,\n  Chart,\n  ChartConfiguration,\n  ChartItem,\n  CoreChartOptions,\n  LinearScale,\n  PointElement,\n} from 'chart.js';\nimport { merge } from 'chart.js/helpers';\nimport { EdgeLine } from '../elements';\nimport { DendrogramController, IDendrogramChartControllerDatasetOptions, ITreeOptions } from './DendrogramController';\nimport type { IGraphDataPoint, ITreeNode } from './GraphController';\nimport patchController from './patchController';\n\nexport class TreeController extends DendrogramController {\n  static readonly id = 'tree';\n\n  /**\n   * @hidden\n   */\n  static readonly defaults: any = /* #__PURE__ */ merge({}, [\n    DendrogramController.defaults,\n    {\n      tree: {\n        mode: 'tree',\n      },\n    },\n  ]);\n\n  /**\n   * @hidden\n   */\n  static readonly overrides: any = /* #__PURE__ */ DendrogramController.overrides;\n}\n\ndeclare module 'chart.js' {\n  export interface ChartTypeRegistry {\n    tree: {\n      chartOptions: CoreChartOptions<'tree'> & { tree: ITreeOptions };\n      datasetOptions: IDendrogramChartControllerDatasetOptions;\n      defaultDataPoint: IGraphDataPoint & Record<string, unknown>;\n      metaExtensions: Record<string, never>;\n      parsedDataType: ITreeNode;\n      scales: keyof CartesianScaleTypeRegistry;\n    };\n  }\n}\n\nexport class TreeChart<DATA extends unknown[] = IGraphDataPoint[], LABEL = string> extends Chart<'tree', DATA, LABEL> {\n  static id = TreeController.id;\n\n  constructor(item: ChartItem, config: Omit<ChartConfiguration<'tree', DATA, LABEL>, 'type'>) {\n    super(item, patchController('tree', config, TreeController, [EdgeLine, PointElement], LinearScale));\n  }\n}\n"],"names":[],"mappings":";;;;;;;;;;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9BO;AACA;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;AC5FO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACA;AACA;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;AC7FO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;;AC/DA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;;"}