{"version":3,"file":"index.d.ts","sources":["../src/layers/public.ts","../src/layers/interfaces.ts","../src/elements/utils.ts","../src/elements/common.ts","../src/elements/edges.ts","../src/elements/nodes.ts","../src/LayersPlugin.ts","../src/index.ts"],"sourcesContent":["import type cy from 'cytoscape';\n\nexport interface IPoint {\n  x: number;\n  y: number;\n}\n\nexport interface IRenderHint {\n  forExport?: boolean;\n}\n\nexport interface IRenderFunction {\n  (ctx: CanvasRenderingContext2D, hint: IRenderHint): void;\n}\n\nexport interface IDOMUpdateFunction<T extends HTMLElement | SVGElement> {\n  (node: T): void;\n}\n\nexport interface ILayerOptions {\n  stopClicks?: boolean;\n}\n\nexport type ISVGLayerOptions = ILayerOptions;\nexport type IHTMLLayerOptions = ILayerOptions;\nexport interface ICanvasLayerOptions extends ILayerOptions, Partial<CanvasRenderingContext2DSettings> {\n  pixelRatio?: number;\n}\n\nexport interface ILayerDescFunction {\n  (type: 'svg', options?: ISVGLayerOptions): ISVGLayer;\n  (type: 'svg-static', options?: ISVGLayerOptions): ISVGStaticLayer;\n  (type: 'canvas', options?: ICanvasLayerOptions): ICanvasLayer;\n  (type: 'canvas-static', options?: ICanvasLayerOptions): ICanvasStaticLayer;\n  (type: 'html', options?: IHTMLLayerOptions): IHTMLLayer;\n  (type: 'html-static', options?: IHTMLLayerOptions): IHTMLStaticLayer;\n}\n\nexport interface IMoveAbleLayer {\n  readonly cy: cy.Core;\n\n  moveUp(): void;\n  moveDown(): void;\n  moveBack(): void;\n  moveFront(): void;\n\n  insertBefore: ILayerDescFunction;\n  insertAfter: ILayerDescFunction;\n}\n\nexport interface ICustomLayer extends IMoveAbleLayer {\n  remove(): void;\n  hide(): void;\n  show(): void;\n  visible: boolean;\n\n  updateOnRender: boolean;\n  update(): void;\n  updateOnRenderOnce(): void;\n}\n\nexport interface ITransformedLayer extends ICustomLayer {\n  /**\n   * checks whether the given point in model coordinates is visible i.e., within the visible rendered bounds\n   * @param point\n   */\n  inVisibleBounds(point: IPoint | cy.BoundingBox12): boolean;\n}\n\nexport interface ISVGLayer extends ITransformedLayer {\n  readonly type: 'svg';\n  readonly node: SVGElement;\n  updateOnTransform: boolean;\n\n  readonly callbacks: IDOMUpdateFunction<SVGElement>[];\n  callback(callback: IDOMUpdateFunction<SVGElement>): ISVGLayer;\n}\n\nexport interface ISVGStaticLayer extends ICustomLayer {\n  readonly type: 'svg-static';\n  readonly node: SVGElement;\n\n  readonly callbacks: IDOMUpdateFunction<SVGElement>[];\n  callback(callback: IDOMUpdateFunction<SVGElement>): ISVGStaticLayer;\n}\nexport interface ICanvasLayer extends ITransformedLayer {\n  readonly type: 'canvas';\n\n  readonly callbacks: IRenderFunction[];\n  callback(callback: IRenderFunction): ICanvasLayer;\n}\n\nexport interface ICanvasStaticLayer extends ICustomLayer {\n  readonly type: 'canvas-static';\n\n  readonly callbacks: IRenderFunction[];\n  callback(callback: IRenderFunction): ICanvasStaticLayer;\n}\n\nexport interface IHTMLLayer extends ITransformedLayer {\n  readonly type: 'html';\n  readonly node: HTMLElement;\n  updateOnTransform: boolean;\n\n  readonly callbacks: IDOMUpdateFunction<HTMLElement>[];\n  callback(callback: IDOMUpdateFunction<HTMLElement>): IHTMLLayer;\n}\n\nexport interface IHTMLStaticLayer extends ICustomLayer {\n  readonly type: 'html-static';\n  readonly node: HTMLElement;\n\n  readonly callbacks: IDOMUpdateFunction<HTMLElement>[];\n  callback(callback: IDOMUpdateFunction<HTMLElement>): IHTMLStaticLayer;\n}\n\nexport interface ICytoscapeNodeLayer extends IMoveAbleLayer {\n  readonly node: HTMLCanvasElement;\n  readonly type: 'node';\n}\nexport interface ICytoscapeDragLayer extends IMoveAbleLayer {\n  readonly node: HTMLCanvasElement;\n  readonly type: 'drag';\n}\nexport interface ICytoscapeSelectBoxLayer extends IMoveAbleLayer {\n  readonly node: HTMLCanvasElement;\n  readonly type: 'select-box';\n}\n\nexport type ILayer =\n  | ICytoscapeNodeLayer\n  | ICytoscapeDragLayer\n  | ICytoscapeSelectBoxLayer\n  | IHTMLLayer\n  | IHTMLStaticLayer\n  | ISVGLayer\n  | ISVGStaticLayer\n  | ICanvasLayer\n  | ICanvasStaticLayer;\n","import type { ILayer } from './public';\n\nexport * from './public';\n\nexport interface ILayerElement {\n  __cy_layer: ILayer & ILayerImpl;\n}\n\nexport interface ILayerImpl {\n  readonly root: HTMLElement | SVGElement;\n  resize(width: number, height: number): void;\n  remove(): void;\n  setViewport(tx: number, ty: number, zoom: number): void;\n\n  supportsRender(): boolean;\n  renderInto(ctx: CanvasRenderingContext2D): void;\n}\n","import type cy from 'cytoscape';\n\nexport interface IMatchOptions<T extends HTMLElement | SVGElement> {\n  bb: (node: cy.NodeSingular) => cy.BoundingBox12 & cy.BoundingBoxWH;\n  isVisible: (bb: cy.BoundingBox12 & cy.BoundingBoxWH) => boolean;\n  enter: (node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => T;\n  update: (elem: T, node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => void;\n  uniqueElements: boolean;\n}\n\nexport function matchNodes<T extends HTMLElement | SVGElement>(\n  root: T,\n  nodes: cy.NodeCollection,\n  options: IMatchOptions<T>\n) {\n  const arr = Array.from(root.children) as T[];\n  if (!options.uniqueElements) {\n    nodes.forEach((node) => {\n      if (node.removed()) {\n        return;\n      }\n      const bb = options.bb(node);\n      if (!options.isVisible(bb)) {\n        return;\n      }\n      if (arr.length > 0) {\n        options.update(arr.shift()!, node, bb);\n      } else {\n        const elem = options.enter(node, bb);\n        root.appendChild(elem);\n        options.update(elem, node, bb);\n      }\n    });\n    for (const rest of arr) {\n      rest.remove();\n    }\n    return;\n  }\n  // match\n  const map = new Map(arr.map((d) => [d.dataset.id!, d] as [string, T]));\n\n  let i = -1;\n  nodes.forEach((node) => {\n    if (node.removed()) {\n      return;\n    }\n    const bb = options.bb(node);\n    if (!options.isVisible(bb)) {\n      return;\n    }\n    i++;\n    const id = node.id();\n    const expected = arr[i];\n    const has = map.get(id);\n    let n: T;\n    if (has) {\n      options.update(has, node, bb);\n      map.delete(id);\n      if (expected === has) {\n        // match 1:1\n        return;\n      }\n      n = has;\n    } else {\n      // need a new one\n      n = options.enter(node, bb);\n      n.dataset.id = id;\n      options.update(n, node, bb);\n    }\n\n    if (i === 0) {\n      root.insertAdjacentElement('afterbegin', n);\n    } else {\n      arr[i - 1].insertAdjacentElement('afterend', n);\n    }\n    arr.splice(i, 0, n);\n  });\n  // delete rest\n  map.forEach((n) => n.remove());\n}\n\nexport interface ICallbackRemover {\n  remove(): void;\n}\n\nexport function registerCallback<U, T extends { callback(c: U): void; callbacks: U[] }>(\n  layer: T,\n  renderer: U\n): ICallbackRemover {\n  layer.callback(renderer);\n  return {\n    remove: () => {\n      layer.callbacks.splice(layer.callbacks.indexOf(renderer), 1);\n    },\n  };\n}\n","export interface IElementLayerOptions {\n  /**\n   * selector to determine elements to render\n   * @default :visible\n   */\n  selector: string;\n  /**\n   * whether to update the collection on each update\n   */\n  queryEachTime: boolean;\n  /**\n   * automatically update the layer upon a certain event\n   * render ... upon render\n   * none ... only when add/remove\n   * position or custom ... extra events to listen to\n   * @default auto = render in case of a queryEachTime else position\n   */\n  updateOn: 'render' | 'position' | 'none' | string;\n\n  /**\n   * whether to check that the element is actually visible\n   * @default true\n   */\n  checkBounds: boolean;\n}\n\nexport function defaultElementLayerOptions(o?: Partial<IElementLayerOptions>): IElementLayerOptions {\n  return {\n    selector: ':visible',\n    updateOn: o != null && o.queryEachTime ? 'render' : 'position',\n    queryEachTime: false,\n    checkBounds: true,\n  };\n}\n","import type cy from 'cytoscape';\nimport type { ICanvasLayer, IPoint, IRenderHint } from '../layers';\nimport { ICallbackRemover, registerCallback } from './utils';\nimport { IElementLayerOptions, defaultElementLayerOptions } from './common';\n\nexport interface IRenderPerEdgeResult extends ICallbackRemover {\n  layer: ICanvasLayer;\n  edges: cy.EdgeCollection;\n}\n\nexport interface IEdgeLayerOptions extends IElementLayerOptions {\n  checkBoundsPointCount: number;\n  /**\n   * init function for the collection\n   * @param edges\n   */\n  initCollection(edges: cy.EdgeCollection): void;\n}\n\nexport function renderPerEdge(\n  layer: ICanvasLayer,\n  render: (ctx: CanvasRenderingContext2D, edge: cy.EdgeSingular, path: Path2D, start: IPoint, end: IPoint) => void,\n  options?: Partial<IEdgeLayerOptions>\n): IRenderPerEdgeResult {\n  const o: IEdgeLayerOptions = Object.assign(\n    {\n      checkBoundsPointCount: 5,\n      initCollection: () => undefined,\n    },\n    defaultElementLayerOptions(options),\n    options\n  );\n  let edges = layer.cy.collection() as cy.EdgeCollection;\n  let updateOnRenderOnceEnabled = false;\n  const revaluateAndUpdateOnce = () => {\n    if (updateOnRenderOnceEnabled) {\n      return;\n    }\n    updateOnRenderOnceEnabled = true;\n    layer.cy.one('render', () => {\n      updateOnRenderOnceEnabled = false;\n      edges = reevaluateCollection(edges);\n      layer.update();\n    });\n  };\n\n  const reevaluateCollection = (current: cy.EdgeCollection) => {\n    // clean up old\n    if (o.updateOn !== 'none' && o.updateOn !== 'render') {\n      current.off(o.updateOn, undefined, layer.updateOnRenderOnce);\n      current.sources().off(o.updateOn, undefined, layer.updateOnRenderOnce);\n      current.targets().off(o.updateOn, undefined, layer.updateOnRenderOnce);\n    }\n\n    // init new\n    const newEdges = layer.cy.edges(o.selector);\n    o.initCollection(newEdges);\n    if (o.updateOn !== 'none' && o.updateOn !== 'render') {\n      newEdges.on(o.updateOn, layer.updateOnRenderOnce);\n      newEdges.sources().on(o.updateOn, layer.updateOnRenderOnce);\n      newEdges.targets().on(o.updateOn, layer.updateOnRenderOnce);\n    }\n    layer.updateOnRenderOnce();\n    return newEdges;\n  };\n\n  if (o.updateOn === 'render') {\n    layer.updateOnRender = true;\n  }\n  if (!o.queryEachTime) {\n    edges = reevaluateCollection(edges);\n    layer.cy.on('add remove', o.selector, revaluateAndUpdateOnce);\n  }\n\n  const renderer = (ctx: CanvasRenderingContext2D, hint: IRenderHint) => {\n    if (o.queryEachTime) {\n      edges = reevaluateCollection(edges);\n    }\n    edges.forEach((edge) => {\n      if (edge.removed()) {\n        return;\n      }\n      const impl = (edge as any)._private.rscratch as {\n        pathCache: Path2D;\n        startX?: number;\n        startY?: number;\n        endX?: number;\n        endY?: number;\n      };\n      const s =\n        impl && impl.startX != null && impl.startY != null ? { x: impl.startX, y: impl.startY } : edge.sourceEndpoint();\n      const t = impl && impl.endX != null && impl.endY != null ? { x: impl.endX, y: impl.endY } : edge.targetEndpoint();\n\n      if (\n        !hint.forExport &&\n        o.checkBounds &&\n        o.checkBoundsPointCount > 0 &&\n        !anyVisible(layer, s, t, o.checkBoundsPointCount)\n      ) {\n        return;\n      }\n      if (impl && impl.pathCache) {\n        render(ctx, edge, impl.pathCache, s, t);\n        return;\n      }\n      const path = new Path2D();\n      path.moveTo(s.x, s.y);\n      path.lineTo(t.x, t.y);\n      render(ctx, edge, path, s, t);\n    });\n  };\n\n  const r = registerCallback(layer, renderer);\n  return {\n    layer,\n    edges,\n    remove: () => {\n      if (o.updateOn !== 'none' && o.updateOn !== 'render') {\n        edges.off(o.updateOn, undefined, layer.updateOnRenderOnce);\n        edges.sources().off(o.updateOn, undefined, layer.updateOnRenderOnce);\n        edges.targets().off(o.updateOn, undefined, layer.updateOnRenderOnce);\n      }\n      layer.cy.off('add remove', o.selector, revaluateAndUpdateOnce);\n      r.remove();\n    },\n  };\n}\n\nfunction anyVisible(layer: ICanvasLayer, s: IPoint, t: IPoint, count: number) {\n  const interpolate = (v: number) => ({\n    x: s.x * v + t.x * (1 - v),\n    y: s.y * v + t.y * (1 - v),\n  });\n  if (count === 1) {\n    return layer.inVisibleBounds(interpolate(0.5));\n  }\n  const step = 1 / count;\n  for (let i = 0; i <= count; i++) {\n    if (layer.inVisibleBounds(interpolate(i * step))) {\n      return true;\n    }\n  }\n  return false;\n}\n","import type cy from 'cytoscape';\nimport type { ICanvasLayer, IHTMLLayer, ISVGLayer, ILayer, IRenderHint } from '../layers';\nimport { SVG_NS } from '../layers/SVGLayer';\nimport { matchNodes, registerCallback, ICallbackRemover, IMatchOptions } from './utils';\nimport { IElementLayerOptions, defaultElementLayerOptions } from './common';\n\nexport interface INodeLayerOption extends IElementLayerOptions {\n  /**\n   * how to compute the bounding box\n   */\n  boundingBox: cy.BoundingBoxOptions;\n  /**\n   * where to position the canvas / node relative to a node\n   */\n  position: 'none' | 'top-left' | 'center';\n  /**\n   * init function for the collection\n   * @param nodes\n   */\n  initCollection(nodes: cy.NodeCollection): void;\n}\n\nexport interface INodeDOMLayerOption<T extends HTMLElement | SVGElement> extends INodeLayerOption {\n  /**\n   * whether to use unique DOM elements per node (id), similar to D3 key argument\n   * @default false\n   */\n  uniqueElements: boolean;\n  /**\n   * init function for newly created DOM elements\n   * @param elem\n   * @param node\n   */\n  init(elem: T, node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH): void;\n\n  /**\n   * additional transform to apply to a node\n   */\n  transform?: string;\n}\n\nexport interface INodeCanvasLayerOption extends INodeLayerOption {\n  /**\n   * init function for newly added node\n   * @param elem\n   * @param node\n   */\n  init(node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH): void;\n}\n\nexport interface IRenderPerNodeResult extends ICallbackRemover {\n  layer: ILayer;\n  nodes: cy.NodeCollection;\n}\n\nexport function renderPerNode(\n  layer: ICanvasLayer,\n  render: (ctx: CanvasRenderingContext2D, node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => void,\n  options?: Partial<INodeCanvasLayerOption>\n): IRenderPerNodeResult;\nexport function renderPerNode(\n  layer: IHTMLLayer,\n  render: (elem: HTMLElement, node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => void,\n  options?: Partial<INodeDOMLayerOption<HTMLElement>>\n): IRenderPerNodeResult;\nexport function renderPerNode(\n  layer: ISVGLayer,\n  render: (elem: SVGElement, node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => void,\n  options?: Partial<INodeDOMLayerOption<SVGElement>>\n): IRenderPerNodeResult;\nexport function renderPerNode(\n  layer: ICanvasLayer | IHTMLLayer | ISVGLayer,\n  render: (ctx: any, node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => void,\n  options: Partial<INodeDOMLayerOption<any> | INodeCanvasLayerOption> = {}\n): IRenderPerNodeResult {\n  const o = Object.assign(\n    {\n      transform: '',\n      position: 'top-left',\n      boundingBox: {\n        includeLabels: false,\n        includeOverlays: false,\n      },\n      uniqueElements: false,\n      initCollection: () => undefined,\n    },\n    defaultElementLayerOptions(options),\n    options\n  );\n\n  let nodes = layer.cy.collection() as cy.NodeCollection;\n  let updateOnRenderOnceEnabled = false;\n  const revaluateAndUpdateOnce = () => {\n    if (updateOnRenderOnceEnabled) {\n      return;\n    }\n    updateOnRenderOnceEnabled = true;\n    layer.cy.one('render', () => {\n      updateOnRenderOnceEnabled = false;\n      nodes = reevaluateCollection(nodes);\n      layer.update();\n    });\n  };\n\n  const reevaluateCollection = (current: cy.NodeCollection) => {\n    // clean up old\n    if (o.updateOn !== 'none' && o.updateOn !== 'render') {\n      current.off(o.updateOn, undefined, layer.updateOnRenderOnce);\n    }\n\n    // init new\n    const newNodes = layer.cy.nodes(o.selector);\n    o.initCollection(newNodes);\n    if (o.updateOn !== 'none' && o.updateOn !== 'render') {\n      newNodes.on(o.updateOn, layer.updateOnRenderOnce);\n    }\n    layer.updateOnRenderOnce();\n    return newNodes;\n  };\n\n  if (o.updateOn === 'render') {\n    layer.updateOnRender = true;\n  }\n  if (!o.queryEachTime) {\n    nodes = reevaluateCollection(nodes);\n    layer.cy.on('add remove', o.selector, revaluateAndUpdateOnce);\n  }\n\n  const wrapResult = (v: ICallbackRemover): IRenderPerNodeResult => ({\n    layer,\n    nodes,\n    remove: () => {\n      if (o.updateOn !== 'none' && o.updateOn !== 'render') {\n        nodes.off(o.updateOn, undefined, layer.updateOnRenderOnce);\n      }\n      layer.cy.off('add remove', o.selector, revaluateAndUpdateOnce);\n      v.remove();\n    },\n  });\n\n  if (layer.type === 'canvas') {\n    const oCanvas = o as INodeCanvasLayerOption;\n    const renderer = (ctx: CanvasRenderingContext2D, hint: IRenderHint) => {\n      const t = ctx.getTransform();\n      if (o.queryEachTime) {\n        nodes = reevaluateCollection(nodes);\n      }\n      nodes.forEach((node) => {\n        if (node.removed()) {\n          return;\n        }\n        const bb = node.boundingBox(o.boundingBox);\n        if (!hint.forExport && oCanvas.checkBounds && !layer.inVisibleBounds(bb)) {\n          return;\n        }\n        if (oCanvas.position === 'top-left') {\n          ctx.translate(bb.x1, bb.y1);\n        } else if (oCanvas.position === 'center') {\n          const pos = node.position();\n          ctx.translate(pos.x, pos.y);\n        }\n        render(ctx, node, bb);\n        if (oCanvas.position !== 'none') {\n          ctx.setTransform(t);\n        }\n      });\n    };\n    return wrapResult(registerCallback(layer, renderer));\n  }\n\n  const oDOM = o as INodeDOMLayerOption<any>;\n  // HTML or SVG\n  const baseOptions = {\n    bb: (node: cy.NodeSingular) => node.boundingBox(oDOM.boundingBox),\n    isVisible: oDOM.checkBounds ? (bb: cy.BoundingBox12 & cy.BoundingBoxWH) => layer.inVisibleBounds(bb) : () => true,\n    uniqueElements: oDOM.uniqueElements === true,\n  };\n  if (oDOM.checkBounds) {\n    layer.updateOnTransform = true;\n  }\n\n  if (layer.type === 'html') {\n    const matchOptions: IMatchOptions<HTMLElement> = {\n      ...baseOptions,\n      enter: (node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => {\n        const r = layer.node.ownerDocument.createElement('div');\n        r.style.position = 'absolute';\n        if (oDOM.init) {\n          oDOM.init(r, node, bb);\n        }\n        return r;\n      },\n      update: (elem, node, bb) => {\n        if (oDOM.position === 'top-left') {\n          elem.style.transform = `${oDOM.transform}translate3d(${bb.x1}px,${bb.y1}px,0)`;\n        } else if (oDOM.position === 'center') {\n          const pos = node.position();\n          elem.style.transform = `${oDOM.transform}translate3d(${pos.x}px,${pos.y}px,0)`;\n        }\n        render(elem, node, bb);\n      },\n    };\n    const renderer = (root: HTMLElement) => {\n      if (o.queryEachTime) {\n        nodes = reevaluateCollection(nodes);\n      }\n      matchNodes(root, nodes, matchOptions);\n    };\n    return wrapResult(registerCallback(layer, renderer));\n  }\n\n  // if (layer.type === 'svg') {\n  const matchOptions: IMatchOptions<SVGElement> = {\n    ...baseOptions,\n    enter: (node: cy.NodeSingular, bb: cy.BoundingBox12 & cy.BoundingBoxWH) => {\n      const r = layer.node.ownerDocument.createElementNS(SVG_NS, 'g');\n      if (oDOM.init) {\n        oDOM.init(r, node, bb);\n      }\n      return r;\n    },\n    update: (elem, node, bb) => {\n      if (oDOM.position === 'top-left') {\n        elem.setAttribute('transform', `${oDOM.transform}translate3d(${bb.x1},${bb.y1},0)`);\n      } else if (oDOM.position === 'center') {\n        const pos = node.position();\n        elem.setAttribute('transform', `${oDOM.transform}translate3d(${pos.x},${pos.y},0)`);\n      }\n      render(elem, node, bb);\n    },\n  };\n  const renderer = (root: SVGElement) => {\n    if (o.queryEachTime) {\n      nodes = reevaluateCollection(nodes);\n    }\n    matchNodes(root, nodes, matchOptions);\n  };\n  return wrapResult(registerCallback(layer, renderer));\n}\n","import type cy from 'cytoscape';\nimport {\n  HTMLLayer,\n  ICytoscapeDragLayer,\n  ICytoscapeNodeLayer,\n  ICytoscapeSelectBoxLayer,\n  ILayer,\n  ISVGLayer,\n  ICanvasLayer,\n  IHTMLLayer,\n  CytoscapeNodeLayer,\n  CytoscapeDragLayer,\n  CytoscapeSelectBoxLayer,\n  ILayerImpl,\n  ILayerElement,\n  CanvasLayer,\n  SVGLayer,\n  IMoveAbleLayer,\n  SVGStaticLayer,\n  HTMLStaticLayer,\n  ISVGStaticLayer,\n  IHTMLStaticLayer,\n  CanvasStaticLayer,\n  ICanvasStaticLayer,\n  IHTMLLayerOptions,\n  ISVGLayerOptions,\n  ICanvasLayerOptions,\n  IPoint,\n  CytoscapeBaseLayer,\n} from './layers';\nimport type { ILayerAdapter } from './layers/ABaseLayer';\nimport { renderPerEdge, renderPerNode } from './elements';\nimport { output } from './copied';\n\nfunction isPoint(p: IPoint | cy.BoundingBox12): p is IPoint {\n  return (p as IPoint).x != null;\n}\n\nexport interface LayerExportOptions {\n  ignoreUnsupportedLayers?: boolean;\n  ignoreUnsupportedLayerOrder?: boolean;\n}\n\nexport class LayerExportException extends Error {}\n\nexport default class LayersPlugin {\n  readonly cy: cy.Core;\n\n  private bak: Record<string, any> = {};\n\n  readonly nodeLayer: ICytoscapeNodeLayer;\n\n  readonly dragLayer: ICytoscapeDragLayer;\n\n  readonly selectBoxLayer: ICytoscapeSelectBoxLayer;\n\n  private readonly adapter: ILayerAdapter;\n\n  private readonly viewport: { width: number; height: number; tx: number; ty: number; zoom: number };\n\n  constructor(cy: cy.Core) {\n    this.cy = cy;\n    this.adapter = {\n      cy: this.cy,\n      insert: (where: 'before' | 'after', layer: IMoveAbleLayer, type) =>\n        this.insert(where, layer as ILayer & ILayerImpl, type),\n      move: (layer: IMoveAbleLayer, offset) => this.move(layer as ILayer & ILayerImpl, offset),\n      inVisibleBounds: (p: IPoint | cy.BoundingBox12) => {\n        const v = this.viewport;\n        const inX = (x: number) => {\n          const xp = x * v.zoom + v.tx;\n          return xp >= 0 && xp <= v.width;\n        };\n        const inY = (y: number) => {\n          const yp = y * v.zoom + v.ty;\n          return yp >= 0 && yp <= v.height;\n        };\n        if (isPoint(p)) {\n          return inX(p.x) && inY(p.y);\n        }\n        // any of the four corners or the center are inside\n        return (\n          (inX(p.x1) && inY(p.y1)) ||\n          (inX(p.x2) && inY(p.y1)) ||\n          (inX(p.x2) && inY(p.y2)) ||\n          (inX(p.x1) && inY(p.y2)) ||\n          (inX((p.x1 + p.x2) / 2) && inY((p.y1 + p.y2) / 2))\n        );\n      },\n    };\n\n    const container = cy.container()!;\n\n    const nodeLayer = new CytoscapeNodeLayer(\n      this.adapter,\n      container.querySelector<HTMLCanvasElement>('[data-id=\"layer2-node\"]')!\n    );\n    this.nodeLayer = nodeLayer;\n\n    const dragLayer = new CytoscapeDragLayer(\n      this.adapter,\n      container.querySelector<HTMLCanvasElement>('[data-id=\"layer1-drag\"]')!\n    );\n    this.dragLayer = dragLayer;\n\n    const selectBoxLayer = new CytoscapeSelectBoxLayer(\n      this.adapter,\n      container.querySelector<HTMLCanvasElement>('[data-id=\"layer0-selectbox\"]')!\n    );\n    this.selectBoxLayer = selectBoxLayer;\n\n    nodeLayer.root.style.zIndex = '';\n    dragLayer.root.style.zIndex = '';\n    selectBoxLayer.root.style.zIndex = '';\n    nodeLayer.root.insertAdjacentElement('afterend', dragLayer.root);\n    dragLayer.root.insertAdjacentElement('afterend', selectBoxLayer.root);\n\n    cy.on('viewport', this.zoomed);\n    cy.on('resize', this.resize);\n    cy.on('destroy', this.destroy);\n\n    this.bak = {\n      png: cy.png,\n      jpg: cy.jpg,\n      jpeg: cy.jpeg,\n    };\n    cy.png = this.png.bind(this);\n    cy.jpg = this.jpg.bind(this);\n    cy.jpeg = this.jpeg.bind(this);\n\n    this.viewport = {\n      width: this.cy.width(),\n      height: this.cy.height(),\n      tx: this.cy.pan().x,\n      ty: this.cy.pan().y,\n      zoom: this.cy.zoom(),\n    };\n  }\n\n  private move(layer: ILayer & ILayerImpl, offset: number) {\n    const l = this.layers;\n    const index = l.indexOf(layer);\n    const target = Math.max(Math.min(index + offset, l.length, 0));\n    if (target === index) {\n      return;\n    }\n    if (index >= l.length - 1) {\n      this.root.appendChild(layer.root);\n    } else {\n      this.root.insertBefore(layer.root, l[target].root);\n    }\n  }\n\n  get document() {\n    return this.cy.container()!.ownerDocument;\n  }\n\n  get root() {\n    return this.nodeLayer.node.parentElement! as HTMLElement;\n  }\n\n  private get layers(): readonly (ILayer & ILayerImpl)[] {\n    return Array.from(this.root.children)\n      .map((d) => (d as unknown as ILayerElement).__cy_layer)\n      .filter((d) => d != null);\n  }\n\n  getLayers(): readonly ILayer[] {\n    return this.layers;\n  }\n\n  private readonly resize = () => {\n    const width = this.cy.width();\n    const height = this.cy.height();\n    this.viewport.width = width;\n    this.viewport.height = height;\n\n    for (const layer of this.layers) {\n      layer.resize(width, height);\n    }\n  };\n\n  private readonly destroy = () => {\n    for (const layer of this.layers) {\n      layer.remove();\n    }\n\n    this.cy.off('destroy', this.destroy);\n    this.cy.off('viewport', this.zoomed);\n    this.cy.off('resize', this.resize);\n    this.cy.scratch('_layers', undefined);\n\n    this.cy.png = this.bak.png;\n    this.cy.jpg = this.bak.jpg;\n    this.cy.jpeg = this.bak.jpeg;\n\n    this.bak = {};\n  };\n\n  private readonly zoomed = () => {\n    const pan = this.cy.pan();\n    const zoom = this.cy.zoom();\n    this.viewport.tx = pan.x;\n    this.viewport.ty = pan.y;\n    this.viewport.zoom = zoom;\n\n    for (const layer of this.layers) {\n      layer.setViewport(pan.x, pan.y, zoom);\n    }\n  };\n\n  private init<T extends ILayer & ILayerImpl>(layer: T): T {\n    layer.resize(this.viewport.width, this.viewport.height);\n    layer.setViewport(this.viewport.tx, this.viewport.ty, this.viewport.zoom);\n    return layer;\n  }\n\n  update() {\n    this.zoomed();\n    for (const layer of this.layers) {\n      if (layer instanceof CanvasLayer) {\n        layer.draw();\n      }\n    }\n  }\n\n  private createLayer(\n    type: 'svg' | 'html' | 'canvas' | 'svg-static' | 'html-static' | 'canvas-static',\n    options?: IHTMLLayerOptions | ISVGLayerOptions | ICanvasLayerOptions\n  ) {\n    switch (type) {\n      case 'svg':\n        return this.init(new SVGLayer(this.adapter, this.document, options));\n      case 'html':\n        return this.init(new HTMLLayer(this.adapter, this.document, options));\n      case 'canvas':\n        return this.init(new CanvasLayer(this.adapter, this.document, options));\n      case 'html-static':\n        return this.init(new HTMLStaticLayer(this.adapter, this.document, options));\n      case 'svg-static':\n        return this.init(new SVGStaticLayer(this.adapter, this.document, options));\n      case 'canvas-static':\n        return this.init(new CanvasStaticLayer(this.adapter, this.document, options));\n    }\n  }\n\n  append(type: 'svg', options?: ISVGLayerOptions): ISVGLayer;\n  append(type: 'svg-static', options?: ISVGLayerOptions): ISVGStaticLayer;\n  append(type: 'canvas', options?: ICanvasLayerOptions): ICanvasLayer;\n  append(type: 'canvas-static', options?: ICanvasLayerOptions): ICanvasStaticLayer;\n  append(type: 'html', options?: IHTMLLayerOptions): IHTMLLayer;\n  append(type: 'html-static', options?: IHTMLLayerOptions): IHTMLStaticLayer;\n  append(\n    type: 'svg' | 'html' | 'canvas' | 'canvas-static' | 'svg-static' | 'html-static',\n    options?: IHTMLLayerOptions | ISVGLayerOptions | ICanvasLayerOptions\n  ) {\n    const layer = this.createLayer(type, options);\n    this.root.appendChild(layer.root);\n    return layer as any;\n  }\n\n  insert(where: 'before' | 'after', layer: ILayer & ILayerImpl, type: 'svg', options?: ISVGLayerOptions): ISVGLayer;\n  insert(\n    where: 'before' | 'after',\n    layer: ILayer & ILayerImpl,\n    type: 'svg-static',\n    options?: ISVGLayerOptions\n  ): ISVGStaticLayer;\n  insert(\n    where: 'before' | 'after',\n    layer: ILayer & ILayerImpl,\n    type: 'canvas',\n    options?: ICanvasLayerOptions\n  ): ICanvasLayer;\n  insert(\n    where: 'before' | 'after',\n    layer: ILayer & ILayerImpl,\n    type: 'canvas-static',\n    options?: ICanvasLayerOptions\n  ): ICanvasStaticLayer;\n  insert(where: 'before' | 'after', layer: ILayer & ILayerImpl, type: 'html', options?: IHTMLLayerOptions): IHTMLLayer;\n  insert(\n    where: 'before' | 'after',\n    layer: ILayer & ILayerImpl,\n    type: 'html-static',\n    options?: IHTMLLayerOptions\n  ): IHTMLStaticLayer;\n  insert(\n    where: 'before' | 'after',\n    layer: ILayer & ILayerImpl,\n    type: 'svg' | 'html' | 'canvas' | 'svg-static' | 'html-static' | 'canvas-static',\n    options?: IHTMLLayerOptions | ISVGLayerOptions | ICanvasLayerOptions\n  ): ISVGLayer | ICanvasLayer | IHTMLLayer;\n  insert(\n    where: 'before' | 'after',\n    ref: ILayer & ILayerImpl,\n    type: 'svg' | 'html' | 'canvas' | 'svg-static' | 'html-static' | 'canvas-static',\n    options?: IHTMLLayerOptions | ISVGLayerOptions | ICanvasLayerOptions\n  ) {\n    const layer = this.createLayer(type, options);\n    ref.root.insertAdjacentElement(where === 'before' ? 'beforebegin' : 'afterend', layer.root);\n    return layer as any;\n  }\n\n  getLast(): ILayer | null {\n    const layers = this.layers;\n    return layers[layers.length - 1] ?? null;\n  }\n\n  getFirst(): ILayer | null {\n    const layers = this.layers;\n    return layers[0] ?? null;\n  }\n\n  private hasCustomLayer(): boolean {\n    return this.layers.some((d) => !(d instanceof CytoscapeBaseLayer));\n  }\n\n  private toCanvas(\n    options: (cy.ExportJpgStringOptions | cy.ExportJpgBlobOptions | cy.ExportJpgBlobPromiseOptions) & LayerExportOptions\n  ): HTMLCanvasElement {\n    const layers = this.layers;\n\n    if (layers.some((d) => !d.supportsRender) && !options.ignoreUnsupportedLayers) {\n      throw new LayerExportException(\n        `some layer doesn't support exporting, use {ignoreUnsupportedLayers: true} option to ignore`\n      );\n    }\n\n    const nodeIndex = layers.indexOf(this.nodeLayer as ILayer & ILayerImpl);\n    const dragIndex = layers.indexOf(this.dragLayer as ILayer & ILayerImpl);\n    const selectBoxIndex = layers.indexOf(this.selectBoxLayer as ILayer & ILayerImpl);\n\n    if ((nodeIndex !== dragIndex - 1 || dragIndex !== selectBoxIndex - 1) && !options.ignoreUnsupportedLayerOrder) {\n      throw new LayerExportException(\n        `cytoscape layers are not in order, use {ignoreUnsupportedLayerOrder: true} option to ignore`\n      );\n    }\n\n    const before = layers\n      .slice(0, nodeIndex)\n      .reverse()\n      .filter((d) => d.supportsRender() && d !== this.dragLayer && d !== this.selectBoxLayer);\n\n    const after = layers\n      .slice(nodeIndex + 1)\n      .filter((d) => d.supportsRender() && d !== this.dragLayer && d !== this.selectBoxLayer);\n\n    const renderer = (\n      this.cy as unknown as {\n        renderer(): {\n          bufferCanvasImage(\n            o: cy.ExportJpgStringOptions | cy.ExportJpgBlobOptions | cy.ExportJpgBlobPromiseOptions\n          ): HTMLCanvasElement;\n          drawElements(ctx: CanvasRenderingContext2D, elems: cy.Collection): void;\n        };\n      }\n    ).renderer();\n\n    const drawElements = renderer.drawElements;\n    // patch with all levels\n    renderer.drawElements = function (ctx, elems) {\n      for (const l of before) {\n        l.renderInto(ctx);\n      }\n      drawElements.call(this, ctx, elems);\n      for (const l of after) {\n        l.renderInto(ctx);\n      }\n    };\n    const canvas = renderer.bufferCanvasImage(options);\n    renderer.drawElements = drawElements;\n    return canvas;\n  }\n\n  /**\n   * Export the current graph view as a PNG image in Base64 representation.\n   */\n  png(options?: cy.ExportStringOptions & LayerExportOptions): string;\n  png(options?: cy.ExportBlobOptions & LayerExportOptions): Blob;\n  png(options?: cy.ExportBlobPromiseOptions & LayerExportOptions): Promise<Blob>;\n\n  png(\n    options?: (cy.ExportStringOptions | cy.ExportBlobOptions | cy.ExportBlobPromiseOptions) & LayerExportOptions\n  ): any {\n    if (!this.hasCustomLayer()) {\n      return this.bak.png.call(this.cy, options);\n    }\n    return output(options ?? {}, this.toCanvas(options ?? {}), 'image/png');\n  }\n\n  /**\n   * Export the current graph view as a JPG image in Base64 representation.\n   */\n  jpg(options?: cy.ExportJpgStringOptions & LayerExportOptions): string;\n  jpg(options?: cy.ExportJpgBlobOptions & LayerExportOptions): Blob;\n  jpg(options?: cy.ExportJpgBlobPromiseOptions & LayerExportOptions): Promise<Blob>;\n\n  jpg(\n    options?: (cy.ExportJpgStringOptions | cy.ExportJpgBlobOptions | cy.ExportJpgBlobPromiseOptions) &\n      LayerExportOptions\n  ): any {\n    if (!this.hasCustomLayer()) {\n      return this.bak.jpg.call(this.cy, options);\n    }\n    const o = {\n      bg: '#fff',\n      ...(options ?? {}),\n    };\n    return output(o, this.toCanvas(o), 'image/png');\n  }\n\n  /**\n   * Export the current graph view as a JPG image in Base64 representation.\n   */\n  jpeg(options?: cy.ExportJpgStringOptions): string;\n  jpeg(options?: cy.ExportJpgBlobOptions): Blob;\n  jpeg(options?: cy.ExportJpgBlobPromiseOptions): Promise<Blob>;\n\n  jpeg(options?: cy.ExportJpgStringOptions | cy.ExportJpgBlobOptions | cy.ExportJpgBlobPromiseOptions): any {\n    if (!this.hasCustomLayer()) {\n      return this.bak.jpeg.call(this.cy, options);\n    }\n    return this.jpg(options as unknown as any);\n  }\n\n  readonly renderPerEdge = renderPerEdge;\n\n  readonly renderPerNode = renderPerNode;\n}\n\nexport function layers(this: cy.Core): LayersPlugin;\nexport function layers(cy: cy.Core): LayersPlugin;\nexport function layers(this: cy.Core, cy: cy.Core = this) {\n  if (!cy.container()) {\n    throw new Error('layers plugin does not work in headless environments');\n  }\n  // ensure just one instance exists\n  const singleton = cy.scratch('_layers') as LayersPlugin;\n  if (singleton) {\n    return singleton;\n  }\n  const plugin = new LayersPlugin(cy);\n  cy.scratch('_layers', plugin);\n  return plugin;\n}\n","import { layers } from './LayersPlugin';\nexport * from './LayersPlugin';\nexport { default as LayersPlugin } from './LayersPlugin';\nexport * from './elements';\nexport * from './layers/public';\n\nexport default function register(\n  cytoscape: (type: 'core' | 'collection' | 'layout', name: string, extension: unknown) => void\n) {\n  cytoscape('core', 'layers', layers);\n}\n\n// auto register\n\nif (typeof (window as any).cytoscape !== 'undefined') {\n  register((window as any).cytoscape);\n}\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport declare namespace cytoscape {\n  type Ext2 = (cytoscape: (type: 'core' | 'collection' | 'layout', name: string, extension: unknown) => void) => void;\n  function use(module: Ext2): void;\n\n  interface Core {\n    layers: typeof layers;\n  }\n}\n"],"names":[],"mappings":";;;;;;;;;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACO;AACP;AACA;AACO;AACP;AACA;AACO;AACP;AACA;AACO;AACA;AACA;AACP;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;;AClGA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;;ACHO;AACP;AACA;;ACXO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;ACnBO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACZA;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;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACA;AACA;;AC/CA;AACP;AACA;AACA;AACO;AACP;AACe;AACf;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACA;;AC/DQ;AACR;AACP;AACA;AACA;AACA;AACA;AACA;;;"}