mirror of
https://github.com/Hopiu/fabric.js.git
synced 2026-03-16 22:10:32 +00:00
Normalize Api for getSelectionStyles, setSelectionStyles (#4202)
* reworked the text selection * reorganized api * missing file * fixed lint * more test
This commit is contained in:
parent
10545cec77
commit
3b10702512
6 changed files with 467 additions and 325 deletions
1
build.js
1
build.js
|
|
@ -224,6 +224,7 @@ var filesToInclude = [
|
|||
ifSpecifiedInclude('image_filters', 'src/filters/hue_rotation.class.js'),
|
||||
|
||||
ifSpecifiedInclude('text', 'src/shapes/text.class.js'),
|
||||
ifSpecifiedInclude('text', 'src/mixins/text_style.mixin.js'),
|
||||
|
||||
ifSpecifiedInclude('itext', 'src/shapes/itext.class.js'),
|
||||
ifSpecifiedInclude('itext', 'src/mixins/itext_behavior.mixin.js'),
|
||||
|
|
|
|||
310
src/mixins/text_style.mixin.js
Normal file
310
src/mixins/text_style.mixin.js
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
(function() {
|
||||
fabric.util.object.extend(fabric.Text.prototype, /** @lends fabric.Text.prototype */ {
|
||||
/**
|
||||
* Returns true if object has no styling or no styling in a line
|
||||
* @param {Number} lineIndex
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isEmptyStyles: function(lineIndex) {
|
||||
if (!this.styles) {
|
||||
return true;
|
||||
}
|
||||
if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
|
||||
return true;
|
||||
}
|
||||
var obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
|
||||
for (var p1 in obj) {
|
||||
for (var p2 in obj[p1]) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (var p3 in obj[p1][p2]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if object has a style property or has it ina specified line
|
||||
* @param {Number} lineIndex
|
||||
* @return {Boolean}
|
||||
*/
|
||||
styleHas: function(property, lineIndex) {
|
||||
if (!this.styles || !property || property === '') {
|
||||
return false;
|
||||
}
|
||||
if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
|
||||
return false;
|
||||
}
|
||||
var obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
|
||||
// eslint-disable-next-line
|
||||
for (var p1 in obj) {
|
||||
// eslint-disable-next-line
|
||||
for (var 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.
|
||||
*
|
||||
* @param {string} property The property to compare between characters and text.
|
||||
*/
|
||||
cleanStyle: function(property) {
|
||||
if (!this.styles || !property || property === '') {
|
||||
return false;
|
||||
}
|
||||
var obj = this.styles, stylesCount = 0, letterCount, foundStyle = false, style,
|
||||
canBeSwapped = true, graphemeCount = 0;
|
||||
// eslint-disable-next-line
|
||||
for (var p1 in obj) {
|
||||
letterCount = 0;
|
||||
// eslint-disable-next-line
|
||||
for (var p2 in obj[p1]) {
|
||||
stylesCount++;
|
||||
if (!foundStyle) {
|
||||
style = obj[p1][p2][property];
|
||||
foundStyle = true;
|
||||
}
|
||||
else if (obj[p1][p2][property] !== style) {
|
||||
canBeSwapped = false;
|
||||
}
|
||||
if (obj[p1][p2][property] === this[property]) {
|
||||
delete obj[p1][p2][property];
|
||||
}
|
||||
if (Object.keys(obj[p1][p2]).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 (var i = 0; i < this._textLines.length; i++) {
|
||||
graphemeCount += this._textLines[i].length;
|
||||
}
|
||||
if (canBeSwapped && stylesCount === graphemeCount) {
|
||||
this[property] = style;
|
||||
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: function(property) {
|
||||
if (!this.styles || !property || property === '') {
|
||||
return;
|
||||
}
|
||||
var obj = this.styles, 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];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_extendStyles: function(index, styles) {
|
||||
var loc = this.get2DCursorLocation(index);
|
||||
|
||||
if (!this._getLineStyle(loc.lineIndex)) {
|
||||
this._setLineStyle(loc.lineIndex, {});
|
||||
}
|
||||
|
||||
if (!this._getStyleDeclaration(loc.lineIndex, loc.charIndex)) {
|
||||
this._setStyleDeclaration(loc.lineIndex, loc.charIndex, {});
|
||||
}
|
||||
|
||||
fabric.util.object.extend(this._getStyleDeclaration(loc.lineIndex, loc.charIndex), styles);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)
|
||||
* @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
|
||||
* @param {Boolean} [skipWrapping] consider the location for unwrapped lines. usefull to manage styles.
|
||||
*/
|
||||
get2DCursorLocation: function(selectionStart, skipWrapping) {
|
||||
if (typeof selectionStart === 'undefined') {
|
||||
selectionStart = this.selectionStart;
|
||||
}
|
||||
var lines = skipWrapping ? this._unwrappedTextLines : this._textLines;
|
||||
var len = lines.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (selectionStart <= lines[i].length) {
|
||||
return {
|
||||
lineIndex: i,
|
||||
charIndex: selectionStart
|
||||
};
|
||||
}
|
||||
selectionStart -= lines[i].length + 1;
|
||||
}
|
||||
return {
|
||||
lineIndex: i - 1,
|
||||
charIndex: lines[i - 1].length < selectionStart ? lines[i - 1].length : selectionStart
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets style of a current selection/cursor (at the start position)
|
||||
* if startIndex or endIndex are not provided, slectionStart or selectionEnd will be used.
|
||||
* @param {Number} [startIndex] Start index to get styles at
|
||||
* @param {Number} [endIndex] End index to get styles at, if not specified selectionEnd or startIndex + 1
|
||||
* @param {Boolean} [complete] get full style or not
|
||||
* @return {Array} styles an array with one, zero or more Style objects
|
||||
*/
|
||||
getSelectionStyles: function(startIndex, endIndex, complete) {
|
||||
if (typeof startIndex === 'undefined') {
|
||||
startIndex = this.selectionStart || 0;
|
||||
}
|
||||
if (typeof endIndex === 'undefined') {
|
||||
endIndex = this.selectionEnd || startIndex;
|
||||
}
|
||||
var styles = [];
|
||||
for (var i = startIndex; i < endIndex; 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: function(position, complete) {
|
||||
var loc = this.get2DCursorLocation(position),
|
||||
style = complete ? this.getCompleteStyleDeclaration(loc.lineIndex, loc.charIndex) :
|
||||
this._getStyleDeclaration(loc.lineIndex, loc.charIndex);
|
||||
return style || {};
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 selectionEnd or startIndex + 1
|
||||
* @return {fabric.IText} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setSelectionStyles: function(styles, startIndex, endIndex) {
|
||||
if (typeof startIndex === 'undefined') {
|
||||
startIndex = this.selectionStart || 0;
|
||||
}
|
||||
if (typeof endIndex === 'undefined') {
|
||||
endIndex = this.selectionEnd || startIndex;
|
||||
}
|
||||
for (var i = startIndex; i < endIndex; i++) {
|
||||
this._extendStyles(i, styles);
|
||||
}
|
||||
/* not included in _extendStyles to avoid clearing cache more than once */
|
||||
this._forceClearCache = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the reference, not a clone, of the style object for a given character
|
||||
* @param {Number} lineIndex
|
||||
* @param {Number} charIndex
|
||||
* @return {Object} style object
|
||||
*/
|
||||
_getStyleDeclaration: function(lineIndex, charIndex) {
|
||||
var lineStyle = this.styles && this.styles[lineIndex];
|
||||
if (!lineStyle) {
|
||||
return null;
|
||||
}
|
||||
return 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: function(lineIndex, charIndex) {
|
||||
var style = this._getStyleDeclaration(lineIndex, charIndex) || { },
|
||||
styleObject = { }, prop;
|
||||
for (var i = 0; i < this._styleProperties.length; i++) {
|
||||
prop = this._styleProperties[i];
|
||||
styleObject[prop] = typeof style[prop] === 'undefined' ? this[prop] : style[prop];
|
||||
}
|
||||
return styleObject;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @param {Number} charIndex
|
||||
* @param {Object} style
|
||||
* @private
|
||||
*/
|
||||
_setStyleDeclaration: function(lineIndex, charIndex, style) {
|
||||
this.styles[lineIndex][charIndex] = style;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} lineIndex
|
||||
* @param {Number} charIndex
|
||||
* @private
|
||||
*/
|
||||
_deleteStyleDeclaration: function(lineIndex, charIndex) {
|
||||
delete this.styles[lineIndex][charIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @private
|
||||
*/
|
||||
_getLineStyle: function(lineIndex) {
|
||||
return this.styles[lineIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @param {Object} style
|
||||
* @private
|
||||
*/
|
||||
_setLineStyle: function(lineIndex, style) {
|
||||
this.styles[lineIndex] = style;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @private
|
||||
*/
|
||||
_deleteLineStyle: function(lineIndex) {
|
||||
delete this.styles[lineIndex];
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
|
@ -223,50 +223,6 @@
|
|||
this.canvas && this.canvas.fire('text:selection:changed', { target: this });
|
||||
},
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {Boolean} [endIndex] End index to get styles at
|
||||
* @return {Object} styles Style object at a specified (or current) index
|
||||
*/
|
||||
getSelectionStyles: function(startIndex, endIndex, complete) {
|
||||
|
||||
if (endIndex && startIndex !== endIndex) {
|
||||
var styles = [];
|
||||
for (var i = startIndex; i < endIndex; i++) {
|
||||
styles.push(this.getSelectionStyles(i, i, complete));
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
|
||||
var loc = this.get2DCursorLocation(startIndex),
|
||||
style = complete ? this.getCompleteStyleDeclaration(loc.lineIndex, loc.charIndex) :
|
||||
this._getStyleDeclaration(loc.lineIndex, loc.charIndex);
|
||||
|
||||
return style || {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets style of a current selection, if no selection exist, do not set anything.
|
||||
* @param {Object} [styles] Styles object
|
||||
* @return {fabric.IText} thisArg
|
||||
* @chainable
|
||||
*/
|
||||
setSelectionStyles: function(styles) {
|
||||
if (this.selectionStart === this.selectionEnd) {
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
for (var i = this.selectionStart; i < this.selectionEnd; i++) {
|
||||
this._extendStyles(i, styles);
|
||||
}
|
||||
}
|
||||
/* not included in _extendStyles to avoid clearing cache more than once */
|
||||
this._forceClearCache = true;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize text dimensions. Render all text on given context
|
||||
* or on a offscreen canvas to get the text width with measureText.
|
||||
|
|
@ -350,31 +306,6 @@
|
|||
var width = this.width + 4, height = this.height + 4;
|
||||
ctx.clearRect(-width / 2, -height / 2, width, height);
|
||||
},
|
||||
/**
|
||||
* Returns 2d representation (lineIndex and charIndex) of cursor (or selection start)
|
||||
* @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
|
||||
* @param {Boolean} [skipWrapping] consider the location for unwrapped lines. usefull to manage styles.
|
||||
*/
|
||||
get2DCursorLocation: function(selectionStart, skipWrapping) {
|
||||
if (typeof selectionStart === 'undefined') {
|
||||
selectionStart = this.selectionStart;
|
||||
}
|
||||
var lines = skipWrapping ? this._unwrappedTextLines : this._textLines;
|
||||
var len = lines.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (selectionStart <= lines[i].length) {
|
||||
return {
|
||||
lineIndex: i,
|
||||
charIndex: selectionStart
|
||||
};
|
||||
}
|
||||
selectionStart -= lines[i].length + 1;
|
||||
}
|
||||
return {
|
||||
lineIndex: i - 1,
|
||||
charIndex: lines[i - 1].length < selectionStart ? lines[i - 1].length : selectionStart
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns cursor boundaries (left, top, leftOffset, topOffset)
|
||||
|
|
|
|||
|
|
@ -309,152 +309,6 @@
|
|||
return fabric._measuringContext;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if object has no styling or no styling in a line
|
||||
* @param {Number} lineIndex
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isEmptyStyles: function(lineIndex) {
|
||||
if (!this.styles) {
|
||||
return true;
|
||||
}
|
||||
if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
|
||||
return true;
|
||||
}
|
||||
var obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
|
||||
for (var p1 in obj) {
|
||||
for (var p2 in obj[p1]) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (var p3 in obj[p1][p2]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if object has a style property or has it ina specified line
|
||||
* @param {Number} lineIndex
|
||||
* @return {Boolean}
|
||||
*/
|
||||
styleHas: function(property, lineIndex) {
|
||||
if (!this.styles || !property || property === '') {
|
||||
return false;
|
||||
}
|
||||
if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
|
||||
return false;
|
||||
}
|
||||
var obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] };
|
||||
// eslint-disable-next-line
|
||||
for (var p1 in obj) {
|
||||
// eslint-disable-next-line
|
||||
for (var 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.
|
||||
*
|
||||
* @param {string} property The property to compare between characters and text.
|
||||
*/
|
||||
cleanStyle: function(property) {
|
||||
if (!this.styles || !property || property === '') {
|
||||
return false;
|
||||
}
|
||||
var obj = this.styles, stylesCount = 0, letterCount, foundStyle = false, style,
|
||||
canBeSwapped = true, graphemeCount = 0;
|
||||
// eslint-disable-next-line
|
||||
for (var p1 in obj) {
|
||||
letterCount = 0;
|
||||
// eslint-disable-next-line
|
||||
for (var p2 in obj[p1]) {
|
||||
stylesCount++;
|
||||
if (!foundStyle) {
|
||||
style = obj[p1][p2][property];
|
||||
foundStyle = true;
|
||||
}
|
||||
else if (obj[p1][p2][property] !== style) {
|
||||
canBeSwapped = false;
|
||||
}
|
||||
if (obj[p1][p2][property] === this[property]) {
|
||||
delete obj[p1][p2][property];
|
||||
}
|
||||
if (Object.keys(obj[p1][p2]).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 (var i = 0; i < this._textLines.length; i++) {
|
||||
graphemeCount += this._textLines[i].length;
|
||||
}
|
||||
if (canBeSwapped && stylesCount === graphemeCount) {
|
||||
this[property] = style;
|
||||
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: function(property) {
|
||||
if (!this.styles || !property || property === '') {
|
||||
return;
|
||||
}
|
||||
var obj = this.styles, 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];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_extendStyles: function(index, styles) {
|
||||
var loc = this.get2DCursorLocation(index);
|
||||
|
||||
if (!this._getLineStyle(loc.lineIndex)) {
|
||||
this._setLineStyle(loc.lineIndex, {});
|
||||
}
|
||||
|
||||
if (!this._getStyleDeclaration(loc.lineIndex, loc.charIndex)) {
|
||||
this._setStyleDeclaration(loc.lineIndex, loc.charIndex, {});
|
||||
}
|
||||
|
||||
fabric.util.object.extend(this._getStyleDeclaration(loc.lineIndex, loc.charIndex), styles);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize or update text dimensions.
|
||||
* Updates this.width and this.height with the proper values.
|
||||
|
|
@ -700,82 +554,6 @@
|
|||
ctx.font = this._getFontDeclaration(styleDeclaration);
|
||||
},
|
||||
|
||||
/**
|
||||
* get the reference, not a clone, of the style object for a given character
|
||||
* @param {Number} lineIndex
|
||||
* @param {Number} charIndex
|
||||
* @return {Object} style object
|
||||
*/
|
||||
_getStyleDeclaration: function(lineIndex, charIndex) {
|
||||
var lineStyle = this.styles && this.styles[lineIndex];
|
||||
if (!lineStyle) {
|
||||
return null;
|
||||
}
|
||||
return 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: function(lineIndex, charIndex) {
|
||||
var style = this._getStyleDeclaration(lineIndex, charIndex) || { },
|
||||
styleObject = { }, prop;
|
||||
for (var i = 0; i < this._styleProperties.length; i++) {
|
||||
prop = this._styleProperties[i];
|
||||
styleObject[prop] = typeof style[prop] === 'undefined' ? this[prop] : style[prop];
|
||||
}
|
||||
return styleObject;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @param {Number} charIndex
|
||||
* @param {Object} style
|
||||
* @private
|
||||
*/
|
||||
_setStyleDeclaration: function(lineIndex, charIndex, style) {
|
||||
this.styles[lineIndex][charIndex] = style;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} lineIndex
|
||||
* @param {Number} charIndex
|
||||
* @private
|
||||
*/
|
||||
_deleteStyleDeclaration: function(lineIndex, charIndex) {
|
||||
delete this.styles[lineIndex][charIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @private
|
||||
*/
|
||||
_getLineStyle: function(lineIndex) {
|
||||
return this.styles[lineIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @param {Object} style
|
||||
* @private
|
||||
*/
|
||||
_setLineStyle: function(lineIndex, style) {
|
||||
this.styles[lineIndex] = style;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} lineIndex
|
||||
* @private
|
||||
*/
|
||||
_deleteLineStyle: function(lineIndex) {
|
||||
delete this.styles[lineIndex];
|
||||
},
|
||||
|
||||
/**
|
||||
* measure and return the width of a single character.
|
||||
* possibly overridden to accommodate different measure logic or
|
||||
|
|
|
|||
|
|
@ -573,48 +573,21 @@
|
|||
iText.selectionStart = 0;
|
||||
iText.selectionEnd = 0;
|
||||
|
||||
deepEqual(iText.getSelectionStyles(), {
|
||||
textDecoration: 'underline'
|
||||
});
|
||||
deepEqual(iText.getSelectionStyles(), []);
|
||||
|
||||
iText.selectionStart = 2;
|
||||
iText.selectionEnd = 2;
|
||||
iText.selectionEnd = 3;
|
||||
|
||||
deepEqual(iText.getSelectionStyles(), {
|
||||
deepEqual(iText.getSelectionStyles(), [{
|
||||
textDecoration: 'overline'
|
||||
});
|
||||
}]);
|
||||
|
||||
iText.selectionStart = 17;
|
||||
iText.selectionStart = 17;
|
||||
iText.selectionEnd = 18;
|
||||
|
||||
deepEqual(iText.getSelectionStyles(), {
|
||||
deepEqual(iText.getSelectionStyles(), [{
|
||||
fill: 'red'
|
||||
});
|
||||
});
|
||||
|
||||
test('getSelectionStyles with 1 arg', function() {
|
||||
|
||||
var iText = new fabric.IText('test foo bar-baz\nqux', {
|
||||
styles: {
|
||||
0: {
|
||||
0: { textDecoration: 'underline' },
|
||||
2: { textDecoration: 'overline' },
|
||||
4: { textBackgroundColor: '#ffc' }
|
||||
},
|
||||
1: {
|
||||
0: { fill: 'red' },
|
||||
1: { fill: 'green' },
|
||||
2: { fill: 'blue' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
iText.selectionStart = 17;
|
||||
iText.selectionStart = 17;
|
||||
|
||||
deepEqual(iText.getSelectionStyles(2), {
|
||||
textDecoration: 'overline'
|
||||
});
|
||||
}]);
|
||||
});
|
||||
|
||||
test('getSelectionStyles with 2 args', function() {
|
||||
|
|
|
|||
|
|
@ -381,5 +381,154 @@
|
|||
var cache2 = text2.getFontCache(text2);
|
||||
equal(cache, cache2, 'you get the same cache');
|
||||
});
|
||||
// moved
|
||||
test('getSelectionStyles with no arguments', function() {
|
||||
var iText = new fabric.Text('test foo bar-baz\nqux', {
|
||||
styles: {
|
||||
0: {
|
||||
0: { textDecoration: 'underline' },
|
||||
2: { textDecoration: 'overline' },
|
||||
4: { textBackgroundColor: '#ffc' }
|
||||
},
|
||||
1: {
|
||||
0: { fill: 'red' },
|
||||
1: { fill: 'green' },
|
||||
2: { fill: 'blue' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
equal(typeof iText.getSelectionStyles, 'function');
|
||||
|
||||
deepEqual(iText.getSelectionStyles(), []);
|
||||
|
||||
});
|
||||
|
||||
test('getSelectionStyles with 2 args', function() {
|
||||
var iText = new fabric.Text('test foo bar-baz\nqux', {
|
||||
styles: {
|
||||
0: {
|
||||
0: { textDecoration: 'underline' },
|
||||
2: { textDecoration: 'overline' },
|
||||
4: { textBackgroundColor: '#ffc' }
|
||||
},
|
||||
1: {
|
||||
0: { fill: 'red' },
|
||||
1: { fill: 'green' },
|
||||
2: { fill: 'blue' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
deepEqual(iText.getSelectionStyles(0, 5), [
|
||||
{ textDecoration: 'underline' },
|
||||
{},
|
||||
{ textDecoration: 'overline' },
|
||||
{},
|
||||
{ textBackgroundColor: '#ffc' },
|
||||
]);
|
||||
|
||||
deepEqual(iText.getSelectionStyles(2, 2), [
|
||||
]);
|
||||
});
|
||||
|
||||
test('setSelectionStyles', function() {
|
||||
var iText = new fabric.Text('test foo bar-baz\nqux', {
|
||||
styles: {
|
||||
0: {
|
||||
0: { fill: '#112233' },
|
||||
2: { stroke: '#223344' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
equal(typeof iText.setSelectionStyles, 'function');
|
||||
|
||||
iText.setSelectionStyles({
|
||||
fill: 'red',
|
||||
stroke: 'yellow'
|
||||
});
|
||||
|
||||
deepEqual(iText.styles[0][0], {
|
||||
fill: '#112233'
|
||||
});
|
||||
|
||||
iText.setSelectionStyles({
|
||||
fill: 'red',
|
||||
stroke: 'yellow'
|
||||
}, 0, 1);
|
||||
|
||||
deepEqual(iText.styles[0][0], {
|
||||
fill: 'red',
|
||||
stroke: 'yellow'
|
||||
});
|
||||
|
||||
iText.setSelectionStyles({
|
||||
fill: '#998877',
|
||||
stroke: 'yellow'
|
||||
}, 2, 3);
|
||||
|
||||
deepEqual(iText.styles[0][2], {
|
||||
fill: '#998877',
|
||||
stroke: 'yellow'
|
||||
});
|
||||
});
|
||||
|
||||
test('getStyleAtPosition', function() {
|
||||
var iText = new fabric.Text('test foo bar-baz\nqux', {
|
||||
styles: {
|
||||
0: {
|
||||
0: { textDecoration: 'underline' },
|
||||
2: { textDecoration: 'overline' },
|
||||
4: { textBackgroundColor: '#ffc' }
|
||||
},
|
||||
1: {
|
||||
0: { fill: 'red' },
|
||||
1: { fill: 'green' },
|
||||
2: { fill: 'blue' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
equal(typeof iText.getStyleAtPosition, 'function');
|
||||
|
||||
deepEqual(iText.getStyleAtPosition(2), { textDecoration: 'overline' });
|
||||
|
||||
deepEqual(iText.getStyleAtPosition(1), { });
|
||||
|
||||
deepEqual(iText.getStyleAtPosition(18), { fill: 'green' });
|
||||
});
|
||||
|
||||
test('getStyleAtPosition complete', function() {
|
||||
var iText = new fabric.Text('test foo bar-baz\nqux', {
|
||||
styles: {
|
||||
0: {
|
||||
0: { textDecoration: 'underline' },
|
||||
2: { textDecoration: 'overline' },
|
||||
4: { textBackgroundColor: '#ffc' }
|
||||
},
|
||||
1: {
|
||||
0: { fill: 'red' },
|
||||
1: { fill: 'green' },
|
||||
2: { fill: 'blue' }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
equal(typeof iText.getStyleAtPosition, 'function');
|
||||
|
||||
deepEqual(iText.getStyleAtPosition(2, true), {
|
||||
stroke: null,
|
||||
strokeWidth: 1,
|
||||
fill: 'rgb(0,0,0)',
|
||||
fontFamily: 'Times New Roman',
|
||||
fontSize: 40,
|
||||
fontWeight: 'normal',
|
||||
fontStyle: 'normal',
|
||||
underline: false,
|
||||
overline: false,
|
||||
linethrough: false,
|
||||
textBackgroundColor: ''
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
|
|
|||
Loading…
Reference in a new issue