fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
284 lines (268 loc) • 9.35 kB
JavaScript
import { defineProperty as _defineProperty, objectSpread2 as _objectSpread2 } from '../../../_virtual/_rollupPluginBabelHelpers.mjs';
import { FabricObject } from '../Object/FabricObject.mjs';
import { styleProperties } from './constants.mjs';
import '../../util/misc/vectors.mjs';
import '../../Point.mjs';
import '../../util/misc/projectStroke/StrokeLineJoinProjections.mjs';
import '../../config.mjs';
import '../Group.mjs';
import { pickBy, pick } from '../../util/misc/pick.mjs';
import '../../cache.mjs';
import '../../parser/constants.mjs';
import '../../util/animation/AnimationRegistry.mjs';
class StyledText 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]) {
// eslint-disable-next-line no-unused-vars
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]
};
// eslint-disable-next-line
for (const p1 in obj) {
// eslint-disable-next-line
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] !== undefined;
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];
}
}
// if every grapheme has the same style set then
// delete those styles and set it on the parent
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(_objectSpread2(_objectSpread2({}, this._getStyleDeclaration(lineIndex, charIndex)), style), value => value !== undefined);
// finally assign to the old position the new style
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);
}
/* not included in _extendStyles to avoid clearing cache more than once */
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 _objectSpread2(_objectSpread2({}, 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);
export { StyledText };
//# sourceMappingURL=StyledText.mjs.map