Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cleanStyle, removeStyle #4060

Merged
merged 2 commits into from
Jul 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion src/shapes/text.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@
* @return {Boolean}
*/
styleHas: function(property, lineIndex) {
if (!this.styles) {
if (!this.styles || !property || property === '') {
return false;
}
if (typeof lineIndex !== 'undefined' && !this.styles[lineIndex]) {
Expand All @@ -511,6 +511,86 @@
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) {
var 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
*/
Expand Down
54 changes: 54 additions & 0 deletions test/unit/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,58 @@
equal(removeTranslate(text.toSVG()), removeTranslate(TEXT_SVG_JUSTIFIED));
});

test('text styleHas', function() {
var text = new fabric.Text('xxxxxx\nx y');
text.styles = { };
ok(typeof text.styleHas === 'function');
equal(text.styleHas('stroke'), false, 'the text style has no stroke');
text.styles = { 1: { 0: { stroke: 'red' }}};
equal(text.styleHas('stroke'), true, 'the text style has stroke');
});

test('text cleanStyle', function() {
var text = new fabric.Text('xxxxxx\nx y');
text.styles = { 1: { 0: { stroke: 'red' }}};
text.stroke = 'red';
ok(typeof text.cleanStyle === 'function');
text.cleanStyle('stroke');
equal(text.styles[1], undefined, 'the style has been cleaned since stroke was equal to text property');
text.styles = { 1: { 0: { stroke: 'blue' }}};
text.stroke = 'red';
text.cleanStyle('stroke');
equal(text.styles[1][0].stroke, 'blue', 'nothing to clean, style untouched');
});

test('text cleanStyle with empty styles', function() {
var text = new fabric.Text('xxxxxx\nx y');
text.styles = { 1: { 0: { }, 1: { }}, 2: { }, 3: { 4: { }}};
text.cleanStyle('any');
equal(text.styles[1], undefined, 'the style has been cleaned since there were no usefull informations');
equal(text.styles[2], undefined, 'the style has been cleaned since there were no usefull informations');
equal(text.styles[3], undefined, 'the style has been cleaned since there were no usefull informations');
});

test('text cleanStyle with full style', function() {
var text = new fabric.Text('xxx');
text.styles = { 0: { 0: { fill: 'blue' }, 1: { fill: 'blue' }, 2: { fill: 'blue' }}};
text.fill = 'black';
text.cleanStyle('fill');
equal(text.fill, 'blue', 'the fill has been changed to blue');
equal(text.styles[0], undefined, 'all the style has been removed');
});

test('text removeStyle with some style', function() {
var text = new fabric.Text('xxx');
text.styles = { 0: { 0: { stroke: 'black', fill: 'blue' }, 1: { fill: 'blue' }, 2: { fill: 'blue' }}};
ok(typeof text.removeStyle === 'function');
text.fill = 'red';
text.removeStyle('fill');
equal(text.fill, 'red', 'the fill has not been changed');
equal(text.styles[0][0].stroke, 'black', 'the non fill part of the style is still there');
equal(text.styles[0][0].fill, undefined, 'the fill part of the style has been removed');
text.styles = { 0: { 0: { fill: 'blue' }, 1: { fill: 'blue' }, 2: { fill: 'blue' }}};
text.removeStyle('fill');
equal(text.styles[0], undefined, 'the styles got empty and has been removed');
});

})();