{"version":3,"file":"ITextBehavior.min.mjs","sources":["../../../../src/shapes/IText/ITextBehavior.ts"],"sourcesContent":["import type { ObjectEvents, TPointerEvent } from '../../EventTypeDefs';\nimport { Point } from '../../Point';\nimport type { FabricObject } from '../Object/FabricObject';\nimport { FabricText } from '../Text/Text';\nimport { animate } from '../../util/animation/animate';\nimport type { TOnAnimationChangeCallback } from '../../util/animation/types';\nimport type { ValueAnimation } from '../../util/animation/ValueAnimation';\nimport type { TextStyleDeclaration } from '../Text/StyledText';\nimport type { SerializedTextProps, TextProps } from '../Text/Text';\nimport type { TOptions, TOriginX } from '../../typedefs';\nimport { getDocumentFromElement } from '../../util/dom_misc';\nimport { LEFT, LTR, MODIFIED, RIGHT, reNewline } from '../../constants';\nimport type { IText } from './IText';\nimport { JUSTIFY } from '../Text/constants';\n\n/**\n *  extend this regex to support non english languages\n *\n *  - ` `      Matches a SPACE character (char code 32).\n *  - `\\n`     Matches a LINE FEED character (char code 10).\n *  - `\\.`     Matches a \".\" character (char code 46).\n *  - `,`      Matches a \",\" character (char code 44).\n *  - `;`      Matches a \";\" character (char code 59).\n *  - `!`      Matches a \"!\" character (char code 33).\n *  - `\\?`     Matches a \"?\" character (char code 63).\n *  - `\\-`     Matches a \"-\" character (char code 45).\n */\n// eslint-disable-next-line no-useless-escape\nconst reNonWord = /[ \\n\\.,;!\\?\\-]/;\n\nexport type ITextEvents = ObjectEvents & {\n  'selection:changed': never;\n  changed: never | { index: number; action: string };\n  'editing:entered': never | { e: TPointerEvent };\n  'editing:exited': never;\n};\n\nexport abstract class ITextBehavior<\n  Props extends TOptions<TextProps> = Partial<TextProps>,\n  SProps extends SerializedTextProps = SerializedTextProps,\n  EventSpec extends ITextEvents = ITextEvents,\n> extends FabricText<Props, SProps, EventSpec> {\n  declare abstract isEditing: boolean;\n  declare abstract cursorDelay: number;\n  declare abstract selectionStart: number;\n  declare abstract selectionEnd: number;\n  declare abstract cursorDuration: number;\n  declare abstract editable: boolean;\n  declare abstract editingBorderColor: string;\n\n  declare abstract compositionStart: number;\n  declare abstract compositionEnd: number;\n\n  declare abstract hiddenTextarea: HTMLTextAreaElement | null;\n\n  /**\n   * Helps determining when the text is in composition, so that the cursor\n   * rendering is altered.\n   */\n  declare protected inCompositionMode: boolean;\n\n  declare protected _reSpace: RegExp;\n  declare private _currentTickState?: ValueAnimation;\n  declare private _currentTickCompleteState?: ValueAnimation;\n  protected _currentCursorOpacity = 1;\n  declare private _textBeforeEdit: string;\n  declare protected __selectionStartOnMouseDown: number;\n\n  /**\n   * Keeps track if the IText object was selected before the actual click.\n   * This because we want to delay enter editing by a click.\n   */\n  declare protected selected: boolean;\n  declare protected cursorOffsetCache: { left?: number; top?: number };\n  declare protected _savedProps?: {\n    hasControls: boolean;\n    borderColor: string;\n    lockMovementX: boolean;\n    lockMovementY: boolean;\n    selectable: boolean;\n    hoverCursor: CSSStyleDeclaration['cursor'] | null;\n    defaultCursor?: CSSStyleDeclaration['cursor'];\n    moveCursor?: CSSStyleDeclaration['cursor'];\n  };\n  declare protected _selectionDirection: 'left' | 'right' | null;\n\n  abstract initHiddenTextarea(): void;\n  abstract _fireSelectionChanged(): void;\n  abstract renderCursorOrSelection(): void;\n  abstract getSelectionStartFromPointer(e: TPointerEvent): number;\n  abstract _getCursorBoundaries(\n    index: number,\n    skipCaching?: boolean,\n  ): {\n    left: number;\n    top: number;\n    leftOffset: number;\n    topOffset: number;\n  };\n\n  /**\n   * Initializes all the interactive behavior of IText\n   */\n  initBehavior() {\n    this._tick = this._tick.bind(this);\n    this._onTickComplete = this._onTickComplete.bind(this);\n    this.updateSelectionOnMouseMove =\n      this.updateSelectionOnMouseMove.bind(this);\n  }\n\n  onDeselect(options?: { e?: TPointerEvent; object?: FabricObject }) {\n    this.isEditing && this.exitEditing();\n    this.selected = false;\n    return super.onDeselect(options);\n  }\n\n  /**\n   * @private\n   */\n  _animateCursor({\n    toValue,\n    duration,\n    delay,\n    onComplete,\n  }: {\n    toValue: number;\n    duration: number;\n    delay?: number;\n    onComplete?: TOnAnimationChangeCallback<number>;\n  }) {\n    return animate({\n      startValue: this._currentCursorOpacity,\n      endValue: toValue,\n      duration,\n      delay,\n      onComplete,\n      abort: () =>\n        !this.canvas ||\n        // we do not want to animate a selection, only cursor\n        this.selectionStart !== this.selectionEnd,\n      onChange: (value) => {\n        this._currentCursorOpacity = value;\n        this.renderCursorOrSelection();\n      },\n    });\n  }\n\n  /**\n   * changes the cursor from visible to invisible\n   */\n  private _tick(delay?: number) {\n    this._currentTickState = this._animateCursor({\n      toValue: 0,\n      duration: this.cursorDuration / 2,\n      delay: Math.max(delay || 0, 100),\n      onComplete: this._onTickComplete,\n    });\n  }\n\n  /**\n   * Changes the cursor from invisible to visible\n   */\n  private _onTickComplete() {\n    this._currentTickCompleteState?.abort();\n    this._currentTickCompleteState = this._animateCursor({\n      toValue: 1,\n      duration: this.cursorDuration,\n      onComplete: this._tick,\n    });\n  }\n\n  /**\n   * Initializes delayed cursor\n   */\n  initDelayedCursor(restart?: boolean) {\n    this.abortCursorAnimation();\n    this._tick(restart ? 0 : this.cursorDelay);\n  }\n\n  /**\n   * Aborts cursor animation, clears all timeouts and clear textarea context if necessary\n   */\n  abortCursorAnimation() {\n    let shouldClear = false;\n    [this._currentTickState, this._currentTickCompleteState].forEach(\n      (cursorAnimation) => {\n        if (cursorAnimation && !cursorAnimation.isDone()) {\n          shouldClear = true;\n          cursorAnimation.abort();\n        }\n      },\n    );\n\n    this._currentCursorOpacity = 1;\n\n    //  make sure we clear context even if instance is not editing\n    if (shouldClear) {\n      this.clearContextTop();\n    }\n  }\n\n  /**\n   * Restart tue cursor animation if either is in complete state ( between animations )\n   * or if it never started before\n   */\n  restartCursorIfNeeded() {\n    if (\n      [this._currentTickState, this._currentTickCompleteState].some(\n        (cursorAnimation) => !cursorAnimation || cursorAnimation.isDone(),\n      )\n    ) {\n      this.initDelayedCursor();\n    }\n  }\n\n  /**\n   * Selects entire text\n   */\n  selectAll() {\n    this.selectionStart = 0;\n    this.selectionEnd = this._text.length;\n    this._fireSelectionChanged();\n    this._updateTextarea();\n    return this;\n  }\n\n  /**\n   * Selects entire text and updates the visual state\n   */\n  cmdAll() {\n    this.selectAll();\n    this.renderCursorOrSelection();\n  }\n\n  /**\n   * Returns selected text\n   * @return {String}\n   */\n  getSelectedText(): string {\n    return this._text.slice(this.selectionStart, this.selectionEnd).join('');\n  }\n\n  /**\n   * Find new selection index representing start of current word according to current selection index\n   * @param {Number} startFrom Current selection index\n   * @return {Number} New selection index\n   */\n  findWordBoundaryLeft(startFrom: number): number {\n    let offset = 0,\n      index = startFrom - 1;\n\n    // remove space before cursor first\n    if (this._reSpace.test(this._text[index])) {\n      while (this._reSpace.test(this._text[index])) {\n        offset++;\n        index--;\n      }\n    }\n    while (/\\S/.test(this._text[index]) && index > -1) {\n      offset++;\n      index--;\n    }\n\n    return startFrom - offset;\n  }\n\n  /**\n   * Find new selection index representing end of current word according to current selection index\n   * @param {Number} startFrom Current selection index\n   * @return {Number} New selection index\n   */\n  findWordBoundaryRight(startFrom: number): number {\n    let offset = 0,\n      index = startFrom;\n\n    // remove space after cursor first\n    if (this._reSpace.test(this._text[index])) {\n      while (this._reSpace.test(this._text[index])) {\n        offset++;\n        index++;\n      }\n    }\n    while (/\\S/.test(this._text[index]) && index < this._text.length) {\n      offset++;\n      index++;\n    }\n\n    return startFrom + offset;\n  }\n\n  /**\n   * Find new selection index representing start of current line according to current selection index\n   * @param {Number} startFrom Current selection index\n   * @return {Number} New selection index\n   */\n  findLineBoundaryLeft(startFrom: number): number {\n    let offset = 0,\n      index = startFrom - 1;\n\n    while (!/\\n/.test(this._text[index]) && index > -1) {\n      offset++;\n      index--;\n    }\n\n    return startFrom - offset;\n  }\n\n  /**\n   * Find new selection index representing end of current line according to current selection index\n   * @param {Number} startFrom Current selection index\n   * @return {Number} New selection index\n   */\n  findLineBoundaryRight(startFrom: number): number {\n    let offset = 0,\n      index = startFrom;\n\n    while (!/\\n/.test(this._text[index]) && index < this._text.length) {\n      offset++;\n      index++;\n    }\n\n    return startFrom + offset;\n  }\n\n  /**\n   * Finds index corresponding to beginning or end of a word\n   * @param {Number} selectionStart Index of a character\n   * @param {Number} direction 1 or -1\n   * @return {Number} Index of the beginning or end of a word\n   */\n  searchWordBoundary(selectionStart: number, direction: 1 | -1): number {\n    const text = this._text;\n    // if we land on a space we move the cursor backwards\n    // if we are searching boundary end we move the cursor backwards ONLY if we don't land on a line break\n    let index =\n        selectionStart > 0 &&\n        this._reSpace.test(text[selectionStart]) &&\n        (direction === -1 || !reNewline.test(text[selectionStart - 1]))\n          ? selectionStart - 1\n          : selectionStart,\n      _char = text[index];\n    while (index > 0 && index < text.length && !reNonWord.test(_char)) {\n      index += direction;\n      _char = text[index];\n    }\n    if (direction === -1 && reNonWord.test(_char)) {\n      index++;\n    }\n    return index;\n  }\n\n  /**\n   * Selects the word that contains the char at index selectionStart\n   * @param {Number} selectionStart Index of a character\n   */\n  selectWord(selectionStart?: number) {\n    selectionStart = selectionStart ?? this.selectionStart;\n    // search backwards\n    const newSelectionStart = this.searchWordBoundary(selectionStart, -1),\n      // search forward\n      newSelectionEnd = Math.max(\n        newSelectionStart,\n        this.searchWordBoundary(selectionStart, 1),\n      );\n\n    this.selectionStart = newSelectionStart;\n    this.selectionEnd = newSelectionEnd;\n    this._fireSelectionChanged();\n    this._updateTextarea();\n    // remove next major, for now it renders twice :(\n    this.renderCursorOrSelection();\n  }\n\n  /**\n   * Selects the line that contains selectionStart\n   * @param {Number} selectionStart Index of a character\n   */\n  selectLine(selectionStart?: number) {\n    selectionStart = selectionStart ?? this.selectionStart;\n    const newSelectionStart = this.findLineBoundaryLeft(selectionStart),\n      newSelectionEnd = this.findLineBoundaryRight(selectionStart);\n\n    this.selectionStart = newSelectionStart;\n    this.selectionEnd = newSelectionEnd;\n    this._fireSelectionChanged();\n    this._updateTextarea();\n  }\n\n  /**\n   * Enters editing state\n   */\n  enterEditing(e?: TPointerEvent) {\n    if (this.isEditing || !this.editable) {\n      return;\n    }\n    this.enterEditingImpl();\n    this.fire('editing:entered', e ? { e } : undefined);\n    this._fireSelectionChanged();\n    if (this.canvas) {\n      this.canvas.fire('text:editing:entered', {\n        target: this as unknown as IText,\n        e,\n      });\n      this.canvas.requestRenderAll();\n    }\n  }\n\n  /**\n   * runs the actual logic that enter from editing state, see {@link enterEditing}\n   */\n  enterEditingImpl() {\n    if (this.canvas) {\n      this.canvas.calcOffset();\n      this.canvas.textEditingManager.exitTextEditing();\n    }\n\n    this.isEditing = true;\n\n    this.initHiddenTextarea();\n    this.hiddenTextarea!.focus();\n    this.hiddenTextarea!.value = this.text;\n    this._updateTextarea();\n    this._saveEditingProps();\n    this._setEditingProps();\n    this._textBeforeEdit = this.text;\n\n    this._tick();\n  }\n\n  /**\n   * called by {@link Canvas#textEditingManager}\n   */\n  updateSelectionOnMouseMove(e: TPointerEvent) {\n    if (this.getActiveControl()) {\n      return;\n    }\n\n    const el = this.hiddenTextarea!;\n    // regain focus\n    getDocumentFromElement(el).activeElement !== el && el.focus();\n\n    const newSelectionStart = this.getSelectionStartFromPointer(e),\n      currentStart = this.selectionStart,\n      currentEnd = this.selectionEnd;\n    if (\n      (newSelectionStart !== this.__selectionStartOnMouseDown ||\n        currentStart === currentEnd) &&\n      (currentStart === newSelectionStart || currentEnd === newSelectionStart)\n    ) {\n      return;\n    }\n    if (newSelectionStart > this.__selectionStartOnMouseDown) {\n      this.selectionStart = this.__selectionStartOnMouseDown;\n      this.selectionEnd = newSelectionStart;\n    } else {\n      this.selectionStart = newSelectionStart;\n      this.selectionEnd = this.__selectionStartOnMouseDown;\n    }\n    if (\n      this.selectionStart !== currentStart ||\n      this.selectionEnd !== currentEnd\n    ) {\n      this._fireSelectionChanged();\n      this._updateTextarea();\n      this.renderCursorOrSelection();\n    }\n  }\n\n  /**\n   * @private\n   */\n  _setEditingProps() {\n    this.hoverCursor = 'text';\n\n    if (this.canvas) {\n      this.canvas.defaultCursor = this.canvas.moveCursor = 'text';\n    }\n\n    this.borderColor = this.editingBorderColor;\n    this.hasControls = this.selectable = false;\n    this.lockMovementX = this.lockMovementY = true;\n  }\n\n  /**\n   * convert from textarea to grapheme indexes\n   */\n  fromStringToGraphemeSelection(start: number, end: number, text: string) {\n    const smallerTextStart = text.slice(0, start),\n      graphemeStart = this.graphemeSplit(smallerTextStart).length;\n    if (start === end) {\n      return { selectionStart: graphemeStart, selectionEnd: graphemeStart };\n    }\n    const smallerTextEnd = text.slice(start, end),\n      graphemeEnd = this.graphemeSplit(smallerTextEnd).length;\n    return {\n      selectionStart: graphemeStart,\n      selectionEnd: graphemeStart + graphemeEnd,\n    };\n  }\n\n  /**\n   * convert from fabric to textarea values\n   */\n  fromGraphemeToStringSelection(\n    start: number,\n    end: number,\n    graphemes: string[],\n  ) {\n    const smallerTextStart = graphemes.slice(0, start),\n      graphemeStart = smallerTextStart.join('').length;\n    if (start === end) {\n      return { selectionStart: graphemeStart, selectionEnd: graphemeStart };\n    }\n    const smallerTextEnd = graphemes.slice(start, end),\n      graphemeEnd = smallerTextEnd.join('').length;\n    return {\n      selectionStart: graphemeStart,\n      selectionEnd: graphemeStart + graphemeEnd,\n    };\n  }\n\n  /**\n   * @private\n   */\n  _updateTextarea() {\n    this.cursorOffsetCache = {};\n    if (!this.hiddenTextarea) {\n      return;\n    }\n    if (!this.inCompositionMode) {\n      const newSelection = this.fromGraphemeToStringSelection(\n        this.selectionStart,\n        this.selectionEnd,\n        this._text,\n      );\n      this.hiddenTextarea.selectionStart = newSelection.selectionStart;\n      this.hiddenTextarea.selectionEnd = newSelection.selectionEnd;\n    }\n    this.updateTextareaPosition();\n  }\n\n  /**\n   * This function updates the text value from the hidden textarea and recalculates the text bounding box\n   * size and position.\n   * It is called by fabricJS internals, do not use it directly.\n   * @private\n   */\n  updateFromTextArea() {\n    const { hiddenTextarea, direction, textAlign, inCompositionMode } = this;\n    if (!hiddenTextarea) {\n      return;\n    }\n    // we want to anchor the textarea position depending on text alignment\n    // or in case of text justify depending on ltr/rtl direction.\n    // this.textAlign.replace('justify-', '') leverages the fact that our textAlign values all contain the word left/right/center,\n    // that match the originX values.\n    const anchorX: TOriginX =\n      textAlign !== JUSTIFY\n        ? (textAlign.replace('justify-', '') as TOriginX)\n        : direction === LTR\n          ? LEFT\n          : RIGHT;\n    const originalPosition = this.getPositionByOrigin(anchorX, 'top');\n    this.cursorOffsetCache = {};\n    this.text = hiddenTextarea.value;\n    this.set('dirty', true);\n    this.initDimensions();\n    this.setPositionByOrigin(originalPosition, anchorX, 'top');\n    this.setCoords();\n    const newSelection = this.fromStringToGraphemeSelection(\n      hiddenTextarea.selectionStart,\n      hiddenTextarea.selectionEnd,\n      hiddenTextarea.value,\n    );\n    this.selectionEnd = this.selectionStart = newSelection.selectionEnd;\n    if (!inCompositionMode) {\n      this.selectionStart = newSelection.selectionStart;\n    }\n    this.updateTextareaPosition();\n  }\n\n  /**\n   * @private\n   */\n  updateTextareaPosition() {\n    if (this.selectionStart === this.selectionEnd) {\n      const style = this._calcTextareaPosition();\n      this.hiddenTextarea!.style.left = style.left;\n      this.hiddenTextarea!.style.top = style.top;\n    }\n  }\n\n  /**\n   * @private\n   * @return {Object} style contains style for hiddenTextarea\n   */\n  _calcTextareaPosition() {\n    if (!this.canvas) {\n      return { left: '1px', top: '1px' };\n    }\n    const desiredPosition = this.inCompositionMode\n        ? this.compositionStart\n        : this.selectionStart,\n      boundaries = this._getCursorBoundaries(desiredPosition),\n      cursorLocation = this.get2DCursorLocation(desiredPosition),\n      lineIndex = cursorLocation.lineIndex,\n      charIndex = cursorLocation.charIndex,\n      charHeight =\n        this.getValueOfPropertyAt(lineIndex, charIndex, 'fontSize') *\n        this.lineHeight,\n      leftOffset = boundaries.leftOffset,\n      retinaScaling = this.getCanvasRetinaScaling(),\n      upperCanvas = this.canvas.upperCanvasEl,\n      upperCanvasWidth = upperCanvas.width / retinaScaling,\n      upperCanvasHeight = upperCanvas.height / retinaScaling,\n      maxWidth = upperCanvasWidth - charHeight,\n      maxHeight = upperCanvasHeight - charHeight;\n\n    const p = new Point(\n      boundaries.left + leftOffset,\n      boundaries.top + boundaries.topOffset + charHeight,\n    )\n      .transform(this.calcTransformMatrix())\n      .transform(this.canvas.viewportTransform)\n      .multiply(\n        new Point(\n          upperCanvas.clientWidth / upperCanvasWidth,\n          upperCanvas.clientHeight / upperCanvasHeight,\n        ),\n      );\n\n    if (p.x < 0) {\n      p.x = 0;\n    }\n    if (p.x > maxWidth) {\n      p.x = maxWidth;\n    }\n    if (p.y < 0) {\n      p.y = 0;\n    }\n    if (p.y > maxHeight) {\n      p.y = maxHeight;\n    }\n\n    // add canvas offset on document\n    p.x += this.canvas._offset.left;\n    p.y += this.canvas._offset.top;\n\n    return {\n      left: `${p.x}px`,\n      top: `${p.y}px`,\n      fontSize: `${charHeight}px`,\n      charHeight: charHeight,\n    };\n  }\n\n  /**\n   * @private\n   */\n  _saveEditingProps() {\n    this._savedProps = {\n      hasControls: this.hasControls,\n      borderColor: this.borderColor,\n      lockMovementX: this.lockMovementX,\n      lockMovementY: this.lockMovementY,\n      hoverCursor: this.hoverCursor,\n      selectable: this.selectable,\n      defaultCursor: this.canvas && this.canvas.defaultCursor,\n      moveCursor: this.canvas && this.canvas.moveCursor,\n    };\n  }\n\n  /**\n   * @private\n   */\n  _restoreEditingProps() {\n    if (!this._savedProps) {\n      return;\n    }\n\n    this.hoverCursor = this._savedProps.hoverCursor;\n    this.hasControls = this._savedProps.hasControls;\n    this.borderColor = this._savedProps.borderColor;\n    this.selectable = this._savedProps.selectable;\n    this.lockMovementX = this._savedProps.lockMovementX;\n    this.lockMovementY = this._savedProps.lockMovementY;\n\n    if (this.canvas) {\n      this.canvas.defaultCursor =\n        this._savedProps.defaultCursor || this.canvas.defaultCursor;\n      this.canvas.moveCursor =\n        this._savedProps.moveCursor || this.canvas.moveCursor;\n    }\n\n    delete this._savedProps;\n  }\n\n  /**\n   * runs the actual logic that exits from editing state, see {@link exitEditing}\n   * But it does not fire events\n   */\n  exitEditingImpl() {\n    const hiddenTextarea = this.hiddenTextarea;\n    this.selected = false;\n    this.isEditing = false;\n\n    if (hiddenTextarea) {\n      hiddenTextarea.blur && hiddenTextarea.blur();\n      hiddenTextarea.parentNode &&\n        hiddenTextarea.parentNode.removeChild(hiddenTextarea);\n    }\n    this.hiddenTextarea = null;\n    this.abortCursorAnimation();\n    this.selectionStart !== this.selectionEnd && this.clearContextTop();\n    this.selectionEnd = this.selectionStart;\n    this._restoreEditingProps();\n    if (this._forceClearCache) {\n      this.initDimensions();\n      this.setCoords();\n    }\n  }\n\n  /**\n   * Exits from editing state and fires relevant events\n   */\n  exitEditing() {\n    const isTextChanged = this._textBeforeEdit !== this.text;\n    this.exitEditingImpl();\n\n    this.fire('editing:exited');\n    isTextChanged && this.fire(MODIFIED);\n    if (this.canvas) {\n      this.canvas.fire('text:editing:exited', {\n        target: this as unknown as IText,\n      });\n      // todo: evaluate add an action to this event\n      isTextChanged && this.canvas.fire('object:modified', { target: this });\n    }\n    return this;\n  }\n\n  /**\n   * @private\n   */\n  _removeExtraneousStyles() {\n    for (const prop in this.styles) {\n      if (!this._textLines[prop as unknown as number]) {\n        delete this.styles[prop];\n      }\n    }\n  }\n\n  /**\n   * remove and reflow a style block from start to end.\n   * @param {Number} start linear start position for removal (included in removal)\n   * @param {Number} end linear end position for removal ( excluded from removal )\n   */\n  removeStyleFromTo(start: number, end: number) {\n    const { lineIndex: lineStart, charIndex: charStart } =\n        this.get2DCursorLocation(start, true),\n      { lineIndex: lineEnd, charIndex: charEnd } = this.get2DCursorLocation(\n        end,\n        true,\n      );\n    if (lineStart !== lineEnd) {\n      // step1 remove the trailing of lineStart\n      if (this.styles[lineStart]) {\n        for (\n          let i = charStart;\n          i < this._unwrappedTextLines[lineStart].length;\n          i++\n        ) {\n          delete this.styles[lineStart][i];\n        }\n      }\n      // step2 move the trailing of lineEnd to lineStart if needed\n      if (this.styles[lineEnd]) {\n        for (\n          let i = charEnd;\n          i < this._unwrappedTextLines[lineEnd].length;\n          i++\n        ) {\n          const styleObj = this.styles[lineEnd][i];\n          if (styleObj) {\n            this.styles[lineStart] || (this.styles[lineStart] = {});\n            this.styles[lineStart][charStart + i - charEnd] = styleObj;\n          }\n        }\n      }\n      // step3 detects lines will be completely removed.\n      for (let i = lineStart + 1; i <= lineEnd; i++) {\n        delete this.styles[i];\n      }\n      // step4 shift remaining lines.\n      this.shiftLineStyles(lineEnd, lineStart - lineEnd);\n    } else {\n      // remove and shift left on the same line\n      if (this.styles[lineStart]) {\n        const styleObj = this.styles[lineStart];\n        const diff = charEnd - charStart;\n        for (let i = charStart; i < charEnd; i++) {\n          delete styleObj[i];\n        }\n        for (const char in this.styles[lineStart]) {\n          const numericChar = parseInt(char, 10);\n          if (numericChar >= charEnd) {\n            styleObj[numericChar - diff] = styleObj[char];\n            delete styleObj[char];\n          }\n        }\n      }\n    }\n  }\n\n  /**\n   * Shifts line styles up or down\n   * @param {Number} lineIndex Index of a line\n   * @param {Number} offset Can any number?\n   */\n  shiftLineStyles(lineIndex: number, offset: number) {\n    const clonedStyles = Object.assign({}, this.styles);\n    for (const line in this.styles) {\n      const numericLine = parseInt(line, 10);\n      if (numericLine > lineIndex) {\n        this.styles[numericLine + offset] = clonedStyles[numericLine];\n        if (!clonedStyles[numericLine - offset]) {\n          delete this.styles[numericLine];\n        }\n      }\n    }\n  }\n\n  /**\n   * Handle insertion of more consecutive style lines for when one or more\n   * newlines gets added to the text. Since current style needs to be shifted\n   * first we shift the current style of the number lines needed, then we add\n   * new lines from the last to the first.\n   * @param {Number} lineIndex Index of a line\n   * @param {Number} charIndex Index of a char\n   * @param {Number} qty number of lines to add\n   * @param {Array} copiedStyle Array of objects styles\n   */\n  insertNewlineStyleObject(\n    lineIndex: number,\n    charIndex: number,\n    qty: number,\n    copiedStyle?: { [index: number]: TextStyleDeclaration },\n  ) {\n    const newLineStyles: { [index: number]: TextStyleDeclaration } = {};\n    const originalLineLength = this._unwrappedTextLines[lineIndex].length;\n    const isEndOfLine = originalLineLength === charIndex;\n\n    let someStyleIsCarryingOver = false;\n    qty || (qty = 1);\n    this.shiftLineStyles(lineIndex, qty);\n    const currentCharStyle = this.styles[lineIndex]\n      ? this.styles[lineIndex][charIndex === 0 ? charIndex : charIndex - 1]\n      : undefined;\n\n    // we clone styles of all chars\n    // after cursor onto the current line\n    for (const index in this.styles[lineIndex]) {\n      const numIndex = parseInt(index, 10);\n      if (numIndex >= charIndex) {\n        someStyleIsCarryingOver = true;\n        newLineStyles[numIndex - charIndex] = this.styles[lineIndex][index];\n        // remove lines from the previous line since they're on a new line now\n        if (!(isEndOfLine && charIndex === 0)) {\n          delete this.styles[lineIndex][index];\n        }\n      }\n    }\n    let styleCarriedOver = false;\n    if (someStyleIsCarryingOver && !isEndOfLine) {\n      // if is end of line, the extra style we copied\n      // is probably not something we want\n      this.styles[lineIndex + qty] = newLineStyles;\n      styleCarriedOver = true;\n    }\n    if (styleCarriedOver || originalLineLength > charIndex) {\n      // skip the last line of since we already prepared it.\n      // or contains text without style that we don't want to style\n      // just because it changed lines\n      qty--;\n    }\n    // for the all the lines or all the other lines\n    // we clone current char style onto the next (otherwise empty) line\n    while (qty > 0) {\n      if (copiedStyle && copiedStyle[qty - 1]) {\n        this.styles[lineIndex + qty] = {\n          0: { ...copiedStyle[qty - 1] },\n        };\n      } else if (currentCharStyle) {\n        this.styles[lineIndex + qty] = {\n          0: { ...currentCharStyle },\n        };\n      } else {\n        delete this.styles[lineIndex + qty];\n      }\n      qty--;\n    }\n    this._forceClearCache = true;\n  }\n\n  /**\n   * Inserts style object for a given line/char index\n   * @param {Number} lineIndex Index of a line\n   * @param {Number} charIndex Index of a char\n   * @param {Number} quantity number Style object to insert, if given\n   * @param {Array} copiedStyle array of style objects\n   */\n  insertCharStyleObject(\n    lineIndex: number,\n    charIndex: number,\n    quantity: number,\n    copiedStyle?: TextStyleDeclaration[],\n  ) {\n    if (!this.styles) {\n      this.styles = {};\n    }\n    const currentLineStyles = this.styles[lineIndex],\n      currentLineStylesCloned = currentLineStyles\n        ? { ...currentLineStyles }\n        : {};\n\n    quantity || (quantity = 1);\n    // shift all char styles by quantity forward\n    // 0,1,2,3 -> (charIndex=2) -> 0,1,3,4 -> (insert 2) -> 0,1,2,3,4\n    for (const index in currentLineStylesCloned) {\n      const numericIndex = parseInt(index, 10);\n      if (numericIndex >= charIndex) {\n        currentLineStyles[numericIndex + quantity] =\n          currentLineStylesCloned[numericIndex];\n        // only delete the style if there was nothing moved there\n        if (!currentLineStylesCloned[numericIndex - quantity]) {\n          delete currentLineStyles[numericIndex];\n        }\n      }\n    }\n    this._forceClearCache = true;\n    if (copiedStyle) {\n      while (quantity--) {\n        if (!Object.keys(copiedStyle[quantity]).length) {\n          continue;\n        }\n        if (!this.styles[lineIndex]) {\n          this.styles[lineIndex] = {};\n        }\n        this.styles[lineIndex][charIndex + quantity] = {\n          ...copiedStyle[quantity],\n        };\n      }\n      return;\n    }\n    if (!currentLineStyles) {\n      return;\n    }\n    const newStyle = currentLineStyles[charIndex ? charIndex - 1 : 1];\n    while (newStyle && quantity--) {\n      this.styles[lineIndex][charIndex + quantity] = { ...newStyle };\n    }\n  }\n\n  /**\n   * Inserts style object(s)\n   * @param {Array} insertedText Characters at the location where style is inserted\n   * @param {Number} start cursor index for inserting style\n   * @param {Array} [copiedStyle] array of style objects to insert.\n   */\n  insertNewStyleBlock(\n    insertedText: string[],\n    start: number,\n    copiedStyle?: TextStyleDeclaration[],\n  ) {\n    const cursorLoc = this.get2DCursorLocation(start, true),\n      addedLines = [0];\n    let linesLength = 0;\n    // get an array of how many char per lines are being added.\n    for (let i = 0; i < insertedText.length; i++) {\n      if (insertedText[i] === '\\n') {\n        linesLength++;\n        addedLines[linesLength] = 0;\n      } else {\n        addedLines[linesLength]++;\n      }\n    }\n    // for the first line copy the style from the current char position.\n    if (addedLines[0] > 0) {\n      this.insertCharStyleObject(\n        cursorLoc.lineIndex,\n        cursorLoc.charIndex,\n        addedLines[0],\n        copiedStyle,\n      );\n      copiedStyle = copiedStyle && copiedStyle.slice(addedLines[0] + 1);\n    }\n    linesLength &&\n      this.insertNewlineStyleObject(\n        cursorLoc.lineIndex,\n        cursorLoc.charIndex + addedLines[0],\n        linesLength,\n      );\n    let i;\n    for (i = 1; i < linesLength; i++) {\n      if (addedLines[i] > 0) {\n        this.insertCharStyleObject(\n          cursorLoc.lineIndex + i,\n          0,\n          addedLines[i],\n          copiedStyle,\n        );\n      } else if (copiedStyle) {\n        // this test is required in order to close #6841\n        // when a pasted buffer begins with a newline then\n        // this.styles[cursorLoc.lineIndex + i] and copiedStyle[0]\n        // may be undefined for some reason\n        if (this.styles[cursorLoc.lineIndex + i] && copiedStyle[0]) {\n          this.styles[cursorLoc.lineIndex + i][0] = copiedStyle[0];\n        }\n      }\n      copiedStyle = copiedStyle && copiedStyle.slice(addedLines[i] + 1);\n    }\n    if (addedLines[i] > 0) {\n      this.insertCharStyleObject(\n        cursorLoc.lineIndex + i,\n        0,\n        addedLines[i],\n        copiedStyle,\n      );\n    }\n  }\n\n  /**\n   * Removes characters from start/end\n   * start/end ar per grapheme position in _text array.\n   *\n   * @param {Number} start\n   * @param {Number} end default to start + 1\n   */\n  removeChars(start: number, end: number = start + 1) {\n    this.removeStyleFromTo(start, end);\n    this._text.splice(start, end - start);\n    this.text = this._text.join('');\n    this.set('dirty', true);\n    this.initDimensions();\n    this.setCoords();\n    this._removeExtraneousStyles();\n  }\n\n  /**\n   * insert characters at start position, before start position.\n   * start  equal 1 it means the text get inserted between actual grapheme 0 and 1\n   * if style array is provided, it must be as the same length of text in graphemes\n   * if end is provided and is bigger than start, old text is replaced.\n   * start/end ar per grapheme position in _text array.\n   *\n   * @param {String} text text to insert\n   * @param {Array} style array of style objects\n   * @param {Number} start\n   * @param {Number} end default to start + 1\n   */\n  insertChars(\n    text: string,\n    style: TextStyleDeclaration[] | undefined,\n    start: number,\n    end: number = start,\n  ) {\n    if (end > start) {\n      this.removeStyleFromTo(start, end);\n    }\n    const graphemes = this.graphemeSplit(text);\n    this.insertNewStyleBlock(graphemes, start, style);\n    this._text = [\n      ...this._text.slice(0, start),\n      ...graphemes,\n      ...this._text.slice(end),\n    ];\n    this.text = this._text.join('');\n    this.set('dirty', true);\n    this.initDimensions();\n    this.setCoords();\n    this._removeExtraneousStyles();\n  }\n\n  /**\n   * Set the selectionStart and selectionEnd according to the new position of cursor\n   * mimic the key - mouse navigation when shift is pressed.\n   */\n  setSelectionStartEndWithShift(\n    start: number,\n    end: number,\n    newSelection: number,\n  ) {\n    if (newSelection <= start) {\n      if (end === start) {\n        this._selectionDirection = LEFT;\n      } else if (this._selectionDirection === RIGHT) {\n        this._selectionDirection = LEFT;\n        this.selectionEnd = start;\n      }\n      this.selectionStart = newSelection;\n    } else if (newSelection > start && newSelection < end) {\n      if (this._selectionDirection === RIGHT) {\n        this.selectionEnd = newSelection;\n      } else {\n        this.selectionStart = newSelection;\n      }\n    } else {\n      // newSelection is > selection start and end\n      if (end === start) {\n        this._selectionDirection = RIGHT;\n      } else if (this._selectionDirection === LEFT) {\n        this._selectionDirection = RIGHT;\n        this.selectionStart = end;\n      }\n      this.selectionEnd = newSelection;\n    }\n  }\n}\n"],"names":["reNonWord","ITextBehavior","FabricText","constructor","super","arguments","_defineProperty","initBehavior","this","_tick","bind","_onTickComplete","updateSelectionOnMouseMove","onDeselect","options","isEditing","exitEditing","selected","_animateCursor","_ref","toValue","duration","delay","onComplete","animate","startValue","_currentCursorOpacity","endValue","abort","canvas","selectionStart","selectionEnd","onChange","value","renderCursorOrSelection","_currentTickState","cursorDuration","Math","max","_this$_currentTickCom","_currentTickCompleteState","initDelayedCursor","restart","abortCursorAnimation","cursorDelay","shouldClear","forEach","cursorAnimation","isDone","clearContextTop","restartCursorIfNeeded","some","selectAll","_text","length","_fireSelectionChanged","_updateTextarea","cmdAll","getSelectedText","slice","join","findWordBoundaryLeft","startFrom","offset","index","_reSpace","test","findWordBoundaryRight","findLineBoundaryLeft","findLineBoundaryRight","searchWordBoundary","direction","text","reNewline","_char","selectWord","newSelectionStart","newSelectionEnd","selectLine","enterEditing","e","editable","enterEditingImpl","fire","undefined","target","requestRenderAll","calcOffset","textEditingManager","exitTextEditing","initHiddenTextarea","hiddenTextarea","focus","_saveEditingProps","_setEditingProps","_textBeforeEdit","getActiveControl","el","getDocumentFromElement","activeElement","getSelectionStartFromPointer","currentStart","currentEnd","__selectionStartOnMouseDown","hoverCursor","defaultCursor","moveCursor","borderColor","editingBorderColor","hasControls","selectable","lockMovementX","lockMovementY","fromStringToGraphemeSelection","start","end","smallerTextStart","graphemeStart","graphemeSplit","smallerTextEnd","fromGraphemeToStringSelection","graphemes","cursorOffsetCache","inCompositionMode","newSelection","updateTextareaPosition","updateFromTextArea","textAlign","anchorX","JUSTIFY","replace","LTR","LEFT","RIGHT","originalPosition","getPositionByOrigin","set","initDimensions","setPositionByOrigin","setCoords","style","_calcTextareaPosition","left","top","desiredPosition","compositionStart","boundaries","_getCursorBoundaries","cursorLocation","get2DCursorLocation","lineIndex","charIndex","charHeight","getValueOfPropertyAt","lineHeight","leftOffset","retinaScaling","getCanvasRetinaScaling","upperCanvas","upperCanvasEl","upperCanvasWidth","width","upperCanvasHeight","height","maxWidth","maxHeight","p","Point","topOffset","transform","calcTransformMatrix","viewportTransform","multiply","clientWidth","clientHeight","x","y","_offset","fontSize","_savedProps","_restoreEditingProps","exitEditingImpl","blur","parentNode","removeChild","_forceClearCache","isTextChanged","MODIFIED","_removeExtraneousStyles","prop","styles","_textLines","removeStyleFromTo","lineStart","charStart","lineEnd","charEnd","i","_unwrappedTextLines","styleObj","shiftLineStyles","diff","char","numericChar","parseInt","clonedStyles","Object","assign","line","numericLine","insertNewlineStyleObject","qty","copiedStyle","newLineStyles","originalLineLength","isEndOfLine","someStyleIsCarryingOver","currentCharStyle","numIndex","styleCarriedOver","insertCharStyleObject","quantity","currentLineStyles","currentLineStylesCloned","numericIndex","keys","newStyle","insertNewStyleBlock","insertedText","cursorLoc","addedLines","linesLength","removeChars","splice","insertChars","setSelectionStartEndWithShift","_selectionDirection"],"mappings":"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"}