UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 47 kB
{"version":3,"file":"ITextBehavior.mjs","names":[],"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"],"mappings":"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"}