fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
199 lines (198 loc) • 7.8 kB
JavaScript
import { _defineProperty } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/defineProperty.mjs";
import { pick, pickBy } from "../../util/misc/pick.mjs";
import { styleProperties } from "./constants.mjs";
import { FabricObject } from "../Object/FabricObject.mjs";
//#region src/shapes/Text/StyledText.ts
var StyledText = class extends FabricObject {
/**
* Returns true if object has no styling or no styling in a line
* @param {Number} lineIndex , lineIndex is on wrapped lines.
* @return {Boolean}
*/
isEmptyStyles(lineIndex) {
if (!this.styles) return true;
if (typeof lineIndex !== "undefined" && !this.styles[lineIndex]) return true;
const obj = typeof lineIndex === "undefined" ? this.styles : { line: this.styles[lineIndex] };
for (const p1 in obj) for (const p2 in obj[p1]) for (const p3 in obj[p1][p2]) return false;
return true;
}
/**
* Returns true if object has a style property or has it ina specified line
* This function is used to detect if a text will use a particular property or not.
* @param {String} property to check for
* @param {Number} lineIndex to check the style on
* @return {Boolean}
*/
styleHas(property, lineIndex) {
if (!this.styles) return false;
if (typeof lineIndex !== "undefined" && !this.styles[lineIndex]) return false;
const obj = typeof lineIndex === "undefined" ? this.styles : { 0: this.styles[lineIndex] };
for (const p1 in obj) for (const p2 in obj[p1]) if (typeof obj[p1][p2][property] !== "undefined") return true;
return false;
}
/**
* Check if characters in a text have a value for a property
* whose value matches the textbox's value for that property. If so,
* the character-level property is deleted. If the character
* has no other properties, then it is also deleted. Finally,
* if the line containing that character has no other characters
* then it also is deleted.
*/
cleanStyle(property) {
if (!this.styles) return false;
const obj = this.styles;
let stylesCount = 0, letterCount, stylePropertyValue, allStyleObjectPropertiesMatch = true, graphemeCount = 0;
for (const p1 in obj) {
letterCount = 0;
for (const p2 in obj[p1]) {
const styleObject = obj[p1][p2] || {}, stylePropertyHasBeenSet = styleObject[property] !== void 0;
stylesCount++;
if (stylePropertyHasBeenSet) {
if (!stylePropertyValue) stylePropertyValue = styleObject[property];
else if (styleObject[property] !== stylePropertyValue) allStyleObjectPropertiesMatch = false;
if (styleObject[property] === this[property]) delete styleObject[property];
} else allStyleObjectPropertiesMatch = false;
if (Object.keys(styleObject).length !== 0) letterCount++;
else delete obj[p1][p2];
}
if (letterCount === 0) delete obj[p1];
}
for (let i = 0; i < this._textLines.length; i++) graphemeCount += this._textLines[i].length;
if (allStyleObjectPropertiesMatch && stylesCount === graphemeCount) {
this[property] = stylePropertyValue;
this.removeStyle(property);
}
}
/**
* Remove a style property or properties from all individual character styles
* in a text object. Deletes the character style object if it contains no other style
* props. Deletes a line style object if it contains no other character styles.
*
* @param {String} props The property to remove from character styles.
*/
removeStyle(property) {
if (!this.styles) return;
const obj = this.styles;
let line, lineNum, charNum;
for (lineNum in obj) {
line = obj[lineNum];
for (charNum in line) {
delete line[charNum][property];
if (Object.keys(line[charNum]).length === 0) delete line[charNum];
}
if (Object.keys(line).length === 0) delete obj[lineNum];
}
}
_extendStyles(index, style) {
const { lineIndex, charIndex } = this.get2DCursorLocation(index);
if (!this._getLineStyle(lineIndex)) this._setLineStyle(lineIndex);
const newStyle = pickBy({
...this._getStyleDeclaration(lineIndex, charIndex),
...style
}, (value) => value !== void 0);
this._setStyleDeclaration(lineIndex, charIndex, newStyle);
}
/**
* Gets style of a current selection/cursor (at the start position)
* @param {Number} startIndex Start index to get styles at
* @param {Number} endIndex End index to get styles at, if not specified startIndex + 1
* @param {Boolean} [complete] get full style or not
* @return {Array} styles an array with one, zero or more Style objects
*/
getSelectionStyles(startIndex, endIndex, complete) {
const styles = [];
for (let i = startIndex; i < (endIndex || startIndex); i++) styles.push(this.getStyleAtPosition(i, complete));
return styles;
}
/**
* Gets style of a current selection/cursor position
* @param {Number} position to get styles at
* @param {Boolean} [complete] full style if true
* @return {Object} style Style object at a specified index
* @private
*/
getStyleAtPosition(position, complete) {
const { lineIndex, charIndex } = this.get2DCursorLocation(position);
return complete ? this.getCompleteStyleDeclaration(lineIndex, charIndex) : this._getStyleDeclaration(lineIndex, charIndex);
}
/**
* Sets style of a current selection, if no selection exist, do not set anything.
* @param {Object} styles Styles object
* @param {Number} startIndex Start index to get styles at
* @param {Number} [endIndex] End index to get styles at, if not specified startIndex + 1
*/
setSelectionStyles(styles, startIndex, endIndex) {
for (let i = startIndex; i < (endIndex || startIndex); i++) this._extendStyles(i, styles);
this._forceClearCache = true;
}
/**
* Get a reference, not a clone, to the style object for a given character,
* if no style is set for a line or char, return a new empty object.
* This is tricky and confusing because when you get an empty object you can't
* determine if it is a reference or a new one.
* @TODO this should always return a reference or always a clone or undefined when necessary.
* @protected
* @param {Number} lineIndex
* @param {Number} charIndex
* @return {TextStyleDeclaration} a style object reference to the existing one or a new empty object when undefined
*/
_getStyleDeclaration(lineIndex, charIndex) {
var _lineStyle$charIndex;
const lineStyle = this.styles && this.styles[lineIndex];
return lineStyle ? (_lineStyle$charIndex = lineStyle[charIndex]) !== null && _lineStyle$charIndex !== void 0 ? _lineStyle$charIndex : {} : {};
}
/**
* return a new object that contains all the style property for a character
* the object returned is newly created
* @param {Number} lineIndex of the line where the character is
* @param {Number} charIndex position of the character on the line
* @return {Object} style object
*/
getCompleteStyleDeclaration(lineIndex, charIndex) {
return {
...pick(this, this.constructor._styleProperties),
...this._getStyleDeclaration(lineIndex, charIndex)
};
}
/**
* @param {Number} lineIndex
* @param {Number} charIndex
* @param {Object} style
* @private
*/
_setStyleDeclaration(lineIndex, charIndex, style) {
this.styles[lineIndex][charIndex] = style;
}
/**
*
* @param {Number} lineIndex
* @param {Number} charIndex
* @private
*/
_deleteStyleDeclaration(lineIndex, charIndex) {
delete this.styles[lineIndex][charIndex];
}
/**
* @param {Number} lineIndex
* @return {Boolean} if the line exists or not
* @private
*/
_getLineStyle(lineIndex) {
return !!this.styles[lineIndex];
}
/**
* Set the line style to an empty object so that is initialized
* @param {Number} lineIndex
* @private
*/
_setLineStyle(lineIndex) {
this.styles[lineIndex] = {};
}
_deleteLineStyle(lineIndex) {
delete this.styles[lineIndex];
}
};
_defineProperty(StyledText, "_styleProperties", styleProperties);
//#endregion
export { StyledText };
//# sourceMappingURL=StyledText.mjs.map