{"version":3,"file":"StyledText.min.mjs","sources":["../../../../src/shapes/Text/StyledText.ts"],"sourcesContent":["import type { ObjectEvents } from '../../EventTypeDefs';\nimport type { FabricObjectProps, SerializedObjectProps } from '../Object/types';\nimport type { TOptions } from '../../typedefs';\nimport { FabricObject } from '../Object/FabricObject';\nimport { styleProperties } from './constants';\nimport type { StylePropertiesType } from './constants';\nimport type { FabricText } from './Text';\nimport { pick } from '../../util';\nimport { pickBy } from '../../util/misc/pick';\n\nexport type CompleteTextStyleDeclaration = Pick<\n  FabricText,\n  StylePropertiesType\n>;\n\nexport type TextStyleDeclaration = Partial<CompleteTextStyleDeclaration>;\n\nexport type TextStyle = {\n  [line: number | string]: { [char: number | string]: TextStyleDeclaration };\n};\n\nexport abstract class StyledText<\n  Props extends TOptions<FabricObjectProps> = Partial<FabricObjectProps>,\n  SProps extends SerializedObjectProps = SerializedObjectProps,\n  EventSpec extends ObjectEvents = ObjectEvents,\n> extends FabricObject<Props, SProps, EventSpec> {\n  declare abstract styles: TextStyle;\n  protected declare abstract _textLines: string[][];\n  protected declare _forceClearCache: boolean;\n  static _styleProperties: Readonly<StylePropertiesType[]> = styleProperties;\n  abstract get2DCursorLocation(\n    selectionStart: number,\n    skipWrapping?: boolean,\n  ): { charIndex: number; lineIndex: number };\n\n  /**\n   * Returns true if object has no styling or no styling in a line\n   * @param {Number} lineIndex , lineIndex is on wrapped lines.\n   * @return {Boolean}\n   */\n  isEmptyStyles(lineIndex?: number): boolean {\n    if (!this.styles) {\n      return true;\n    }\n    if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {\n      return true;\n    }\n    const obj =\n      typeof lineIndex === 'undefined'\n        ? this.styles\n        : { line: this.styles[lineIndex] };\n    for (const p1 in obj) {\n      for (const p2 in obj[p1]) {\n        // eslint-disable-next-line no-unused-vars\n        for (const p3 in obj[p1][p2]) {\n          return false;\n        }\n      }\n    }\n    return true;\n  }\n\n  /**\n   * Returns true if object has a style property or has it ina specified line\n   * This function is used to detect if a text will use a particular property or not.\n   * @param {String} property to check for\n   * @param {Number} lineIndex to check the style on\n   * @return {Boolean}\n   */\n  styleHas(property: keyof TextStyleDeclaration, lineIndex?: number): boolean {\n    if (!this.styles) {\n      return false;\n    }\n    if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {\n      return false;\n    }\n    const obj =\n      typeof lineIndex === 'undefined'\n        ? this.styles\n        : { 0: this.styles[lineIndex] };\n    // eslint-disable-next-line\n    for (const p1 in obj) {\n      // eslint-disable-next-line\n      for (const p2 in obj[p1]) {\n        if (typeof obj[p1][p2][property] !== 'undefined') {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n\n  /**\n   * Check if characters in a text have a value for a property\n   * whose value matches the textbox's value for that property.  If so,\n   * the character-level property is deleted.  If the character\n   * has no other properties, then it is also deleted.  Finally,\n   * if the line containing that character has no other characters\n   * then it also is deleted.\n   *\n   * @param {string} property The property to compare between characters and text.\n   */\n  cleanStyle(property: keyof TextStyleDeclaration) {\n    if (!this.styles) {\n      return false;\n    }\n    const obj = this.styles;\n    let stylesCount = 0,\n      letterCount,\n      stylePropertyValue,\n      allStyleObjectPropertiesMatch = true,\n      graphemeCount = 0;\n    for (const p1 in obj) {\n      letterCount = 0;\n      for (const p2 in obj[p1]) {\n        const styleObject = obj[p1][p2] || {},\n          stylePropertyHasBeenSet = styleObject[property] !== undefined;\n\n        stylesCount++;\n\n        if (stylePropertyHasBeenSet) {\n          if (!stylePropertyValue) {\n            stylePropertyValue = styleObject[property];\n          } else if (styleObject[property] !== stylePropertyValue) {\n            allStyleObjectPropertiesMatch = false;\n          }\n\n          if (styleObject[property] === this[property as keyof this]) {\n            delete styleObject[property];\n          }\n        } else {\n          allStyleObjectPropertiesMatch = false;\n        }\n\n        if (Object.keys(styleObject).length !== 0) {\n          letterCount++;\n        } else {\n          delete obj[p1][p2];\n        }\n      }\n\n      if (letterCount === 0) {\n        delete obj[p1];\n      }\n    }\n    // if every grapheme has the same style set then\n    // delete those styles and set it on the parent\n    for (let i = 0; i < this._textLines.length; i++) {\n      graphemeCount += this._textLines[i].length;\n    }\n    if (allStyleObjectPropertiesMatch && stylesCount === graphemeCount) {\n      // @ts-expect-error conspiracy theory of TS\n      this[property as keyof this] = stylePropertyValue;\n      this.removeStyle(property);\n    }\n  }\n\n  /**\n   * Remove a style property or properties from all individual character styles\n   * in a text object.  Deletes the character style object if it contains no other style\n   * props.  Deletes a line style object if it contains no other character styles.\n   *\n   * @param {String} props The property to remove from character styles.\n   */\n  removeStyle(property: keyof TextStyleDeclaration) {\n    if (!this.styles) {\n      return;\n    }\n    const obj = this.styles;\n    let line, lineNum, charNum;\n    for (lineNum in obj) {\n      line = obj[lineNum];\n      for (charNum in line) {\n        delete line[charNum][property];\n        if (Object.keys(line[charNum]).length === 0) {\n          delete line[charNum];\n        }\n      }\n      if (Object.keys(line).length === 0) {\n        delete obj[lineNum];\n      }\n    }\n  }\n\n  private _extendStyles(index: number, style: TextStyleDeclaration): void {\n    const { lineIndex, charIndex } = this.get2DCursorLocation(index);\n\n    if (!this._getLineStyle(lineIndex)) {\n      this._setLineStyle(lineIndex);\n    }\n\n    const newStyle = pickBy(\n      {\n        // first create a new object that is a merge of existing and new\n        ...this._getStyleDeclaration(lineIndex, charIndex),\n        ...style,\n        // use the predicate to discard undefined values\n      },\n      (value) => value !== undefined,\n    );\n\n    // finally assign to the old position the new style\n    this._setStyleDeclaration(lineIndex, charIndex, newStyle);\n  }\n\n  /**\n   * Gets style of a current selection/cursor (at the start position)\n   * @param {Number} startIndex Start index to get styles at\n   * @param {Number} endIndex End index to get styles at, if not specified startIndex + 1\n   * @param {Boolean} [complete] get full style or not\n   * @return {Array} styles an array with one, zero or more Style objects\n   */\n  getSelectionStyles(\n    startIndex: number,\n    endIndex?: number,\n    complete?: boolean,\n  ): TextStyleDeclaration[] {\n    const styles: TextStyleDeclaration[] = [];\n    for (let i = startIndex; i < (endIndex || startIndex); i++) {\n      styles.push(this.getStyleAtPosition(i, complete));\n    }\n    return styles;\n  }\n\n  /**\n   * Gets style of a current selection/cursor position\n   * @param {Number} position  to get styles at\n   * @param {Boolean} [complete] full style if true\n   * @return {Object} style Style object at a specified index\n   * @private\n   */\n  getStyleAtPosition(position: number, complete?: boolean) {\n    const { lineIndex, charIndex } = this.get2DCursorLocation(position);\n    return complete\n      ? this.getCompleteStyleDeclaration(lineIndex, charIndex)\n      : this._getStyleDeclaration(lineIndex, charIndex);\n  }\n\n  /**\n   * Sets style of a current selection, if no selection exist, do not set anything.\n   * @param {Object} styles Styles object\n   * @param {Number} startIndex Start index to get styles at\n   * @param {Number} [endIndex] End index to get styles at, if not specified startIndex + 1\n   */\n  setSelectionStyles(styles: object, startIndex: number, endIndex?: number) {\n    for (let i = startIndex; i < (endIndex || startIndex); i++) {\n      this._extendStyles(i, styles);\n    }\n    /* not included in _extendStyles to avoid clearing cache more than once */\n    this._forceClearCache = true;\n  }\n\n  /**\n   * Get a reference, not a clone, to the style object for a given character,\n   * if no style is set for a line or char, return a new empty object.\n   * This is tricky and confusing because when you get an empty object you can't\n   * determine if it is a reference or a new one.\n   * @TODO this should always return a reference or always a clone or undefined when necessary.\n   * @protected\n   * @param {Number} lineIndex\n   * @param {Number} charIndex\n   * @return {TextStyleDeclaration} a style object reference to the existing one or a new empty object when undefined\n   */\n  _getStyleDeclaration(\n    lineIndex: number,\n    charIndex: number,\n  ): TextStyleDeclaration {\n    const lineStyle = this.styles && this.styles[lineIndex];\n    return lineStyle ? lineStyle[charIndex] ?? {} : {};\n  }\n\n  /**\n   * return a new object that contains all the style property for a character\n   * the object returned is newly created\n   * @param {Number} lineIndex of the line where the character is\n   * @param {Number} charIndex position of the character on the line\n   * @return {Object} style object\n   */\n  getCompleteStyleDeclaration(\n    lineIndex: number,\n    charIndex: number,\n  ): CompleteTextStyleDeclaration {\n    return {\n      // @ts-expect-error readonly\n      ...pick(this, (this.constructor as typeof StyledText)._styleProperties),\n      ...this._getStyleDeclaration(lineIndex, charIndex),\n    } as CompleteTextStyleDeclaration;\n  }\n\n  /**\n   * @param {Number} lineIndex\n   * @param {Number} charIndex\n   * @param {Object} style\n   * @private\n   */\n  protected _setStyleDeclaration(\n    lineIndex: number,\n    charIndex: number,\n    style: object,\n  ) {\n    this.styles[lineIndex][charIndex] = style;\n  }\n\n  /**\n   *\n   * @param {Number} lineIndex\n   * @param {Number} charIndex\n   * @private\n   */\n  protected _deleteStyleDeclaration(lineIndex: number, charIndex: number) {\n    delete this.styles[lineIndex][charIndex];\n  }\n\n  /**\n   * @param {Number} lineIndex\n   * @return {Boolean} if the line exists or not\n   * @private\n   */\n  protected _getLineStyle(lineIndex: number): boolean {\n    return !!this.styles[lineIndex];\n  }\n\n  /**\n   * Set the line style to an empty object so that is initialized\n   * @param {Number} lineIndex\n   * @private\n   */\n  protected _setLineStyle(lineIndex: number) {\n    this.styles[lineIndex] = {};\n  }\n\n  protected _deleteLineStyle(lineIndex: number) {\n    delete this.styles[lineIndex];\n  }\n}\n"],"names":["StyledText","FabricObject","isEmptyStyles","lineIndex","this","styles","obj","line","p1","p2","p3","styleHas","property","cleanStyle","letterCount","stylePropertyValue","stylesCount","allStyleObjectPropertiesMatch","graphemeCount","styleObject","undefined","Object","keys","length","i","_textLines","removeStyle","lineNum","charNum","_extendStyles","index","style","charIndex","get2DCursorLocation","_getLineStyle","_setLineStyle","newStyle","pickBy","_objectSpread","_getStyleDeclaration","value","_setStyleDeclaration","getSelectionStyles","startIndex","endIndex","complete","push","getStyleAtPosition","position","getCompleteStyleDeclaration","setSelectionStyles","_forceClearCache","_lineStyle$charIndex","lineStyle","pick","constructor","_styleProperties","_deleteStyleDeclaration","_deleteLineStyle","_defineProperty","styleProperties"],"mappings":"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"}