From 5135e3037119e1a0a2f484b3da6befc42e3f51bb Mon Sep 17 00:00:00 2001 From: Andrea Bogazzi Date: Sat, 24 Feb 2018 18:45:23 +0100 Subject: [PATCH] isEmptyStyles for textbox (#4762) * isEMptyStyles for textbox * added tests * added missing file --- src/mixins/text_style.mixin.js | 2 +- src/shapes/textbox.class.js | 47 +++++++++---- test.js | 79 +++++++++++----------- test/unit/textbox.js | 117 +++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 52 deletions(-) create mode 100644 test/unit/textbox.js diff --git a/src/mixins/text_style.mixin.js b/src/mixins/text_style.mixin.js index 1a7ecba5343..149e54de17a 100644 --- a/src/mixins/text_style.mixin.js +++ b/src/mixins/text_style.mixin.js @@ -2,7 +2,7 @@ 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 + * @param {Number} lineIndex , lineIndex is on wrapped lines. * @return {Boolean} */ isEmptyStyles: function(lineIndex) { diff --git a/src/shapes/textbox.class.js b/src/shapes/textbox.class.js index e092a9ff241..593ebc313ef 100644 --- a/src/shapes/textbox.class.js +++ b/src/shapes/textbox.class.js @@ -64,18 +64,6 @@ */ _dimensionAffectingProps: fabric.Text.prototype._dimensionAffectingProps.concat('width'), - /** - * Constructor. Some scaling related property values are forced. Visibility - * of controls is also fixed; only the rotation and width controls are - * made available. - * @param {String} text Text string - * @param {Object} [options] Options object - * @return {fabric.Textbox} thisArg - */ - initialize: function(text, options) { - this.callSuper('initialize', text, options); - }, - /** * Unlike superclass's version of this function, Textbox does not update * its width. @@ -155,6 +143,38 @@ return fabric.Text.prototype.styleHas.call(this, property, lineIndex); }, + /** + * Returns true if object has no styling or no styling in a line + * @param {Number} lineIndex , lineIndex is on wrapped lines. + * @return {Boolean} + */ + isEmptyStyles: function(lineIndex) { + var offset = 0, nextLineIndex = lineIndex + 1, nextOffset, obj, shouldLimit = false; + var map = this._styleMap[lineIndex]; + var mapNextLine = this._styleMap[lineIndex + 1]; + if (map) { + lineIndex = map.line; + offset = map.offset; + } + if (mapNextLine) { + nextLineIndex = mapNextLine.line; + shouldLimit = nextLineIndex === lineIndex; + nextOffset = mapNextLine.offset; + } + obj = typeof lineIndex === 'undefined' ? this.styles : { line: this.styles[lineIndex] }; + for (var p1 in obj) { + for (var p2 in obj[p1]) { + if (p2 >= offset && (!shouldLimit || p2 < nextOffset)) { + // eslint-disable-next-line no-unused-vars + for (var p3 in obj[p1][p2]) { + return false; + } + } + } + } + return true; + }, + /** * @param {Number} lineIndex * @param {Number} charIndex @@ -200,6 +220,7 @@ }, /** + * probably broken need a fix * @param {Number} lineIndex * @private */ @@ -209,6 +230,7 @@ }, /** + * probably broken need a fix * @param {Number} lineIndex * @param {Object} style * @private @@ -219,6 +241,7 @@ }, /** + * probably broken need a fix * @param {Number} lineIndex * @private */ diff --git a/test.js b/test.js index f310c58a91c..c68ea9ecdb3 100644 --- a/test.js +++ b/test.js @@ -9,45 +9,46 @@ testrunner.options.coverage = true; testrunner.options.maxBlockDuration = 120000; testrunner.run({ - deps: "./test/fixtures/test_script.js", - code: "./dist/fabric.js", - tests: [ - './test/unit/activeselection.js', - './test/unit/animation.js', - './test/unit/rect.js', - './test/unit/ellipse.js', - './test/unit/color.js', - './test/unit/circle.js', - './test/unit/line.js', - './test/unit/polyline.js', - './test/unit/polygon.js', - './test/unit/path.js', - './test/unit/observable.js', - './test/unit/object.js', - './test/unit/text.js', - './test/unit/util.js', - './test/unit/brushes.js', - './test/unit/image.js', - './test/unit/image_filters.js', - './test/unit/group.js', - './test/unit/parser.js', - './test/unit/canvas.js', - './test/unit/canvas_static.js', - './test/unit/gradient.js', - './test/unit/pattern.js', - './test/unit/shadow.js', - './test/unit/object_interactivity.js', - './test/unit/object_geometry.js', - './test/unit/object_origin.js', - './test/unit/itext.js', - './test/unit/itext_click_behaviour.js', - './test/unit/itext_key_behaviour.js', - './test/unit/collection.js', - './test/unit/point.js', - './test/unit/intersection.js', - './test/unit/stateful.js' - ], - // tests: ['./test/unit/pattern.js'], + deps: './test/fixtures/test_script.js', + code: './dist/fabric.js', + tests: [ + './test/unit/activeselection.js', + './test/unit/animation.js', + './test/unit/rect.js', + './test/unit/ellipse.js', + './test/unit/color.js', + './test/unit/circle.js', + './test/unit/line.js', + './test/unit/polyline.js', + './test/unit/polygon.js', + './test/unit/path.js', + './test/unit/observable.js', + './test/unit/object.js', + './test/unit/text.js', + './test/unit/util.js', + './test/unit/brushes.js', + './test/unit/image.js', + './test/unit/image_filters.js', + './test/unit/group.js', + './test/unit/parser.js', + './test/unit/canvas.js', + './test/unit/canvas_static.js', + './test/unit/gradient.js', + './test/unit/pattern.js', + './test/unit/shadow.js', + './test/unit/object_interactivity.js', + './test/unit/object_geometry.js', + './test/unit/object_origin.js', + './test/unit/itext.js', + './test/unit/itext_click_behaviour.js', + './test/unit/itext_key_behaviour.js', + './test/unit/collection.js', + './test/unit/point.js', + './test/unit/intersection.js', + './test/unit/stateful.js', + './test/unit/textbox.js', + ], + // tests: ['./test/unit/pattern.js'], }, function(err, report) { if (err) { console.log(err); diff --git a/test/unit/textbox.js b/test/unit/textbox.js new file mode 100644 index 00000000000..3ea6e3622ee --- /dev/null +++ b/test/unit/textbox.js @@ -0,0 +1,117 @@ +(function() { + var canvas = this.canvas = new fabric.Canvas(); + QUnit.module('fabric.Textbox', { + afterEach: function() { + canvas.clear(); + } + }); + + var TEXTBOX_OBJECT = { + version: fabric.version, + type: 'textbox', + originX: 'left', + originY: 'top', + left: 0, + top: 0, + width: 20, + height: 45.2, + fill: 'rgb(0,0,0)', + stroke: null, + strokeWidth: 1, + strokeDashArray: null, + strokeLineCap: 'butt', + strokeLineJoin: 'miter', + strokeMiterLimit: 10, + scaleX: 1, + scaleY: 1, + angle: 0, + flipX: false, + flipY: false, + opacity: 1, + shadow: null, + visible: true, + clipTo: null, + text: 'x', + fontSize: 40, + fontWeight: 'normal', + fontFamily: 'Times New Roman', + fontStyle: 'normal', + lineHeight: 1.16, + underline: false, + overline: false, + linethrough: false, + textAlign: 'left', + backgroundColor: '', + textBackgroundColor: '', + fillRule: 'nonzero', + paintFirst: 'fill', + globalCompositeOperation: 'source-over', + skewX: 0, + skewY: 0, + transformMatrix: null, + charSpacing: 0, + styles: { }, + minWidth: 20, + }; + + QUnit.test('constructor', function(assert) { + var textbox = new fabric.Textbox('test'); + assert.ok(textbox instanceof fabric.Textbox); + assert.ok(textbox instanceof fabric.IText); + assert.ok(textbox instanceof fabric.Text); + }); + + QUnit.test('constructor with width', function(assert) { + var textbox = new fabric.Textbox('test', { width: 400 }); + assert.equal(textbox.width, 400, 'width is taken by contstructor'); + }); + + QUnit.test('constructor with width too small', function(assert) { + var textbox = new fabric.Textbox('test', { width: 5 }); + assert.equal(Math.round(textbox.width), 56, 'width is calculated by constructor'); + }); + + QUnit.test('initial properties', function(assert) { + var textbox = new fabric.Textbox('test'); + assert.equal(textbox.text, 'test'); + assert.equal(textbox.type, 'textbox'); + assert.deepEqual(textbox.styles, { }); + assert.ok(textbox.cacheProperties.indexOf('width') > -1, 'width is in cacheProperties'); + }); + + QUnit.test('toObject', function(assert) { + var textbox = new fabric.Textbox('x'); + var obj = textbox.toObject(); + assert.deepEqual(obj, TEXTBOX_OBJECT, 'JSON OUTPUT MATCH'); + }); + + QUnit.test('isEmptyStyles', function(assert) { + var textbox = new fabric.Textbox('x x', { width: 5, styles: { 0: { 0: { fill: 'red' } } } }); + assert.equal(textbox._textLines.length, 2, 'lines are wrapped'); + assert.equal(textbox._unwrappedTextLines.length, 1, 'there is only one text line'); + assert.equal(textbox.isEmptyStyles(), false, 'style is not empty'); + assert.equal(textbox.isEmptyStyles(0), false, 'style is not empty at line 0'); + assert.equal(textbox.isEmptyStyles(1), true, 'style is empty at line 1'); + }); + + QUnit.test('isEmptyStyles alternate lines', function(assert) { + var textbox = new fabric.Textbox('xa xb xc xd xe\nya yb', { + width: 5, + styles: { + 0: { 0: { fill: 'red' }, 1: { fill: 'blue' }, 9: { fill: 'red' }, 10: { fill: 'blue' } }, + 1: { 3: { fill: 'red' }, 4: { fill: 'blue' } }, + } + }); + assert.equal(textbox._textLines.length, 7, 'lines are wrapped'); + assert.equal(textbox._unwrappedTextLines.length, 2, 'there is only one text line'); + assert.equal(textbox.isEmptyStyles(), false, 'style is not empty'); + assert.equal(textbox.isEmptyStyles(0), false, 'style is not empty at line 0'); + assert.equal(textbox.isEmptyStyles(1), true, 'style is empty at line 1'); + assert.equal(textbox.isEmptyStyles(2), true, 'style is empty at line 2'); + assert.equal(textbox.isEmptyStyles(3), false, 'style is empty at line 3'); + assert.equal(textbox.isEmptyStyles(4), true, 'style is empty at line 4'); + assert.equal(textbox.isEmptyStyles(5), true, 'style is empty at line 5'); + assert.equal(textbox.isEmptyStyles(6), false, 'style is empty at line 6'); + }); + +})();