{"version":3,"file":"ITextClickBehavior.mjs","names":[],"sources":["../../../../src/shapes/IText/ITextClickBehavior.ts"],"sourcesContent":["import type {\n  ObjectPointerEvents,\n  TPointerEvent,\n  TPointerEventInfo,\n} from '../../EventTypeDefs';\nimport { Point } from '../../Point';\nimport { invertTransform } from '../../util/misc/matrix';\nimport { DraggableTextDelegate } from './DraggableTextDelegate';\nimport type { ITextEvents } from './ITextBehavior';\nimport { ITextKeyBehavior } from './ITextKeyBehavior';\nimport type { TOptions } from '../../typedefs';\nimport type { TextProps, SerializedTextProps } from '../Text/Text';\nimport type { IText } from './IText';\n/**\n * `LEFT_CLICK === 0`\n */\nconst notALeftClick = (e: Event) => !!(e as MouseEvent).button;\n\nexport abstract class ITextClickBehavior<\n  Props extends TOptions<TextProps> = Partial<TextProps>,\n  SProps extends SerializedTextProps = SerializedTextProps,\n  EventSpec extends ITextEvents = ITextEvents,\n> extends ITextKeyBehavior<Props, SProps, EventSpec> {\n  protected draggableTextDelegate: DraggableTextDelegate;\n\n  initBehavior() {\n    // Initializes event handlers related to cursor or selection\n    this.on('mousedown', this._mouseDownHandler);\n    this.on('mouseup', this.mouseUpHandler);\n    this.on('mousedblclick', this.doubleClickHandler);\n    this.on('mousetripleclick', this.tripleClickHandler);\n\n    this.draggableTextDelegate = new DraggableTextDelegate(\n      this as unknown as IText,\n    );\n\n    super.initBehavior();\n  }\n\n  /**\n   * If this method returns true a mouse move operation over a text selection\n   * will not prevent the native mouse event allowing the browser to start a drag operation.\n   * shouldStartDragging can be read 'do not prevent default for mouse move event'\n   * To prevent drag and drop between objects both shouldStartDragging and onDragStart should return false\n   * @returns\n   */\n  shouldStartDragging() {\n    return this.draggableTextDelegate.isActive();\n  }\n\n  /**\n   * @public override this method to control whether instance should/shouldn't become a drag source,\n   * @see also {@link DraggableTextDelegate#isActive}\n   * To prevent drag and drop between objects both shouldStartDragging and onDragStart should return false\n   * @returns {boolean} should handle event\n   */\n  onDragStart(e: DragEvent) {\n    return this.draggableTextDelegate.onDragStart(e);\n  }\n\n  /**\n   * @public override this method to control whether instance should/shouldn't become a drop target\n   */\n  canDrop(e: DragEvent) {\n    return this.draggableTextDelegate.canDrop(e);\n  }\n\n  /**\n   * Default handler for double click, select a word\n   */\n  doubleClickHandler(options: TPointerEventInfo) {\n    if (!this.isEditing) {\n      return;\n    }\n    this.selectWord(this.getSelectionStartFromPointer(options.e));\n    this.renderCursorOrSelection();\n  }\n\n  /**\n   * Default handler for triple click, select a line\n   */\n  tripleClickHandler(options: TPointerEventInfo) {\n    if (!this.isEditing) {\n      return;\n    }\n    this.selectLine(this.getSelectionStartFromPointer(options.e));\n    this.renderCursorOrSelection();\n  }\n\n  /**\n   * Default event handler for the basic functionalities needed on _mouseDown\n   * can be overridden to do something different.\n   * Scope of this implementation is: find the click position, set selectionStart\n   * find selectionEnd, initialize the drawing of either cursor or selection area\n   * initializing a mousedDown on a text area will cancel fabricjs knowledge of\n   * current compositionMode. It will be set to false.\n   */\n  _mouseDownHandler({ e, alreadySelected }: ObjectPointerEvents['mousedown']) {\n    if (\n      !this.canvas ||\n      !this.editable ||\n      notALeftClick(e) ||\n      this.getActiveControl()\n    ) {\n      return;\n    }\n\n    if (this.draggableTextDelegate.start(e)) {\n      return;\n    }\n\n    this.canvas.textEditingManager.register(this);\n\n    if (alreadySelected) {\n      this.inCompositionMode = false;\n      this.setCursorByClick(e);\n    }\n\n    if (this.isEditing) {\n      this.__selectionStartOnMouseDown = this.selectionStart;\n      if (this.selectionStart === this.selectionEnd) {\n        this.abortCursorAnimation();\n      }\n      this.renderCursorOrSelection();\n    }\n    this.selected ||= alreadySelected || this.isEditing;\n  }\n\n  /**\n   * standard handler for mouse up, overridable\n   * @private\n   */\n  mouseUpHandler({ e, transform }: ObjectPointerEvents['mouseup']) {\n    const didDrag = this.draggableTextDelegate.end(e);\n\n    if (this.canvas) {\n      this.canvas.textEditingManager.unregister(this);\n\n      const activeObject = this.canvas._activeObject;\n      if (activeObject && activeObject !== this) {\n        // avoid running this logic when there is an active object\n        // this because is possible with shift click and fast clicks,\n        // to rapidly deselect and reselect this object and trigger an enterEdit\n        return;\n      }\n    }\n\n    if (\n      !this.editable ||\n      (this.group && !this.group.interactive) ||\n      (transform && transform.actionPerformed) ||\n      notALeftClick(e) ||\n      didDrag\n    ) {\n      return;\n    }\n\n    if (this.selected && !this.getActiveControl()) {\n      this.enterEditing(e);\n      if (this.selectionStart === this.selectionEnd) {\n        this.initDelayedCursor(true);\n      } else {\n        this.renderCursorOrSelection();\n      }\n    }\n  }\n\n  /**\n   * Changes cursor location in a text depending on passed pointer (x/y) object\n   * @param {TPointerEvent} e Event object\n   */\n  setCursorByClick(e: TPointerEvent) {\n    const newSelection = this.getSelectionStartFromPointer(e),\n      start = this.selectionStart,\n      end = this.selectionEnd;\n    if (e.shiftKey) {\n      this.setSelectionStartEndWithShift(start, end, newSelection);\n    } else {\n      this.selectionStart = newSelection;\n      this.selectionEnd = newSelection;\n    }\n    if (this.isEditing) {\n      this._fireSelectionChanged();\n      this._updateTextarea();\n    }\n  }\n\n  /**\n   * Returns index of a character corresponding to where an object was clicked\n   * @param {TPointerEvent} e Event object\n   * @return {Number} Index of a character\n   */\n  getSelectionStartFromPointer(e: TPointerEvent): number {\n    const mouseOffset = this.canvas!.getScenePoint(e)\n      .transform(invertTransform(this.calcTransformMatrix()))\n      .add(new Point(-this._getLeftOffset(), -this._getTopOffset()));\n    let height = 0,\n      charIndex = 0,\n      lineIndex = 0;\n\n    for (let i = 0; i < this._textLines.length; i++) {\n      if (height <= mouseOffset.y) {\n        height += this.getHeightOfLine(i);\n        lineIndex = i;\n        if (i > 0) {\n          charIndex +=\n            this._textLines[i - 1].length + this.missingNewlineOffset(i - 1);\n        }\n      } else {\n        break;\n      }\n    }\n    const lineLeftOffset = Math.abs(this._getLineLeftOffset(lineIndex));\n    let width = lineLeftOffset;\n    const charLength = this._textLines[lineIndex].length;\n    const chars = this.__charBounds[lineIndex];\n    for (let j = 0; j < charLength; j++) {\n      // i removed something about flipX here, check.\n      const charWidth = chars[j].kernedWidth;\n      const widthAfter = width + charWidth;\n      if (mouseOffset.x <= widthAfter) {\n        // if the pointer is closer to the end of the char we increment charIndex\n        // in order to position the cursor after the char\n        if (\n          Math.abs(mouseOffset.x - widthAfter) <=\n          Math.abs(mouseOffset.x - width)\n        ) {\n          charIndex++;\n        }\n        break;\n      }\n      width = widthAfter;\n      charIndex++;\n    }\n\n    return Math.min(\n      // if object is horizontally flipped, mirror cursor location from the end\n      this.flipX ? charLength - charIndex : charIndex,\n      this._text.length,\n    );\n  }\n}\n"],"mappings":"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"}