{"version":3,"file":"ITextKeyBehavior.min.mjs","names":[],"sources":["../../../../src/shapes/IText/ITextKeyBehavior.ts"],"sourcesContent":["import { config } from '../../config';\nimport { getFabricDocument, getEnv } from '../../env';\nimport { capValue } from '../../util/misc/capValue';\nimport type { ITextEvents } from './ITextBehavior';\nimport { ITextBehavior } from './ITextBehavior';\nimport type { TKeyMapIText } from './constants';\nimport type { TOptions } from '../../typedefs';\nimport type { TextProps, SerializedTextProps } from '../Text/Text';\nimport { getDocumentFromElement } from '../../util/dom_misc';\nimport { CHANGED, LEFT, RIGHT } from '../../constants';\nimport type { IText } from './IText';\nimport type { TextStyleDeclaration } from '../Text/StyledText';\n\nexport abstract class ITextKeyBehavior<\n  Props extends TOptions<TextProps> = Partial<TextProps>,\n  SProps extends SerializedTextProps = SerializedTextProps,\n  EventSpec extends ITextEvents = ITextEvents,\n> extends ITextBehavior<Props, SProps, EventSpec> {\n  /**\n   * For functionalities on keyDown\n   * Map a special key to a function of the instance/prototype\n   * If you need different behavior for ESC or TAB or arrows, you have to change\n   * this map setting the name of a function that you build on the IText or\n   * your prototype.\n   * the map change will affect all Instances unless you need for only some text Instances\n   * in that case you have to clone this object and assign your Instance.\n   * this.keysMap = Object.assign({}, this.keysMap);\n   * The function must be in IText.prototype.myFunction And will receive event as args[0]\n   */\n  declare keysMap: TKeyMapIText;\n\n  declare keysMapRtl: TKeyMapIText;\n\n  /**\n   * For functionalities on keyUp + ctrl || cmd\n   */\n  declare ctrlKeysMapUp: TKeyMapIText;\n\n  /**\n   * For functionalities on keyDown + ctrl || cmd\n   */\n  declare ctrlKeysMapDown: TKeyMapIText;\n\n  declare hiddenTextarea: HTMLTextAreaElement | null;\n\n  /**\n   * DOM container to append the hiddenTextarea.\n   * An alternative to attaching to the document.body.\n   * Useful to reduce laggish redraw of the full document.body tree and\n   * also with modals event capturing that won't let the textarea take focus.\n   * @type HTMLElement\n   */\n  declare hiddenTextareaContainer?: HTMLElement | null;\n\n  declare private _clickHandlerInitialized: boolean;\n  declare private _copyDone: boolean;\n  declare private fromPaste: boolean;\n\n  /**\n   * Initializes hidden textarea (needed to bring up keyboard in iOS)\n   */\n  initHiddenTextarea() {\n    const doc =\n      (this.canvas && getDocumentFromElement(this.canvas.getElement())) ||\n      getFabricDocument();\n    const textarea = doc.createElement('textarea');\n    Object.entries({\n      autocapitalize: 'off',\n      autocorrect: 'off',\n      autocomplete: 'off',\n      spellcheck: 'false',\n      'data-fabric': 'textarea',\n      wrap: 'off',\n      name: 'fabricTextarea',\n    }).map(([attribute, value]) => textarea.setAttribute(attribute, value));\n    const { top, left, fontSize } = this._calcTextareaPosition();\n    // line-height: 1px; was removed from the style to fix this:\n    // https://bugs.chromium.org/p/chromium/issues/detail?id=870966\n    textarea.style.cssText = `position: absolute; top: ${top}; left: ${left}; z-index: -999; opacity: 0; width: 1px; height: 1px; font-size: 1px; padding-top: ${fontSize};`;\n\n    (this.hiddenTextareaContainer || doc.body).appendChild(textarea);\n\n    Object.entries({\n      blur: 'blur',\n      keydown: 'onKeyDown',\n      keyup: 'onKeyUp',\n      input: 'onInput',\n      copy: 'copy',\n      cut: 'copy',\n      paste: 'paste',\n      compositionstart: 'onCompositionStart',\n      compositionupdate: 'onCompositionUpdate',\n      compositionend: 'onCompositionEnd',\n    } as Record<string, keyof this>).map(([eventName, handler]) =>\n      textarea.addEventListener(\n        eventName,\n        (this[handler] as EventListener).bind(this),\n      ),\n    );\n    this.hiddenTextarea = textarea;\n  }\n\n  /**\n   * Override this method to customize cursor behavior on textbox blur\n   */\n  blur() {\n    this.abortCursorAnimation();\n  }\n\n  /**\n   * Handles keydown event\n   * only used for arrows and combination of modifier keys.\n   * @param {KeyboardEvent} e Event object\n   */\n  onKeyDown(e: KeyboardEvent) {\n    if (!this.isEditing) {\n      return;\n    }\n    const keyMap = this.direction === 'rtl' ? this.keysMapRtl : this.keysMap;\n    if (e.keyCode in keyMap) {\n      (this[keyMap[e.keyCode] as keyof this] as (arg: KeyboardEvent) => void)(\n        e,\n      );\n    } else if (e.keyCode in this.ctrlKeysMapDown && (e.ctrlKey || e.metaKey)) {\n      (\n        this[this.ctrlKeysMapDown[e.keyCode] as keyof this] as (\n          arg: KeyboardEvent,\n        ) => void\n      )(e);\n    } else {\n      return;\n    }\n    e.stopImmediatePropagation();\n    e.preventDefault();\n    if (e.keyCode >= 33 && e.keyCode <= 40) {\n      // if i press an arrow key just update selection\n      this.inCompositionMode = false;\n      this.clearContextTop();\n      this.renderCursorOrSelection();\n    } else {\n      this.canvas && this.canvas.requestRenderAll();\n    }\n  }\n\n  /**\n   * Handles keyup event\n   * We handle KeyUp because ie11 and edge have difficulties copy/pasting\n   * if a copy/cut event fired, keyup is dismissed\n   * @param {KeyboardEvent} e Event object\n   */\n  onKeyUp(e: KeyboardEvent) {\n    if (!this.isEditing || this._copyDone || this.inCompositionMode) {\n      this._copyDone = false;\n      return;\n    }\n    if (e.keyCode in this.ctrlKeysMapUp && (e.ctrlKey || e.metaKey)) {\n      (\n        this[this.ctrlKeysMapUp[e.keyCode] as keyof this] as (\n          arg: KeyboardEvent,\n        ) => void\n      )(e);\n    } else {\n      return;\n    }\n    e.stopImmediatePropagation();\n    e.preventDefault();\n    this.canvas && this.canvas.requestRenderAll();\n  }\n\n  /**\n   * Handles onInput event\n   * @param {Event} e Event object\n   */\n  onInput(this: this & { hiddenTextarea: HTMLTextAreaElement }, e: Event) {\n    const fromPaste = this.fromPaste;\n    const { value, selectionStart, selectionEnd } = this.hiddenTextarea;\n    this.fromPaste = false;\n    e && e.stopPropagation();\n    if (!this.isEditing) {\n      return;\n    }\n    const updateAndFire = () => {\n      this.updateFromTextArea();\n      this.fire(CHANGED);\n      if (this.canvas) {\n        this.canvas.fire('text:changed', { target: this as unknown as IText });\n        this.canvas.requestRenderAll();\n      }\n    };\n    if (this.hiddenTextarea.value === '') {\n      this.styles = {};\n      updateAndFire();\n      return;\n    }\n    // decisions about style changes.\n    const nextText = this._splitTextIntoLines(value).graphemeText,\n      charCount = this._text.length,\n      nextCharCount = nextText.length,\n      _selectionStart = this.selectionStart,\n      _selectionEnd = this.selectionEnd,\n      selection = _selectionStart !== _selectionEnd;\n    let copiedStyle: TextStyleDeclaration[] | undefined,\n      removedText,\n      charDiff = nextCharCount - charCount,\n      removeFrom,\n      removeTo;\n\n    const textareaSelection = this.fromStringToGraphemeSelection(\n      selectionStart,\n      selectionEnd,\n      value,\n    );\n    const backDelete = _selectionStart > textareaSelection.selectionStart;\n\n    if (selection) {\n      removedText = this._text.slice(_selectionStart, _selectionEnd);\n      charDiff += _selectionEnd - _selectionStart;\n    } else if (nextCharCount < charCount) {\n      if (backDelete) {\n        removedText = this._text.slice(_selectionEnd + charDiff, _selectionEnd);\n      } else {\n        removedText = this._text.slice(\n          _selectionStart,\n          _selectionStart - charDiff,\n        );\n      }\n    }\n    const insertedText = nextText.slice(\n      textareaSelection.selectionEnd - charDiff,\n      textareaSelection.selectionEnd,\n    );\n    if (removedText && removedText.length) {\n      if (insertedText.length) {\n        // let's copy some style before deleting.\n        // we want to copy the style before the cursor OR the style at the cursor if selection\n        // is bigger than 0.\n        copiedStyle = this.getSelectionStyles(\n          _selectionStart,\n          _selectionStart + 1,\n          false,\n        );\n        // now duplicate the style one for each inserted text.\n        copiedStyle = insertedText.map(\n          () =>\n            // this return an array of references, but that is fine since we are\n            // copying the style later.\n            copiedStyle![0],\n        );\n      }\n      if (selection) {\n        removeFrom = _selectionStart;\n        removeTo = _selectionEnd;\n      } else if (backDelete) {\n        // detect differences between forwardDelete and backDelete\n        removeFrom = _selectionEnd - removedText.length;\n        removeTo = _selectionEnd;\n      } else {\n        removeFrom = _selectionEnd;\n        removeTo = _selectionEnd + removedText.length;\n      }\n      this.removeStyleFromTo(removeFrom, removeTo);\n    }\n    if (insertedText.length) {\n      const { copyPasteData } = getEnv();\n      if (\n        fromPaste &&\n        insertedText.join('') === copyPasteData.copiedText &&\n        !config.disableStyleCopyPaste\n      ) {\n        copiedStyle = copyPasteData.copiedTextStyle;\n      }\n      this.insertNewStyleBlock(insertedText, _selectionStart, copiedStyle);\n    }\n    updateAndFire();\n  }\n\n  /**\n   * Composition start\n   */\n  onCompositionStart() {\n    this.inCompositionMode = true;\n  }\n\n  /**\n   * Composition end\n   */\n  onCompositionEnd() {\n    this.inCompositionMode = false;\n  }\n\n  onCompositionUpdate({ target }: CompositionEvent) {\n    const { selectionStart, selectionEnd } = target as HTMLTextAreaElement;\n    this.compositionStart = selectionStart;\n    this.compositionEnd = selectionEnd;\n    this.updateTextareaPosition();\n  }\n\n  /**\n   * Copies selected text\n   */\n  copy() {\n    if (this.selectionStart === this.selectionEnd) {\n      //do not cut-copy if no selection\n      return;\n    }\n    const { copyPasteData } = getEnv();\n    copyPasteData.copiedText = this.getSelectedText();\n    if (!config.disableStyleCopyPaste) {\n      copyPasteData.copiedTextStyle = this.getSelectionStyles(\n        this.selectionStart,\n        this.selectionEnd,\n        true,\n      );\n    } else {\n      copyPasteData.copiedTextStyle = undefined;\n    }\n    this._copyDone = true;\n  }\n\n  /**\n   * Pastes text\n   */\n  paste() {\n    this.fromPaste = true;\n  }\n\n  /**\n   * Finds the width in pixels before the cursor on the same line\n   * @private\n   * @param {Number} lineIndex\n   * @param {Number} charIndex\n   * @return {Number} widthBeforeCursor width before cursor\n   */\n  _getWidthBeforeCursor(lineIndex: number, charIndex: number): number {\n    let widthBeforeCursor = this._getLineLeftOffset(lineIndex),\n      bound;\n\n    if (charIndex > 0) {\n      bound = this.__charBounds[lineIndex][charIndex - 1];\n      widthBeforeCursor += bound.left + bound.width;\n    }\n    return widthBeforeCursor;\n  }\n\n  /**\n   * Gets start offset of a selection\n   * @param {KeyboardEvent} e Event object\n   * @param {Boolean} isRight\n   * @return {Number}\n   */\n  getDownCursorOffset(e: KeyboardEvent, isRight: boolean): number {\n    const selectionProp = this._getSelectionForOffset(e, isRight),\n      cursorLocation = this.get2DCursorLocation(selectionProp),\n      lineIndex = cursorLocation.lineIndex;\n    // if on last line, down cursor goes to end of line\n    if (\n      lineIndex === this._textLines.length - 1 ||\n      e.metaKey ||\n      e.keyCode === 34\n    ) {\n      // move to the end of a text\n      return this._text.length - selectionProp;\n    }\n    const charIndex = cursorLocation.charIndex,\n      widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),\n      indexOnOtherLine = this._getIndexOnLine(lineIndex + 1, widthBeforeCursor),\n      textAfterCursor = this._textLines[lineIndex].slice(charIndex);\n    return (\n      textAfterCursor.length +\n      indexOnOtherLine +\n      1 +\n      this.missingNewlineOffset(lineIndex)\n    );\n  }\n\n  /**\n   * private\n   * Helps finding if the offset should be counted from Start or End\n   * @param {KeyboardEvent} e Event object\n   * @param {Boolean} isRight\n   * @return {Number}\n   */\n  _getSelectionForOffset(e: KeyboardEvent, isRight: boolean): number {\n    if (e.shiftKey && this.selectionStart !== this.selectionEnd && isRight) {\n      return this.selectionEnd;\n    } else {\n      return this.selectionStart;\n    }\n  }\n\n  /**\n   * @param {KeyboardEvent} e Event object\n   * @param {Boolean} isRight\n   * @return {Number}\n   */\n  getUpCursorOffset(e: KeyboardEvent, isRight: boolean): number {\n    const selectionProp = this._getSelectionForOffset(e, isRight),\n      cursorLocation = this.get2DCursorLocation(selectionProp),\n      lineIndex = cursorLocation.lineIndex;\n    if (lineIndex === 0 || e.metaKey || e.keyCode === 33) {\n      // if on first line, up cursor goes to start of line\n      return -selectionProp;\n    }\n    const charIndex = cursorLocation.charIndex,\n      widthBeforeCursor = this._getWidthBeforeCursor(lineIndex, charIndex),\n      indexOnOtherLine = this._getIndexOnLine(lineIndex - 1, widthBeforeCursor),\n      textBeforeCursor = this._textLines[lineIndex].slice(0, charIndex),\n      missingNewlineOffset = this.missingNewlineOffset(lineIndex - 1);\n    // return a negative offset\n    return (\n      -this._textLines[lineIndex - 1].length +\n      indexOnOtherLine -\n      textBeforeCursor.length +\n      (1 - missingNewlineOffset)\n    );\n  }\n\n  /**\n   * for a given width it founds the matching character.\n   * @private\n   */\n  _getIndexOnLine(lineIndex: number, width: number) {\n    const line = this._textLines[lineIndex],\n      lineLeftOffset = this._getLineLeftOffset(lineIndex);\n    let widthOfCharsOnLine = lineLeftOffset,\n      indexOnLine = 0,\n      charWidth,\n      foundMatch;\n\n    for (let j = 0, jlen = line.length; j < jlen; j++) {\n      charWidth = this.__charBounds[lineIndex][j].width;\n      widthOfCharsOnLine += charWidth;\n      if (widthOfCharsOnLine > width) {\n        foundMatch = true;\n        const leftEdge = widthOfCharsOnLine - charWidth,\n          rightEdge = widthOfCharsOnLine,\n          offsetFromLeftEdge = Math.abs(leftEdge - width),\n          offsetFromRightEdge = Math.abs(rightEdge - width);\n\n        indexOnLine = offsetFromRightEdge < offsetFromLeftEdge ? j : j - 1;\n        break;\n      }\n    }\n\n    // reached end\n    if (!foundMatch) {\n      indexOnLine = line.length - 1;\n    }\n\n    return indexOnLine;\n  }\n\n  /**\n   * Moves cursor down\n   * @param {KeyboardEvent} e Event object\n   */\n  moveCursorDown(e: KeyboardEvent) {\n    if (\n      this.selectionStart >= this._text.length &&\n      this.selectionEnd >= this._text.length\n    ) {\n      return;\n    }\n    this._moveCursorUpOrDown('Down', e);\n  }\n\n  /**\n   * Moves cursor up\n   * @param {KeyboardEvent} e Event object\n   */\n  moveCursorUp(e: KeyboardEvent) {\n    if (this.selectionStart === 0 && this.selectionEnd === 0) {\n      return;\n    }\n    this._moveCursorUpOrDown('Up', e);\n  }\n\n  /**\n   * Moves cursor up or down, fires the events\n   * @param {String} direction 'Up' or 'Down'\n   * @param {KeyboardEvent} e Event object\n   */\n  _moveCursorUpOrDown(direction: 'Up' | 'Down', e: KeyboardEvent) {\n    const offset = this[`get${direction}CursorOffset`](\n      e,\n      this._selectionDirection === RIGHT,\n    );\n    if (e.shiftKey) {\n      this.moveCursorWithShift(offset);\n    } else {\n      this.moveCursorWithoutShift(offset);\n    }\n    if (offset !== 0) {\n      const max = this.text.length;\n      this.selectionStart = capValue(0, this.selectionStart, max);\n      this.selectionEnd = capValue(0, this.selectionEnd, max);\n      // TODO fix: abort and init should be an alternative depending\n      // on selectionStart/End being equal or different\n      this.abortCursorAnimation();\n      this.initDelayedCursor();\n      this._fireSelectionChanged();\n      this._updateTextarea();\n    }\n  }\n\n  /**\n   * Moves cursor with shift\n   * @param {Number} offset\n   */\n  moveCursorWithShift(offset: number) {\n    const newSelection =\n      this._selectionDirection === LEFT\n        ? this.selectionStart + offset\n        : this.selectionEnd + offset;\n    this.setSelectionStartEndWithShift(\n      this.selectionStart,\n      this.selectionEnd,\n      newSelection,\n    );\n    return offset !== 0;\n  }\n\n  /**\n   * Moves cursor up without shift\n   * @param {Number} offset\n   */\n  moveCursorWithoutShift(offset: number) {\n    if (offset < 0) {\n      this.selectionStart += offset;\n      this.selectionEnd = this.selectionStart;\n    } else {\n      this.selectionEnd += offset;\n      this.selectionStart = this.selectionEnd;\n    }\n    return offset !== 0;\n  }\n\n  /**\n   * Moves cursor left\n   * @param {KeyboardEvent} e Event object\n   */\n  moveCursorLeft(e: KeyboardEvent) {\n    if (this.selectionStart === 0 && this.selectionEnd === 0) {\n      return;\n    }\n    this._moveCursorLeftOrRight('Left', e);\n  }\n\n  /**\n   * @private\n   * @return {Boolean} true if a change happened\n   *\n   * @todo refactor not to use method name composition\n   */\n  _move(\n    e: KeyboardEvent,\n    prop: 'selectionStart' | 'selectionEnd',\n    direction: 'Left' | 'Right',\n  ): boolean {\n    let newValue: number | undefined;\n    if (e.altKey) {\n      newValue = this[`findWordBoundary${direction}`](this[prop]);\n    } else if (e.metaKey || e.keyCode === 35 || e.keyCode === 36) {\n      newValue = this[`findLineBoundary${direction}`](this[prop]);\n    } else {\n      this[prop] += direction === 'Left' ? -1 : 1;\n      return true;\n    }\n    if (typeof newValue !== 'undefined' && this[prop] !== newValue) {\n      this[prop] = newValue;\n      return true;\n    }\n    return false;\n  }\n\n  /**\n   * @private\n   */\n  _moveLeft(e: KeyboardEvent, prop: 'selectionStart' | 'selectionEnd') {\n    return this._move(e, prop, 'Left');\n  }\n\n  /**\n   * @private\n   */\n  _moveRight(e: KeyboardEvent, prop: 'selectionStart' | 'selectionEnd') {\n    return this._move(e, prop, 'Right');\n  }\n\n  /**\n   * Moves cursor left without keeping selection\n   * @param {KeyboardEvent} e\n   */\n  moveCursorLeftWithoutShift(e: KeyboardEvent) {\n    let change = true;\n    this._selectionDirection = LEFT;\n\n    // only move cursor when there is no selection,\n    // otherwise we discard it, and leave cursor on same place\n    if (\n      this.selectionEnd === this.selectionStart &&\n      this.selectionStart !== 0\n    ) {\n      change = this._moveLeft(e, 'selectionStart');\n    }\n    this.selectionEnd = this.selectionStart;\n    return change;\n  }\n\n  /**\n   * Moves cursor left while keeping selection\n   * @param {KeyboardEvent} e\n   */\n  moveCursorLeftWithShift(e: KeyboardEvent) {\n    if (\n      this._selectionDirection === RIGHT &&\n      this.selectionStart !== this.selectionEnd\n    ) {\n      return this._moveLeft(e, 'selectionEnd');\n    } else if (this.selectionStart !== 0) {\n      this._selectionDirection = LEFT;\n      return this._moveLeft(e, 'selectionStart');\n    }\n  }\n\n  /**\n   * Moves cursor right\n   * @param {KeyboardEvent} e Event object\n   */\n  moveCursorRight(e: KeyboardEvent) {\n    if (\n      this.selectionStart >= this._text.length &&\n      this.selectionEnd >= this._text.length\n    ) {\n      return;\n    }\n    this._moveCursorLeftOrRight('Right', e);\n  }\n\n  /**\n   * Moves cursor right or Left, fires event\n   * @param {String} direction 'Left', 'Right'\n   * @param {KeyboardEvent} e Event object\n   */\n  _moveCursorLeftOrRight(direction: 'Left' | 'Right', e: KeyboardEvent) {\n    const actionName = `moveCursor${direction}${\n      e.shiftKey ? 'WithShift' : 'WithoutShift'\n    }` as const;\n    this._currentCursorOpacity = 1;\n    if (this[actionName](e)) {\n      // TODO fix: abort and init should be an alternative depending\n      // on selectionStart/End being equal or different\n      this.abortCursorAnimation();\n      this.initDelayedCursor();\n      this._fireSelectionChanged();\n      this._updateTextarea();\n    }\n  }\n\n  /**\n   * Moves cursor right while keeping selection\n   * @param {KeyboardEvent} e\n   */\n  moveCursorRightWithShift(e: KeyboardEvent) {\n    if (\n      this._selectionDirection === LEFT &&\n      this.selectionStart !== this.selectionEnd\n    ) {\n      return this._moveRight(e, 'selectionStart');\n    } else if (this.selectionEnd !== this._text.length) {\n      this._selectionDirection = RIGHT;\n      return this._moveRight(e, 'selectionEnd');\n    }\n  }\n\n  /**\n   * Moves cursor right without keeping selection\n   * @param {KeyboardEvent} e Event object\n   */\n  moveCursorRightWithoutShift(e: KeyboardEvent) {\n    let changed = true;\n    this._selectionDirection = RIGHT;\n\n    if (this.selectionStart === this.selectionEnd) {\n      changed = this._moveRight(e, 'selectionStart');\n      this.selectionEnd = this.selectionStart;\n    } else {\n      this.selectionStart = this.selectionEnd;\n    }\n    return changed;\n  }\n}\n"],"mappings":"sXAaA,IAAsB,EAAtB,cAIU,CAAA,CA4CR,oBAAA,CACE,IAAM,EACH,KAAK,QAAU,EAAuB,KAAK,OAAO,YAAA,CAAA,EACnD,GAAA,CACI,EAAW,EAAI,cAAc,WAAA,CACnC,OAAO,QAAQ,CACb,eAAgB,MAChB,YAAa,MACb,aAAc,MACd,WAAY,QACZ,cAAe,WACf,KAAM,MACN,KAAM,iBAAA,CAAA,CACL,KAAA,CAAM,EAAW,KAAW,EAAS,aAAa,EAAW,EAAA,CAAA,CAChE,GAAA,CAAM,IAAE,EAAA,KAAK,EAAA,SAAM,GAAa,KAAK,uBAAA,CAGrC,EAAS,MAAM,QAAU,4BAA4B,EAAA,UAAc,EAAA,qFAA0F,EAAA,IAE5J,KAAK,yBAA2B,EAAI,MAAM,YAAY,EAAA,CAEvD,OAAO,QAAQ,CACb,KAAM,OACN,QAAS,YACT,MAAO,UACP,MAAO,UACP,KAAM,OACN,IAAK,OACL,MAAO,QACP,iBAAkB,qBAClB,kBAAmB,sBACnB,eAAgB,mBAAA,CAAA,CACe,KAAA,CAAM,EAAW,KAChD,EAAS,iBACP,EACC,KAAK,GAA2B,KAAK,KAAA,CAAA,CAAA,CAG1C,KAAK,eAAiB,EAMxB,MAAA,CACE,KAAK,sBAAA,CAQP,UAAU,EAAA,CACR,GAAA,CAAK,KAAK,UACR,OAEF,IAAM,EAAS,KAAK,YAAc,MAAQ,KAAK,WAAa,KAAK,QACjE,GAAI,EAAE,WAAW,EACd,KAAK,EAAO,EAAE,UACb,EAAA,KAAA,CAAA,GAAA,EAEO,EAAE,WAAW,KAAK,kBAAA,CAAoB,EAAE,SAAA,CAAW,EAAE,QAO9D,OALE,KAAK,KAAK,gBAAgB,EAAE,UAG5B,EAEF,CAEF,EAAE,0BAAA,CACF,EAAE,gBAAA,CACE,EAAE,SAAW,IAAM,EAAE,SAAW,IAElC,KAAK,kBAAA,CAAoB,EACzB,KAAK,iBAAA,CACL,KAAK,yBAAA,EAEL,KAAK,QAAU,KAAK,OAAO,kBAAA,CAU/B,QAAQ,EAAA,CAAA,CACD,KAAK,WAAa,KAAK,WAAa,KAAK,kBAC5C,KAAK,UAAA,CAAY,EAGf,EAAE,WAAW,KAAK,gBAAkB,EAAE,SAAW,EAAE,WAEnD,KAAK,KAAK,cAAc,EAAE,UAG1B,EAAA,CAIJ,EAAE,0BAAA,CACF,EAAE,gBAAA,CACF,KAAK,QAAU,KAAK,OAAO,kBAAA,EAO7B,QAA8D,EAAA,CAC5D,IAAM,EAAY,KAAK,UAAA,CACjB,MAAE,EAAA,eAAO,EAAA,aAAgB,GAAiB,KAAK,eAGrD,GAFA,KAAK,UAAA,CAAY,EACjB,GAAK,EAAE,iBAAA,CAAA,CACF,KAAK,UACR,OAEF,IAAM,MAAA,CACJ,KAAK,oBAAA,CACL,KAAK,KAAK,EAAA,CACN,KAAK,SACP,KAAK,OAAO,KAAK,eAAgB,CAAE,OAAQ,KAAA,CAAA,CAC3C,KAAK,OAAO,kBAAA,GAGhB,GAAI,KAAK,eAAe,QAAU,GAGhC,MAFA,MAAK,OAAS,EAAA,CAAA,KACd,GAAA,CAIF,IAAM,EAAW,KAAK,oBAAoB,EAAA,CAAO,aAC/C,EAAY,KAAK,MAAM,OACvB,EAAgB,EAAS,OACzB,EAAkB,KAAK,eACvB,EAAgB,KAAK,aACrB,EAAY,IAAoB,EAC9B,EACF,EAEA,EACA,EAFA,EAAW,EAAgB,EAIvB,EAAoB,KAAK,8BAC7B,EACA,EACA,EAAA,CAEI,EAAa,EAAkB,EAAkB,eAEnD,GACF,EAAc,KAAK,MAAM,MAAM,EAAiB,EAAA,CAChD,GAAY,EAAgB,GACnB,EAAgB,IAEvB,EADE,EACY,KAAK,MAAM,MAAM,EAAgB,EAAU,EAAA,CAE3C,KAAK,MAAM,MACvB,EACA,EAAkB,EAAA,EAIxB,IAAM,EAAe,EAAS,MAC5B,EAAkB,aAAe,EACjC,EAAkB,aAAA,CAiCpB,GA/BI,GAAe,EAAY,SACzB,EAAa,SAIf,EAAc,KAAK,mBACjB,EACA,EAAkB,EAAA,CAClB,EAAA,CAGF,EAAc,EAAa,QAIvB,EAAa,GAAA,EAGf,GACF,EAAa,EACb,EAAW,GACF,GAET,EAAa,EAAgB,EAAY,OACzC,EAAW,IAEX,EAAa,EACb,EAAW,EAAgB,EAAY,QAEzC,KAAK,kBAAkB,EAAY,EAAA,EAEjC,EAAa,OAAQ,CACvB,GAAA,CAAM,cAAE,GAAkB,GAAA,CAExB,GACA,EAAa,KAAK,GAAA,GAAQ,EAAc,YAAA,CACvC,EAAO,wBAER,EAAc,EAAc,iBAE9B,KAAK,oBAAoB,EAAc,EAAiB,EAAA,CAE1D,GAAA,CAMF,oBAAA,CACE,KAAK,kBAAA,CAAoB,EAM3B,kBAAA,CACE,KAAK,kBAAA,CAAoB,EAG3B,oBAAA,CAAoB,OAAE,GAAA,CACpB,GAAA,CAAM,eAAE,EAAA,aAAgB,GAAiB,EACzC,KAAK,iBAAmB,EACxB,KAAK,eAAiB,EACtB,KAAK,wBAAA,CAMP,MAAA,CACE,GAAI,KAAK,iBAAmB,KAAK,aAE/B,OAEF,GAAA,CAAM,cAAE,GAAkB,GAAA,CAC1B,EAAc,WAAa,KAAK,iBAAA,CAC3B,EAAO,sBAOV,EAAc,gBAAA,IAAkB,GANhC,EAAc,gBAAkB,KAAK,mBACnC,KAAK,eACL,KAAK,aAAA,CACL,EAAA,CAKJ,KAAK,UAAA,CAAY,EAMnB,OAAA,CACE,KAAK,UAAA,CAAY,EAUnB,sBAAsB,EAAmB,EAAA,CACvC,IACE,EADE,EAAoB,KAAK,mBAAmB,EAAA,CAOhD,OAJI,EAAY,IACd,EAAQ,KAAK,aAAa,GAAW,EAAY,GACjD,GAAqB,EAAM,KAAO,EAAM,OAEnC,EAST,oBAAoB,EAAkB,EAAA,CACpC,IAAM,EAAgB,KAAK,uBAAuB,EAAG,EAAA,CACnD,EAAiB,KAAK,oBAAoB,EAAA,CAC1C,EAAY,EAAe,UAE7B,GACE,IAAc,KAAK,WAAW,OAAS,GACvC,EAAE,SACF,EAAE,UAAY,GAGd,OAAO,KAAK,MAAM,OAAS,EAE7B,IAAM,EAAY,EAAe,UAC/B,EAAoB,KAAK,sBAAsB,EAAW,EAAA,CAC1D,EAAmB,KAAK,gBAAgB,EAAY,EAAG,EAAA,CAEzD,OADoB,KAAK,WAAW,GAAW,MAAM,EAAA,CAEnC,OAChB,EACA,EACA,KAAK,qBAAqB,EAAA,CAW9B,uBAAuB,EAAkB,EAAA,CACvC,OAAI,EAAE,UAAY,KAAK,iBAAmB,KAAK,cAAgB,EACtD,KAAK,aAEL,KAAK,eAShB,kBAAkB,EAAkB,EAAA,CAClC,IAAM,EAAgB,KAAK,uBAAuB,EAAG,EAAA,CACnD,EAAiB,KAAK,oBAAoB,EAAA,CAC1C,EAAY,EAAe,UAC7B,GAAI,IAAc,GAAK,EAAE,SAAW,EAAE,UAAY,GAEhD,MAAA,CAAQ,EAEV,IAAM,EAAY,EAAe,UAC/B,EAAoB,KAAK,sBAAsB,EAAW,EAAA,CAC1D,EAAmB,KAAK,gBAAgB,EAAY,EAAG,EAAA,CACvD,EAAmB,KAAK,WAAW,GAAW,MAAM,EAAG,EAAA,CACvD,EAAuB,KAAK,qBAAqB,EAAY,EAAA,CAE/D,MAAA,CACG,KAAK,WAAW,EAAY,GAAG,OAChC,EACA,EAAiB,QAChB,EAAI,GAQT,gBAAgB,EAAmB,EAAA,CACjC,IAAM,EAAO,KAAK,WAAW,GAI3B,EACA,EAHE,EADe,KAAK,mBAAmB,EAAA,CAEzC,EAAc,EAIhB,IAAK,IAAI,EAAI,EAAG,EAAO,EAAK,OAAQ,EAAI,EAAM,IAG5C,GAFA,EAAY,KAAK,aAAa,GAAW,GAAG,MAC5C,GAAsB,EAClB,EAAqB,EAAO,CAC9B,EAAA,CAAa,EACb,IAAM,EAAW,EAAqB,EACpC,EAAY,EACZ,EAAqB,KAAK,IAAI,EAAW,EAAA,CAG3C,EAFwB,KAAK,IAAI,EAAY,EAAA,CAET,EAAqB,EAAI,EAAI,EACjE,MASJ,OAJK,IACH,EAAc,EAAK,OAAS,GAGvB,EAOT,eAAe,EAAA,CAEX,KAAK,gBAAkB,KAAK,MAAM,QAClC,KAAK,cAAgB,KAAK,MAAM,QAIlC,KAAK,oBAAoB,OAAQ,EAAA,CAOnC,aAAa,EAAA,CACP,KAAK,iBAAmB,GAAK,KAAK,eAAiB,GAGvD,KAAK,oBAAoB,KAAM,EAAA,CAQjC,oBAAoB,EAA0B,EAAA,CAC5C,IAAM,EAAS,KAAK,MAAM,EAAA,eACxB,EACA,KAAK,sBAAwB,EAAA,CAO/B,GALI,EAAE,SACJ,KAAK,oBAAoB,EAAA,CAEzB,KAAK,uBAAuB,EAAA,CAE1B,IAAW,EAAG,CAChB,IAAM,EAAM,KAAK,KAAK,OACtB,KAAK,eAAiB,EAAS,EAAG,KAAK,eAAgB,EAAA,CACvD,KAAK,aAAe,EAAS,EAAG,KAAK,aAAc,EAAA,CAGnD,KAAK,sBAAA,CACL,KAAK,mBAAA,CACL,KAAK,uBAAA,CACL,KAAK,iBAAA,EAQT,oBAAoB,EAAA,CAClB,IAAM,EACJ,KAAK,sBAAA,OACD,KAAK,eAAiB,EACtB,KAAK,aAAe,EAM1B,OALA,KAAK,8BACH,KAAK,eACL,KAAK,aACL,EAAA,CAEK,IAAW,EAOpB,uBAAuB,EAAA,CAQrB,OAPI,EAAS,GACX,KAAK,gBAAkB,EACvB,KAAK,aAAe,KAAK,iBAEzB,KAAK,cAAgB,EACrB,KAAK,eAAiB,KAAK,cAEtB,IAAW,EAOpB,eAAe,EAAA,CACT,KAAK,iBAAmB,GAAK,KAAK,eAAiB,GAGvD,KAAK,uBAAuB,OAAQ,EAAA,CAStC,MACE,EACA,EACA,EAAA,CAEA,IAAI,EACJ,GAAI,EAAE,OACJ,EAAW,KAAK,mBAAmB,KAAa,KAAK,GAAA,KAAA,CAAA,GAAA,CAC5C,EAAE,SAAW,EAAE,UAAY,IAAM,EAAE,UAAY,GAIxD,MADA,MAAK,IAAS,IAAc,OAAd,GAA4B,EAAA,CACnC,EAHP,EAAW,KAAK,mBAAmB,KAAa,KAAK,GAAA,CAKvD,OAAW,IAAX,IAAwB,IAAe,KAAK,KAAU,IACpD,KAAK,GAAQ,EAAA,CACN,GAQX,UAAU,EAAkB,EAAA,CAC1B,OAAO,KAAK,MAAM,EAAG,EAAM,OAAA,CAM7B,WAAW,EAAkB,EAAA,CAC3B,OAAO,KAAK,MAAM,EAAG,EAAM,QAAA,CAO7B,2BAA2B,EAAA,CACzB,IAAI,EAAA,CAAS,EAYb,MAXA,MAAK,oBAAsB,EAKzB,KAAK,eAAiB,KAAK,gBAC3B,KAAK,iBAAmB,IAExB,EAAS,KAAK,UAAU,EAAG,iBAAA,EAE7B,KAAK,aAAe,KAAK,eAClB,EAOT,wBAAwB,EAAA,CACtB,OACE,KAAK,sBAAA,SACL,KAAK,iBAAmB,KAAK,aAEtB,KAAK,UAAU,EAAG,eAAA,CAChB,KAAK,iBAAmB,EAER,IAAA,IADzB,KAAK,oBAAsB,EACpB,KAAK,UAAU,EAAG,iBAAA,EAQ7B,gBAAgB,EAAA,CAEZ,KAAK,gBAAkB,KAAK,MAAM,QAClC,KAAK,cAAgB,KAAK,MAAM,QAIlC,KAAK,uBAAuB,QAAS,EAAA,CAQvC,uBAAuB,EAA6B,EAAA,CAClD,IAAM,EAAa,aAAa,IAC9B,EAAE,SAAW,YAAc,iBAE7B,KAAK,sBAAwB,EACzB,KAAK,GAAY,EAAA,GAGnB,KAAK,sBAAA,CACL,KAAK,mBAAA,CACL,KAAK,uBAAA,CACL,KAAK,iBAAA,EAQT,yBAAyB,EAAA,CACvB,OACE,KAAK,sBAAA,QACL,KAAK,iBAAmB,KAAK,aAEtB,KAAK,WAAW,EAAG,iBAAA,CACjB,KAAK,eAAiB,KAAK,MAAM,OAEhB,IAAA,IAD1B,KAAK,oBAAsB,EACpB,KAAK,WAAW,EAAG,eAAA,EAQ9B,4BAA4B,EAAA,CAC1B,IAAI,EAAA,CAAU,EASd,MARA,MAAK,oBAAsB,EAEvB,KAAK,iBAAmB,KAAK,cAC/B,EAAU,KAAK,WAAW,EAAG,iBAAA,CAC7B,KAAK,aAAe,KAAK,gBAEzB,KAAK,eAAiB,KAAK,aAEtB,IAAA,OAAA,KAAA"}