{"version":3,"file":"ITextClickBehavior.min.mjs","sources":["../../../../src/shapes/IText/ITextClickBehavior.ts"],"sourcesContent":["import type { TPointerEvent, TPointerEventInfo } from '../../EventTypeDefs';\nimport type { XY } from '../../Point';\nimport { Point } from '../../Point';\nimport { stopEvent } from '../../util/dom_event';\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';\n\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  private declare __lastSelected: boolean;\n  private declare __lastClickTime: number;\n  private declare __lastLastClickTime: number;\n  private declare __lastPointer: XY | Record<string, never>;\n  private declare __newClickTime: number;\n\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('mousedown:before', this._mouseDownHandlerBefore);\n    this.on('mouseup', this.mouseUpHandler);\n    this.on('mousedblclick', this.doubleClickHandler);\n    this.on('tripleclick', this.tripleClickHandler);\n\n    // Initializes \"dbclick\" event handler\n    this.__lastClickTime = +new Date();\n    // for triple click\n    this.__lastLastClickTime = +new Date();\n    this.__lastPointer = {};\n    this.on('mousedown', this.onMouseDown);\n\n    // @ts-expect-error in reality it is an IText instance\n    this.draggableTextDelegate = new DraggableTextDelegate(this);\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 event handler to simulate triple click\n   * @private\n   */\n  onMouseDown(options: TPointerEventInfo) {\n    if (!this.canvas) {\n      return;\n    }\n    this.__newClickTime = +new Date();\n    const newPointer = options.pointer;\n    if (this.isTripleClick(newPointer)) {\n      this.fire('tripleclick', options);\n      stopEvent(options.e);\n    }\n    this.__lastLastClickTime = this.__lastClickTime;\n    this.__lastClickTime = this.__newClickTime;\n    this.__lastPointer = newPointer;\n    this.__lastSelected = this.selected && !this.getActiveControl();\n  }\n\n  isTripleClick(newPointer: XY) {\n    return (\n      this.__newClickTime - this.__lastClickTime < 500 &&\n      this.__lastClickTime - this.__lastLastClickTime < 500 &&\n      this.__lastPointer.x === newPointer.x &&\n      this.__lastPointer.y === newPointer.y\n    );\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  }\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  }\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 }: TPointerEventInfo) {\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 (this.selected) {\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  }\n\n  /**\n   * Default event handler for the basic functionalities needed on mousedown:before\n   * can be overridden to do something different.\n   * Scope of this implementation is: verify the object is already selected when mousing down\n   */\n  _mouseDownHandlerBefore({ e }: TPointerEventInfo) {\n    if (!this.canvas || !this.editable || notALeftClick(e)) {\n      return;\n    }\n    // we want to avoid that an object that was selected and then becomes unselectable,\n    // may trigger editing mode in some way.\n    this.selected = this === this.canvas._activeObject;\n  }\n\n  /**\n   * standard handler for mouse up, overridable\n   * @private\n   */\n  mouseUpHandler({ e, transform }: TPointerEventInfo) {\n    const didDrag = this.draggableTextDelegate.end(e);\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    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.__lastSelected && !this.getActiveControl()) {\n      this.selected = false;\n      this.__lastSelected = false;\n      this.enterEditing(e);\n      if (this.selectionStart === this.selectionEnd) {\n        this.initDelayedCursor(true);\n      } else {\n        this.renderCursorOrSelection();\n      }\n    } else {\n      this.selected = true;\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"],"names":["notALeftClick","e","button","ITextClickBehavior","ITextKeyBehavior","constructor","super","arguments","_defineProperty","this","initBehavior","on","_mouseDownHandler","_mouseDownHandlerBefore","mouseUpHandler","doubleClickHandler","tripleClickHandler","__lastClickTime","Date","__lastLastClickTime","__lastPointer","onMouseDown","draggableTextDelegate","DraggableTextDelegate","shouldStartDragging","isActive","onDragStart","canDrop","options","canvas","__newClickTime","newPointer","pointer","isTripleClick","fire","stopEvent","__lastSelected","selected","getActiveControl","x","y","isEditing","selectWord","getSelectionStartFromPointer","selectLine","_ref","editable","start","textEditingManager","register","inCompositionMode","setCursorByClick","__selectionStartOnMouseDown","selectionStart","selectionEnd","abortCursorAnimation","renderCursorOrSelection","_ref2","_activeObject","_ref3","transform","didDrag","end","unregister","activeObject","group","interactive","actionPerformed","enterEditing","initDelayedCursor","newSelection","shiftKey","setSelectionStartEndWithShift","_fireSelectionChanged","_updateTextarea","mouseOffset","getScenePoint","invertTransform","calcTransformMatrix","add","Point","_getLeftOffset","_getTopOffset","height","charIndex","lineIndex","i","_textLines","length","getHeightOfLine","missingNewlineOffset","width","Math","abs","_getLineLeftOffset","charLength","chars","__charBounds","j","widthAfter","kernedWidth","min","flipX","_text"],"mappings":"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"}