{"version":3,"file":"IText.mjs","names":[],"sources":["../../../../src/shapes/IText/IText.ts"],"sourcesContent":["import { Canvas } from '../../canvas/Canvas';\nimport type { ITextEvents } from './ITextBehavior';\nimport { ITextClickBehavior } from './ITextClickBehavior';\nimport {\n  ctrlKeysMapDown,\n  ctrlKeysMapUp,\n  keysMap,\n  keysMapRtl,\n} from './constants';\nimport type { TClassProperties, TFiller, TOptions } from '../../typedefs';\nimport { classRegistry } from '../../ClassRegistry';\nimport type { SerializedTextProps, TextProps } from '../Text/Text';\nimport {\n  JUSTIFY,\n  JUSTIFY_CENTER,\n  JUSTIFY_LEFT,\n  JUSTIFY_RIGHT,\n} from '../Text/constants';\nimport { CENTER, FILL, LEFT, RIGHT, RTL } from '../../constants';\nimport type { ObjectToCanvasElementOptions } from '../Object/Object';\nimport type { FabricObject } from '../Object/FabricObject';\nimport { createCanvasElementFor } from '../../util/misc/dom';\nimport { applyCanvasTransform } from '../../util/internals/applyCanvasTransform';\n\nexport type CursorBoundaries = {\n  left: number;\n  top: number;\n  leftOffset: number;\n  topOffset: number;\n};\n\nexport type CursorRenderingData = {\n  color: string;\n  opacity: number;\n  left: number;\n  top: number;\n  width: number;\n  height: number;\n};\n\n// Declare IText protected properties to workaround TS\nconst protectedDefaultValues = {\n  _selectionDirection: null,\n  _reSpace: /\\s|\\r?\\n/,\n  inCompositionMode: false,\n};\n\nexport const iTextDefaultValues: Partial<TClassProperties<IText>> = {\n  selectionStart: 0,\n  selectionEnd: 0,\n  selectionColor: 'rgba(17,119,255,0.3)',\n  isEditing: false,\n  editable: true,\n  editingBorderColor: 'rgba(102,153,255,0.25)',\n  cursorWidth: 2,\n  cursorColor: '',\n  cursorDelay: 1000,\n  cursorDuration: 600,\n  caching: true,\n  hiddenTextareaContainer: null,\n  keysMap,\n  keysMapRtl,\n  ctrlKeysMapDown,\n  ctrlKeysMapUp,\n  ...protectedDefaultValues,\n};\n\n// @TODO this is not complete\ninterface UniqueITextProps {\n  selectionStart: number;\n  selectionEnd: number;\n}\n\nexport interface SerializedITextProps\n  extends SerializedTextProps, UniqueITextProps {}\n\nexport interface ITextProps extends TextProps, UniqueITextProps {}\n\n/**\n * @fires changed\n * @fires selection:changed\n * @fires editing:entered\n * @fires editing:exited\n * @fires dragstart\n * @fires drag drag event firing on the drag source\n * @fires dragend\n * @fires copy\n * @fires cut\n * @fires paste\n *\n * #### Supported key combinations\n * ```\n *   Move cursor:                    left, right, up, down\n *   Select character:               shift + left, shift + right\n *   Select text vertically:         shift + up, shift + down\n *   Move cursor by word:            alt + left, alt + right\n *   Select words:                   shift + alt + left, shift + alt + right\n *   Move cursor to line start/end:  cmd + left, cmd + right or home, end\n *   Select till start/end of line:  cmd + shift + left, cmd + shift + right or shift + home, shift + end\n *   Jump to start/end of text:      cmd + up, cmd + down\n *   Select till start/end of text:  cmd + shift + up, cmd + shift + down or shift + pgUp, shift + pgDown\n *   Delete character:               backspace\n *   Delete word:                    alt + backspace\n *   Delete line:                    cmd + backspace\n *   Forward delete:                 delete\n *   Copy text:                      ctrl/cmd + c\n *   Paste text:                     ctrl/cmd + v\n *   Cut text:                       ctrl/cmd + x\n *   Select entire text:             ctrl/cmd + a\n *   Quit editing                    tab or esc\n * ```\n *\n * #### Supported mouse/touch combination\n * ```\n *   Position cursor:                click/touch\n *   Create selection:               click/touch & drag\n *   Create selection:               click & shift + click\n *   Select word:                    double click\n *   Select line:                    triple click\n * ```\n */\nexport class IText<\n  Props extends TOptions<ITextProps> = Partial<ITextProps>,\n  SProps extends SerializedITextProps = SerializedITextProps,\n  EventSpec extends ITextEvents = ITextEvents,\n>\n  extends ITextClickBehavior<Props, SProps, EventSpec>\n  implements UniqueITextProps\n{\n  /**\n   * Index where text selection starts (or where cursor is when there is no selection)\n   * @type Number\n   */\n  declare selectionStart: number;\n\n  /**\n   * Index where text selection ends\n   * @type Number\n   */\n  declare selectionEnd: number;\n\n  declare compositionStart: number;\n\n  declare compositionEnd: number;\n\n  /**\n   * Color of text selection\n   * @type String\n   */\n  declare selectionColor: string;\n\n  /**\n   * Indicates whether text is in editing mode\n   * @type Boolean\n   */\n  declare isEditing: boolean;\n\n  /**\n   * Indicates whether a text can be edited\n   * @type Boolean\n   */\n  declare editable: boolean;\n\n  /**\n   * Border color of text object while it's in editing mode\n   * @type String\n   */\n  declare editingBorderColor: string;\n\n  /**\n   * Width of cursor (in px)\n   * @type Number\n   */\n  declare cursorWidth: number;\n\n  /**\n   * Color of text cursor color in editing mode.\n   * if not set (default) will take color from the text.\n   * if set to a color value that fabric can understand, it will\n   * be used instead of the color of the text at the current position.\n   * @type String\n   */\n  declare cursorColor: string;\n\n  /**\n   * Delay between cursor blink (in ms)\n   * @type Number\n   */\n  declare cursorDelay: number;\n\n  /**\n   * Duration of cursor fade in (in ms)\n   * @type Number\n   */\n  declare cursorDuration: number;\n\n  declare compositionColor: string;\n\n  /**\n   * Indicates whether internal text char widths can be cached\n   * @type Boolean\n   */\n  declare caching: boolean;\n\n  static ownDefaults = iTextDefaultValues;\n\n  static getDefaults(): Record<string, any> {\n    return { ...super.getDefaults(), ...IText.ownDefaults };\n  }\n\n  static type = 'IText';\n\n  get type() {\n    const type = super.type;\n    // backward compatibility\n    return type === 'itext' ? 'i-text' : type;\n  }\n\n  /**\n   * Constructor\n   * @param {String} text Text string\n   * @param {Object} [options] Options object\n   */\n  constructor(text: string, options?: Props) {\n    super(text, { ...IText.ownDefaults, ...options } as Props);\n    this.initBehavior();\n  }\n\n  /**\n   * While editing handle differently\n   * @private\n   * @param {string} key\n   * @param {*} value\n   */\n  _set(key: string, value: any) {\n    if (this.isEditing && this._savedProps && key in this._savedProps) {\n      // @ts-expect-error irritating TS\n      this._savedProps[key] = value;\n      return this;\n    }\n    if (key === 'canvas') {\n      this.canvas instanceof Canvas &&\n        this.canvas.textEditingManager.remove(this);\n      value instanceof Canvas && value.textEditingManager.add(this);\n    }\n    return super._set(key, value);\n  }\n\n  /**\n   * Sets selection start (left boundary of a selection)\n   * @param {Number} index Index to set selection start to\n   */\n  setSelectionStart(index: number) {\n    index = Math.max(index, 0);\n    this._updateAndFire('selectionStart', index);\n  }\n\n  /**\n   * Sets selection end (right boundary of a selection)\n   * @param {Number} index Index to set selection end to\n   */\n  setSelectionEnd(index: number) {\n    index = Math.min(index, this.text.length);\n    this._updateAndFire('selectionEnd', index);\n  }\n\n  /**\n   * @private\n   * @param {String} property 'selectionStart' or 'selectionEnd'\n   * @param {Number} index new position of property\n   */\n  protected _updateAndFire(\n    property: 'selectionStart' | 'selectionEnd',\n    index: number,\n  ) {\n    if (this[property] !== index) {\n      this._fireSelectionChanged();\n      this[property] = index;\n    }\n    this._updateTextarea();\n  }\n\n  /**\n   * Fires the even of selection changed\n   * @private\n   */\n  _fireSelectionChanged() {\n    this.fire('selection:changed');\n    this.canvas && this.canvas.fire('text:selection:changed', { target: this });\n  }\n\n  /**\n   * Initialize text dimensions. Render all text on given context\n   * or on a offscreen canvas to get the text width with measureText.\n   * Updates this.width and this.height with the proper values.\n   * Does not return dimensions.\n   * @private\n   */\n  initDimensions() {\n    this.isEditing && this.initDelayedCursor();\n    super.initDimensions();\n  }\n\n  /**\n   * Gets style of a current selection/cursor (at the start position)\n   * if startIndex or endIndex are not provided, selectionStart or selectionEnd will be used.\n   * @param {Number} startIndex Start index to get styles at\n   * @param {Number} endIndex End index to get styles at, if not specified selectionEnd or startIndex + 1\n   * @param {Boolean} [complete] get full style or not\n   * @return {Array} styles an array with one, zero or more Style objects\n   */\n  getSelectionStyles(\n    startIndex: number = this.selectionStart || 0,\n    endIndex: number = this.selectionEnd,\n    complete?: boolean,\n  ) {\n    return super.getSelectionStyles(startIndex, endIndex, complete);\n  }\n\n  /**\n   * Sets style of a current selection, if no selection exist, do not set anything.\n   * @param {Object} [styles] Styles object\n   * @param {Number} [startIndex] Start index to get styles at\n   * @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1\n   */\n  setSelectionStyles(\n    styles: object,\n    startIndex: number = this.selectionStart || 0,\n    endIndex: number = this.selectionEnd,\n  ) {\n    return super.setSelectionStyles(styles, startIndex, endIndex);\n  }\n\n  /**\n   * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)\n   * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.\n   * @param {Boolean} [skipWrapping] consider the location for unwrapped lines. useful to manage styles.\n   */\n  get2DCursorLocation(\n    selectionStart = this.selectionStart,\n    skipWrapping?: boolean,\n  ) {\n    return super.get2DCursorLocation(selectionStart, skipWrapping);\n  }\n\n  /**\n   * @private\n   * @param {CanvasRenderingContext2D} ctx Context to render on\n   */\n  render(ctx: CanvasRenderingContext2D) {\n    super.render(ctx);\n    // clear the cursorOffsetCache, so we ensure to calculate once per renderCursor\n    // the correct position but not at every cursor animation.\n    this.cursorOffsetCache = {};\n    this.renderCursorOrSelection();\n  }\n\n  /**\n   * @override block cursor/selection logic while rendering the exported canvas\n   * @todo this workaround should be replaced with a more robust solution\n   */\n  toCanvasElement(options?: ObjectToCanvasElementOptions): HTMLCanvasElement {\n    const isEditing = this.isEditing;\n    this.isEditing = false;\n    const canvas = super.toCanvasElement(options);\n    this.isEditing = isEditing;\n    return canvas;\n  }\n\n  /**\n   * Renders cursor or selection (depending on what exists)\n   * it does on the contextTop. If contextTop is not available, do nothing.\n   */\n  renderCursorOrSelection() {\n    if (!this.isEditing || !this.canvas) {\n      return;\n    }\n    const ctx = this.clearContextTop(true);\n    if (!ctx) {\n      return;\n    }\n    const boundaries = this._getCursorBoundaries();\n\n    const ancestors = this.findAncestorsWithClipPath();\n    const hasAncestorsWithClipping = ancestors.length > 0;\n    let drawingCtx: CanvasRenderingContext2D = ctx;\n    let drawingCanvas: HTMLCanvasElement | undefined = undefined;\n    if (hasAncestorsWithClipping) {\n      // we have some clipPath, we need to draw the selection on an intermediate layer.\n      drawingCanvas = createCanvasElementFor(ctx.canvas);\n      drawingCtx = drawingCanvas.getContext('2d')!;\n      applyCanvasTransform(drawingCtx, this.canvas);\n      const m = this.calcTransformMatrix();\n      drawingCtx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);\n    }\n\n    if (this.selectionStart === this.selectionEnd && !this.inCompositionMode) {\n      this.renderCursor(drawingCtx, boundaries);\n    } else {\n      this.renderSelection(drawingCtx, boundaries);\n    }\n\n    if (hasAncestorsWithClipping) {\n      // we need a neutral context.\n      // this won't work for nested clippaths in which a clippath\n      // has its own clippath\n      for (const ancestor of ancestors) {\n        const clipPath = ancestor.clipPath!;\n        const clippingCanvas = createCanvasElementFor(ctx.canvas);\n        const clippingCtx = clippingCanvas.getContext('2d')!;\n        applyCanvasTransform(clippingCtx, this.canvas);\n        // position the ctx in the center of the outer ancestor\n        if (!clipPath.absolutePositioned) {\n          const m = ancestor.calcTransformMatrix();\n          clippingCtx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);\n        }\n        clipPath.transform(clippingCtx);\n        // we assign an empty drawing context, we don't plan to have this working for nested clippaths for now\n        clipPath.drawObject(clippingCtx, true, {});\n        this.drawClipPathOnCache(drawingCtx, clipPath, clippingCanvas);\n      }\n    }\n\n    if (hasAncestorsWithClipping) {\n      ctx.setTransform(1, 0, 0, 1, 0, 0);\n      ctx.drawImage(drawingCanvas!, 0, 0);\n    }\n\n    this.canvas.contextTopDirty = true;\n    ctx.restore();\n  }\n\n  /**\n   * Finds and returns an array of clip paths that are applied to the parent\n   * group(s) of the current FabricObject instance. The object's hierarchy is\n   * traversed upwards (from the current object towards the root of the canvas),\n   * checking each parent object for the presence of a `clipPath` that is not\n   * absolutely positioned.\n   */\n  findAncestorsWithClipPath(): FabricObject[] {\n    const clipPathAncestors: FabricObject[] = [];\n    // eslint-disable-next-line @typescript-eslint/no-this-alias\n    let obj: FabricObject | undefined = this;\n    while (obj) {\n      if (obj.clipPath) {\n        clipPathAncestors.push(obj);\n      }\n      obj = obj.parent;\n    }\n\n    return clipPathAncestors;\n  }\n\n  /**\n   * Returns cursor boundaries (left, top, leftOffset, topOffset)\n   * left/top are left/top of entire text box\n   * leftOffset/topOffset are offset from that left/top point of a text box\n   * @private\n   * @param {number} [index] index from start\n   * @param {boolean} [skipCaching]\n   */\n  _getCursorBoundaries(\n    index: number = this.selectionStart,\n    skipCaching?: boolean,\n  ): CursorBoundaries {\n    const left = this._getLeftOffset(),\n      top = this._getTopOffset(),\n      offsets = this._getCursorBoundariesOffsets(index, skipCaching);\n    return {\n      left: left,\n      top: top,\n      leftOffset: offsets.left,\n      topOffset: offsets.top,\n    };\n  }\n\n  /**\n   * Caches and returns cursor left/top offset relative to instance's center point\n   * @private\n   * @param {number} index index from start\n   * @param {boolean} [skipCaching]\n   */\n  _getCursorBoundariesOffsets(\n    index: number,\n    skipCaching?: boolean,\n  ): { left: number; top: number } {\n    if (skipCaching) {\n      return this.__getCursorBoundariesOffsets(index);\n    }\n    if (this.cursorOffsetCache && 'top' in this.cursorOffsetCache) {\n      return this.cursorOffsetCache as { left: number; top: number };\n    }\n    return (this.cursorOffsetCache = this.__getCursorBoundariesOffsets(index));\n  }\n\n  /**\n   * Calculates cursor left/top offset relative to instance's center point\n   * @private\n   * @param {number} index index from start\n   */\n  __getCursorBoundariesOffsets(index: number) {\n    let topOffset = 0,\n      leftOffset = 0;\n    const { charIndex, lineIndex } = this.get2DCursorLocation(index);\n    const { textAlign, direction } = this;\n    for (let i = 0; i < lineIndex; i++) {\n      topOffset += this.getHeightOfLine(i);\n    }\n    const lineLeftOffset = this._getLineLeftOffset(lineIndex);\n    const bound = this.__charBounds[lineIndex][charIndex];\n    bound && (leftOffset = bound.left);\n    if (\n      this.charSpacing !== 0 &&\n      charIndex === this._textLines[lineIndex].length\n    ) {\n      leftOffset -= this._getWidthOfCharSpacing();\n    }\n    let left = lineLeftOffset + (leftOffset > 0 ? leftOffset : 0);\n\n    if (direction === RTL) {\n      if (\n        textAlign === RIGHT ||\n        textAlign === JUSTIFY ||\n        textAlign === JUSTIFY_RIGHT\n      ) {\n        left *= -1;\n      } else if (textAlign === LEFT || textAlign === JUSTIFY_LEFT) {\n        left = lineLeftOffset - (leftOffset > 0 ? leftOffset : 0);\n      } else if (textAlign === CENTER || textAlign === JUSTIFY_CENTER) {\n        left = lineLeftOffset - (leftOffset > 0 ? leftOffset : 0);\n      }\n    }\n    return {\n      top: topOffset,\n      left,\n    };\n  }\n\n  /**\n   * Renders cursor on context Top, outside the animation cycle, on request\n   * Used for the drag/drop effect.\n   * If contextTop is not available, do nothing.\n   */\n  renderCursorAt(selectionStart: number) {\n    this._renderCursor(\n      this.canvas!.contextTop,\n      this._getCursorBoundaries(selectionStart, true),\n      selectionStart,\n    );\n  }\n\n  /**\n   * Renders cursor\n   * @param {Object} boundaries\n   * @param {CanvasRenderingContext2D} ctx transformed context to draw on\n   */\n  renderCursor(ctx: CanvasRenderingContext2D, boundaries: CursorBoundaries) {\n    this._renderCursor(ctx, boundaries, this.selectionStart);\n  }\n\n  /**\n   * Return the data needed to render the cursor for given selection start\n   * The left,top are relative to the object, while width and height are prescaled\n   * to look think with canvas zoom and object scaling,\n   * so they depend on canvas and object scaling\n   */\n  getCursorRenderingData(\n    selectionStart: number = this.selectionStart,\n    boundaries: CursorBoundaries = this._getCursorBoundaries(selectionStart),\n  ): CursorRenderingData {\n    const cursorLocation = this.get2DCursorLocation(selectionStart),\n      lineIndex = cursorLocation.lineIndex,\n      charIndex =\n        cursorLocation.charIndex > 0 ? cursorLocation.charIndex - 1 : 0,\n      charHeight = this.getValueOfPropertyAt(lineIndex, charIndex, 'fontSize'),\n      multiplier = this.getObjectScaling().x * this.canvas!.getZoom(),\n      cursorWidth = this.cursorWidth / multiplier,\n      dy = this.getValueOfPropertyAt(lineIndex, charIndex, 'deltaY'),\n      topOffset =\n        boundaries.topOffset +\n        ((1 - this._fontSizeFraction) * this.getHeightOfLine(lineIndex)) /\n          this.lineHeight -\n        charHeight * (1 - this._fontSizeFraction);\n\n    return {\n      color:\n        this.cursorColor ||\n        (this.getValueOfPropertyAt(lineIndex, charIndex, 'fill') as string),\n      opacity: this._currentCursorOpacity,\n      left: boundaries.left + boundaries.leftOffset - cursorWidth / 2,\n      top: topOffset + boundaries.top + dy,\n      width: cursorWidth,\n      height: charHeight,\n    };\n  }\n\n  /**\n   * Render the cursor at the given selectionStart.\n   * @param {CanvasRenderingContext2D} ctx transformed context to draw on\n   */\n  _renderCursor(\n    ctx: CanvasRenderingContext2D,\n    boundaries: CursorBoundaries,\n    selectionStart: number,\n  ) {\n    const { color, opacity, left, top, width, height } =\n      this.getCursorRenderingData(selectionStart, boundaries);\n    ctx.fillStyle = color;\n    ctx.globalAlpha = opacity;\n    ctx.fillRect(left, top, width, height);\n  }\n\n  /**\n   * Renders text selection\n   * @param {Object} boundaries Object with left/top/leftOffset/topOffset\n   * @param {CanvasRenderingContext2D} ctx transformed context to draw on\n   */\n  renderSelection(ctx: CanvasRenderingContext2D, boundaries: CursorBoundaries) {\n    const selection = {\n      selectionStart: this.inCompositionMode\n        ? this.hiddenTextarea!.selectionStart\n        : this.selectionStart,\n      selectionEnd: this.inCompositionMode\n        ? this.hiddenTextarea!.selectionEnd\n        : this.selectionEnd,\n    };\n    this._renderSelection(ctx, selection, boundaries);\n  }\n\n  /**\n   * Renders drag start text selection\n   */\n  renderDragSourceEffect() {\n    const dragStartSelection =\n      this.draggableTextDelegate.getDragStartSelection()!;\n    this._renderSelection(\n      this.canvas!.contextTop,\n      dragStartSelection,\n      this._getCursorBoundaries(dragStartSelection.selectionStart, true),\n    );\n  }\n\n  renderDropTargetEffect(e: DragEvent) {\n    const dragSelection = this.getSelectionStartFromPointer(e);\n    this.renderCursorAt(dragSelection);\n  }\n\n  /**\n   * Renders text selection\n   * @private\n   * @param {{ selectionStart: number, selectionEnd: number }} selection\n   * @param {Object} boundaries Object with left/top/leftOffset/topOffset\n   * @param {CanvasRenderingContext2D} ctx transformed context to draw on\n   */\n  _renderSelection(\n    ctx: CanvasRenderingContext2D,\n    selection: { selectionStart: number; selectionEnd: number },\n    boundaries: CursorBoundaries,\n  ) {\n    const { textAlign, direction } = this;\n    const selectionStart = selection.selectionStart,\n      selectionEnd = selection.selectionEnd,\n      isJustify = textAlign.includes(JUSTIFY),\n      start = this.get2DCursorLocation(selectionStart),\n      end = this.get2DCursorLocation(selectionEnd),\n      startLine = start.lineIndex,\n      endLine = end.lineIndex,\n      startChar = start.charIndex < 0 ? 0 : start.charIndex,\n      endChar = end.charIndex < 0 ? 0 : end.charIndex;\n\n    for (let i = startLine; i <= endLine; i++) {\n      const lineOffset = this._getLineLeftOffset(i) || 0;\n      let lineHeight = this.getHeightOfLine(i),\n        realLineHeight = 0,\n        boxStart = 0,\n        boxEnd = 0;\n\n      if (i === startLine) {\n        boxStart = this.__charBounds[startLine][startChar].left;\n      }\n      if (i >= startLine && i < endLine) {\n        boxEnd =\n          isJustify && !this.isEndOfWrapping(i)\n            ? this.width\n            : this.getLineWidth(i) || 5; // WTF is this 5?\n      } else if (i === endLine) {\n        if (endChar === 0) {\n          boxEnd = this.__charBounds[endLine][endChar].left;\n        } else {\n          const charSpacing = this._getWidthOfCharSpacing();\n          boxEnd =\n            this.__charBounds[endLine][endChar - 1].left +\n            this.__charBounds[endLine][endChar - 1].width -\n            charSpacing;\n        }\n      }\n      realLineHeight = lineHeight;\n      if (this.lineHeight < 1 || (i === endLine && this.lineHeight > 1)) {\n        lineHeight /= this.lineHeight;\n      }\n      let drawStart = boundaries.left + lineOffset + boxStart,\n        drawHeight = lineHeight,\n        extraTop = 0;\n      const drawWidth = boxEnd - boxStart;\n      if (this.inCompositionMode) {\n        ctx.fillStyle = this.compositionColor || 'black';\n        drawHeight = 1;\n        extraTop = lineHeight;\n      } else {\n        ctx.fillStyle = this.selectionColor;\n      }\n      if (direction === RTL) {\n        if (\n          textAlign === RIGHT ||\n          textAlign === JUSTIFY ||\n          textAlign === JUSTIFY_RIGHT\n        ) {\n          drawStart = this.width - drawStart - drawWidth;\n        } else if (textAlign === LEFT || textAlign === JUSTIFY_LEFT) {\n          drawStart = boundaries.left + lineOffset - boxEnd;\n        } else if (textAlign === CENTER || textAlign === JUSTIFY_CENTER) {\n          drawStart = boundaries.left + lineOffset - boxEnd;\n        }\n      }\n      ctx.fillRect(\n        drawStart,\n        boundaries.top + boundaries.topOffset + extraTop,\n        drawWidth,\n        drawHeight,\n      );\n      boundaries.topOffset += realLineHeight;\n    }\n  }\n\n  /**\n   * High level function to know the height of the cursor.\n   * the currentChar is the one that precedes the cursor\n   * Returns fontSize of char at the current cursor\n   * Unused from the library, is for the end user\n   * @return {Number} Character font size\n   */\n  getCurrentCharFontSize(): number {\n    const cp = this._getCurrentCharIndex();\n    return this.getValueOfPropertyAt(cp.l, cp.c, 'fontSize');\n  }\n\n  /**\n   * High level function to know the color of the cursor.\n   * the currentChar is the one that precedes the cursor\n   * Returns color (fill) of char at the current cursor\n   * if the text object has a pattern or gradient for filler, it will return that.\n   * Unused by the library, is for the end user\n   * @return {String | TFiller} Character color (fill)\n   */\n  getCurrentCharColor(): string | TFiller | null {\n    const cp = this._getCurrentCharIndex();\n    return this.getValueOfPropertyAt(cp.l, cp.c, FILL);\n  }\n\n  /**\n   * Returns the cursor position for the getCurrent.. functions\n   * @private\n   */\n  _getCurrentCharIndex() {\n    const cursorPosition = this.get2DCursorLocation(this.selectionStart, true),\n      charIndex =\n        cursorPosition.charIndex > 0 ? cursorPosition.charIndex - 1 : 0;\n    return { l: cursorPosition.lineIndex, c: charIndex };\n  }\n\n  dispose() {\n    this.exitEditingImpl();\n    this.draggableTextDelegate.dispose();\n    super.dispose();\n  }\n}\n\nclassRegistry.setClass(IText);\n// legacy\nclassRegistry.setClass(IText, 'i-text');\n"],"mappings":"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"}