2015-05-27 18:47:13 +00:00
|
|
|
(function () {
|
2015-05-27 20:59:56 +00:00
|
|
|
var clone = fabric.util.object.clone;
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
/**
|
|
|
|
|
* Textbox class, based on IText, allows the user to resize the text rectangle
|
|
|
|
|
* and wraps lines automatically. Textboxes have their Y scaling locked, the
|
|
|
|
|
* user can only change width. Height is adjusted automatically based on the
|
|
|
|
|
* wrapping of lines.
|
|
|
|
|
* @class fabric.Textbox
|
|
|
|
|
* @extends fabric.IText
|
|
|
|
|
* @mixes fabric.Observable
|
|
|
|
|
* @return {fabric.Textbox} thisArg
|
|
|
|
|
* @see {@link fabric.Textbox#initialize} for constructor definition
|
|
|
|
|
*/
|
|
|
|
|
fabric.Textbox = fabric.util.createClass(fabric.IText, fabric.Observable, {
|
2015-05-27 18:47:13 +00:00
|
|
|
/**
|
2015-05-27 20:59:56 +00:00
|
|
|
* Type of an object
|
|
|
|
|
* @type String
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
type: 'textbox',
|
|
|
|
|
/**
|
|
|
|
|
* Minimum width of textbox, in pixels.
|
|
|
|
|
* @type Number
|
|
|
|
|
* @default
|
|
|
|
|
*/
|
|
|
|
|
minWidth: 20,
|
|
|
|
|
/**
|
|
|
|
|
* Cached array of text wrapping.
|
|
|
|
|
* @type Array
|
|
|
|
|
*/
|
|
|
|
|
__cachedLines: null,
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
2015-05-27 18:47:13 +00:00
|
|
|
* @return {fabric.Textbox} thisArg
|
|
|
|
|
*/
|
2015-05-27 20:59:56 +00:00
|
|
|
initialize: function (text, options) {
|
|
|
|
|
this.ctx = fabric.util.createCanvasElement().getContext('2d');
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
this.callSuper('initialize', text, options);
|
|
|
|
|
this.set({
|
|
|
|
|
lockUniScaling: false,
|
|
|
|
|
lockScalingY: true,
|
|
|
|
|
lockScalingFlip: true,
|
|
|
|
|
hasBorders: true
|
|
|
|
|
});
|
|
|
|
|
this.setControlsVisibility(fabric.Textbox.getTextboxControlVisibility());
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
// add width to this list of props that effect line wrapping.
|
|
|
|
|
this._dimensionAffectingProps.width = true;
|
|
|
|
|
},
|
|
|
|
|
/**
|
|
|
|
|
* Unlike superclass's version of this function, Textbox does not update
|
|
|
|
|
* its width.
|
|
|
|
|
* @param {CanvasRenderingContext2D} ctx Context to use for measurements
|
|
|
|
|
* @private
|
|
|
|
|
* @override
|
|
|
|
|
*/
|
|
|
|
|
_initDimensions: function (ctx) {
|
|
|
|
|
if (this.__skipDimension) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
if (!ctx) {
|
|
|
|
|
ctx = fabric.util.createCanvasElement().getContext('2d');
|
|
|
|
|
this._setTextStyles(ctx);
|
|
|
|
|
}
|
|
|
|
|
this._textLines = this._splitTextIntoLines();
|
2015-06-11 00:12:54 +00:00
|
|
|
this._styleMap = this._generateStyleMap();
|
2015-05-27 20:59:56 +00:00
|
|
|
this._clearCache();
|
|
|
|
|
this.height = this._getTextHeight(ctx);
|
|
|
|
|
},
|
2015-06-11 00:12:54 +00:00
|
|
|
|
|
|
|
|
_generateStyleMap: function() {
|
2015-06-11 17:25:43 +00:00
|
|
|
var realLineCount = 0,
|
|
|
|
|
realLineCharCount = 0,
|
|
|
|
|
charCount = 0,
|
|
|
|
|
map = {};
|
2015-06-11 00:12:54 +00:00
|
|
|
|
2015-06-11 17:25:43 +00:00
|
|
|
for (var i = 0; i < this._textLines.length; i++) {
|
|
|
|
|
if (this.text[charCount] === '\n') {
|
2015-06-11 00:12:54 +00:00
|
|
|
realLineCharCount = 0;
|
|
|
|
|
charCount++;
|
|
|
|
|
realLineCount++;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-11 17:25:43 +00:00
|
|
|
map[i] = { line: realLineCount, offset: realLineCharCount };
|
2015-06-11 00:12:54 +00:00
|
|
|
|
|
|
|
|
charCount += this._textLines[i].length;
|
|
|
|
|
realLineCharCount += this._textLines[i].length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return map;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Number} lineIndex
|
|
|
|
|
* @param {Number} charIndex
|
|
|
|
|
* @param {Boolean} [returnCloneOrEmpty=false]
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
_getStyleDeclaration: function(lineIndex, charIndex, returnCloneOrEmpty) {
|
|
|
|
|
if(this._styleMap) {
|
|
|
|
|
var map = this._styleMap[lineIndex];
|
|
|
|
|
lineIndex = map.line;
|
|
|
|
|
charIndex = map.offset + charIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(returnCloneOrEmpty) {
|
|
|
|
|
return (this.styles[lineIndex] && this.styles[lineIndex][charIndex])
|
|
|
|
|
? clone(this.styles[lineIndex][charIndex])
|
|
|
|
|
: { };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.styles[lineIndex] && this.styles[lineIndex][charIndex] ? this.styles[lineIndex][charIndex] : null;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Number} lineIndex
|
|
|
|
|
* @param {Number} charIndex
|
|
|
|
|
* @param {Object} style
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
_setStyleDeclaration: function(lineIndex, charIndex, style) {
|
|
|
|
|
var map = this._styleMap[lineIndex];
|
|
|
|
|
lineIndex = map.line;
|
|
|
|
|
charIndex = map.offset + charIndex;
|
|
|
|
|
|
|
|
|
|
this.styles[lineIndex][charIndex] = style;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Number} lineIndex
|
|
|
|
|
* @param {Number} charIndex
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
_deleteStyleDeclaration: function(lineIndex, charIndex) {
|
|
|
|
|
var map = this._styleMap[lineIndex];
|
|
|
|
|
lineIndex = map.line;
|
|
|
|
|
charIndex = map.offset + charIndex;
|
|
|
|
|
|
|
|
|
|
delete this.styles[lineIndex][charIndex];
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Number} lineIndex
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
_getLineStyle: function(lineIndex) {
|
|
|
|
|
var map = this._styleMap[lineIndex];
|
|
|
|
|
return this.styles[map.line];
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Number} lineIndex
|
|
|
|
|
* @param {Object} style
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
_setLineStyle: function(lineIndex, style) {
|
|
|
|
|
var map = this._styleMap[lineIndex];
|
|
|
|
|
this.styles[map.line] = style;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {Number} lineIndex
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
_deleteLineStyle: function(lineIndex) {
|
|
|
|
|
var map = this._styleMap[lineIndex];
|
|
|
|
|
delete this.styles[map.line];
|
|
|
|
|
},
|
|
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
/**
|
|
|
|
|
* Wraps text using the 'width' property of Textbox. First this function
|
|
|
|
|
* splits text on newlines, so we preserve newlines entered by the user.
|
|
|
|
|
* Then it wraps each line using the width of the Textbox by calling
|
|
|
|
|
* _wrapLine().
|
|
|
|
|
* @param {CanvasRenderingContext2D} ctx Context to use for measurements
|
|
|
|
|
* @param {String} text The string of text that is split into lines
|
|
|
|
|
* @returns {Array} Array of lines
|
|
|
|
|
*/
|
|
|
|
|
_wrapText: function (ctx, text) {
|
2015-06-11 00:12:54 +00:00
|
|
|
var lines = text.split(this._reNewline), wrapped = [], i;
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
for (i = 0; i < lines.length; i++) {
|
2015-06-11 00:12:54 +00:00
|
|
|
wrapped = wrapped.concat(this._wrapLine(ctx, lines[i], i));
|
2015-05-27 20:59:56 +00:00
|
|
|
}
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
return wrapped;
|
|
|
|
|
},
|
2015-06-03 23:39:10 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Helper function to measure a string of text, given its lineIndex and charIndex offset
|
|
|
|
|
*
|
|
|
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
|
|
|
* @param {String} text
|
|
|
|
|
* @param {number} lineIndex
|
|
|
|
|
* @param {number} charOffset
|
|
|
|
|
* @returns {number}
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
2015-06-03 23:49:38 +00:00
|
|
|
_measureText: function (ctx, text, lineIndex, charOffset) {
|
2015-06-03 23:39:10 +00:00
|
|
|
var width = 0, decl;
|
|
|
|
|
charOffset = charOffset || 0;
|
|
|
|
|
|
2015-06-11 00:12:54 +00:00
|
|
|
for (var i = 0; i < text.length; i++) {
|
2015-06-11 17:55:18 +00:00
|
|
|
if (this.styles && this.styles[lineIndex] && (decl = this.styles[lineIndex][i + charOffset])) {
|
2015-06-03 23:39:10 +00:00
|
|
|
ctx.save();
|
|
|
|
|
width += this._applyCharStylesGetWidth(ctx, text[i], lineIndex, i, decl);
|
|
|
|
|
ctx.restore();
|
2015-06-04 00:07:42 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2015-06-03 23:39:10 +00:00
|
|
|
width += this._applyCharStylesGetWidth(ctx, text[i], lineIndex, i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return width;
|
|
|
|
|
},
|
|
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
/**
|
|
|
|
|
* Wraps a line of text using the width of the Textbox and a context.
|
|
|
|
|
* @param {CanvasRenderingContext2D} ctx Context to use for measurements
|
|
|
|
|
* @param {String} text The string of text to split into lines
|
2015-06-11 00:12:54 +00:00
|
|
|
* @param {Number} lineIndex
|
2015-05-27 20:59:56 +00:00
|
|
|
* @returns {Array} Array of line(s) into which the given text is wrapped
|
|
|
|
|
* to.
|
|
|
|
|
*/
|
2015-06-03 23:39:10 +00:00
|
|
|
_wrapLine: function (ctx, text, lineIndex) {
|
2015-05-27 20:59:56 +00:00
|
|
|
var maxWidth = this.width, words = text.split(' '),
|
|
|
|
|
lines = [],
|
|
|
|
|
line = '';
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-06-11 00:12:54 +00:00
|
|
|
var offset = 0;
|
|
|
|
|
|
|
|
|
|
if (this._measureText(ctx, text, lineIndex, offset) < maxWidth) {
|
2015-05-27 20:59:56 +00:00
|
|
|
lines.push(text);
|
2015-05-29 05:41:23 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2015-05-27 20:59:56 +00:00
|
|
|
while (words.length > 0) {
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
/*
|
|
|
|
|
* If the textbox's width is less than the widest letter.
|
|
|
|
|
* TODO: Performance improvement - cache the width of W whenever
|
|
|
|
|
* fontSize changes.
|
|
|
|
|
*/
|
|
|
|
|
if (maxWidth <= ctx.measureText('W').width) {
|
|
|
|
|
return text.split('');
|
|
|
|
|
}
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
/*
|
|
|
|
|
* This handles a word that is longer than the width of the
|
|
|
|
|
* text area.
|
|
|
|
|
*/
|
2015-06-11 00:12:54 +00:00
|
|
|
while (Math.ceil(this._measureText(ctx, words[0], lineIndex, offset)) >= maxWidth) {
|
2015-05-27 20:59:56 +00:00
|
|
|
var tmp = words[0];
|
|
|
|
|
words[0] = tmp.slice(0, -1);
|
2015-05-28 04:30:17 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
if (words.length > 1) {
|
|
|
|
|
words[1] = tmp.slice(-1) + words[1];
|
2015-05-29 05:41:23 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2015-05-27 20:59:56 +00:00
|
|
|
words.push(tmp.slice(-1));
|
2015-05-27 18:47:13 +00:00
|
|
|
}
|
2015-05-27 20:59:56 +00:00
|
|
|
}
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-06-11 00:12:54 +00:00
|
|
|
if (Math.ceil(this._measureText(ctx, line + words[0], lineIndex, offset)) < maxWidth) {
|
2015-05-27 20:59:56 +00:00
|
|
|
line += words.shift() + ' ';
|
2015-05-29 05:41:23 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2015-06-11 00:12:54 +00:00
|
|
|
offset += line.length;
|
2015-05-27 20:59:56 +00:00
|
|
|
lines.push(line);
|
|
|
|
|
line = '';
|
|
|
|
|
}
|
2015-05-28 04:30:17 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
if (words.length === 0) {
|
|
|
|
|
lines.push(line.substring(0, line.length - 1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
return lines;
|
|
|
|
|
},
|
|
|
|
|
/**
|
|
|
|
|
* Gets lines of text to render in the Textbox. This function calculates
|
|
|
|
|
* text wrapping on the fly everytime it is called.
|
|
|
|
|
* @returns {Array} Array of lines in the Textbox.
|
|
|
|
|
* @override
|
|
|
|
|
*/
|
|
|
|
|
_splitTextIntoLines: function () {
|
|
|
|
|
this.ctx.save();
|
|
|
|
|
this._setTextStyles(this.ctx);
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-06-03 23:39:10 +00:00
|
|
|
var lines = this._wrapText(this.ctx, this.text);
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
this.ctx.restore();
|
|
|
|
|
return lines;
|
|
|
|
|
},
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
/**
|
|
|
|
|
* When part of a group, we don't want the Textbox's scale to increase if
|
|
|
|
|
* the group's increases. That's why we reduce the scale of the Textbox by
|
|
|
|
|
* the amount that the group's increases. This is to maintain the effective
|
|
|
|
|
* scale of the Textbox at 1, so that font-size values make sense. Otherwise
|
|
|
|
|
* the same font-size value would result in different actual size depending
|
|
|
|
|
* on the value of the scale.
|
|
|
|
|
* @param {String} key
|
|
|
|
|
* @param {Any} value
|
|
|
|
|
*/
|
|
|
|
|
setOnGroup: function (key, value) {
|
|
|
|
|
if (key === 'scaleX') {
|
2015-05-31 22:44:44 +00:00
|
|
|
this.set('scaleX', Math.abs(1 / value));
|
2015-05-27 20:59:56 +00:00
|
|
|
this.set('width', (this.get('width') * value) /
|
|
|
|
|
(typeof this.__oldScaleX === 'undefined' ? 1 : this.__oldScaleX));
|
|
|
|
|
this.__oldScaleX = value;
|
|
|
|
|
}
|
|
|
|
|
},
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-27 20:59:56 +00:00
|
|
|
/**
|
|
|
|
|
* Returns 2d representation (lineIndex and charIndex) of cursor (or selection start).
|
|
|
|
|
* Overrides the superclass function to take into account text wrapping.
|
2015-05-28 03:24:06 +00:00
|
|
|
*
|
|
|
|
|
* @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
|
2015-05-27 20:59:56 +00:00
|
|
|
*/
|
|
|
|
|
get2DCursorLocation: function (selectionStart) {
|
|
|
|
|
if (typeof selectionStart === 'undefined') {
|
|
|
|
|
selectionStart = this.selectionStart;
|
|
|
|
|
}
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-06-03 23:39:10 +00:00
|
|
|
var numLines = this._textLines.length,
|
|
|
|
|
removed = 0,
|
|
|
|
|
textHasChanged = this.__text !== this.text;
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-28 03:24:06 +00:00
|
|
|
for (var i = 0; i < numLines; i++) {
|
2015-06-03 23:39:10 +00:00
|
|
|
var line = this._textLines[i],
|
|
|
|
|
lineLen = line.length;
|
|
|
|
|
|
2015-06-03 23:49:38 +00:00
|
|
|
if (selectionStart <= removed + lineLen) {
|
2015-06-03 23:39:10 +00:00
|
|
|
// edge case:
|
|
|
|
|
// If we are at the end of a line that is wrapping, force cursor on to next line
|
|
|
|
|
// However, doing that for inserting styles breaks things, so don't when text has changed
|
2015-06-03 23:49:38 +00:00
|
|
|
if (!textHasChanged &&
|
|
|
|
|
i !== numLines - 1 && selectionStart === removed + lineLen && this.text[removed + lineLen] !== '\n') {
|
2015-06-03 23:39:10 +00:00
|
|
|
i++;
|
|
|
|
|
selectionStart = removed;
|
|
|
|
|
}
|
2015-05-27 18:47:13 +00:00
|
|
|
|
2015-05-28 03:24:06 +00:00
|
|
|
return {
|
|
|
|
|
lineIndex: i,
|
2015-06-03 23:39:10 +00:00
|
|
|
charIndex: selectionStart - removed
|
2015-05-28 03:24:06 +00:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-03 23:39:10 +00:00
|
|
|
removed += lineLen;
|
2015-05-28 03:24:06 +00:00
|
|
|
|
2015-05-31 22:44:44 +00:00
|
|
|
if (this.text[removed] === '\n') {
|
2015-05-28 03:24:06 +00:00
|
|
|
removed++;
|
2015-05-27 18:47:13 +00:00
|
|
|
}
|
2015-05-27 20:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
2015-05-28 03:24:06 +00:00
|
|
|
lineIndex: numLines - 1,
|
|
|
|
|
charIndex: this._textLines[numLines - 1].length
|
2015-05-27 20:59:56 +00:00
|
|
|
};
|
|
|
|
|
},
|
2015-05-28 03:24:06 +00:00
|
|
|
|
2015-05-27 18:47:13 +00:00
|
|
|
/**
|
2015-05-27 20:59:56 +00:00
|
|
|
* Overrides superclass function and uses text wrapping data to get cursor
|
|
|
|
|
* boundary offsets instead of the array of chars.
|
|
|
|
|
* @param {Array} chars Unused
|
|
|
|
|
* @param {String} typeOfBoundaries Can be 'cursor' or 'selection'
|
|
|
|
|
* @returns {Object} Object with 'top', 'left', and 'lineLeft' properties set.
|
2015-05-27 18:47:13 +00:00
|
|
|
*/
|
2015-05-27 20:59:56 +00:00
|
|
|
_getCursorBoundariesOffsets: function (chars, typeOfBoundaries) {
|
|
|
|
|
var topOffset = 0,
|
|
|
|
|
leftOffset = 0,
|
|
|
|
|
cursorLocation = this.get2DCursorLocation(),
|
|
|
|
|
lineChars = this._textLines[cursorLocation.lineIndex].split(''),
|
|
|
|
|
lineLeftOffset = this._getCachedLineOffset(cursorLocation.lineIndex);
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < cursorLocation.charIndex; i++) {
|
|
|
|
|
leftOffset += this._getWidthOfChar(this.ctx, lineChars[i], cursorLocation.lineIndex, i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cursorLocation.lineIndex; i++) {
|
|
|
|
|
topOffset += this._getHeightOfLine(this.ctx, i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeOfBoundaries === 'cursor') {
|
2015-05-27 21:18:52 +00:00
|
|
|
topOffset += (1 - this._fontSizeFraction) * this._getHeightOfLine(this.ctx, cursorLocation.lineIndex)
|
|
|
|
|
/ this.lineHeight - this.getCurrentCharFontSize(cursorLocation.lineIndex, cursorLocation.charIndex)
|
|
|
|
|
* (1 - this._fontSizeFraction);
|
2015-05-27 20:59:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
top: topOffset,
|
|
|
|
|
left: leftOffset,
|
|
|
|
|
lineLeft: lineLeftOffset
|
|
|
|
|
};
|
|
|
|
|
},
|
2015-05-27 18:47:13 +00:00
|
|
|
/**
|
2015-05-27 20:59:56 +00:00
|
|
|
* Returns object representation of an instance
|
|
|
|
|
* @method toObject
|
|
|
|
|
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
|
|
|
|
|
* @return {Object} object representation of an instance
|
2015-05-27 18:47:13 +00:00
|
|
|
*/
|
2015-05-27 20:59:56 +00:00
|
|
|
toObject: function (propertiesToInclude) {
|
|
|
|
|
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
|
|
|
|
|
minWidth: this.minWidth
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
/**
|
|
|
|
|
* Returns fabric.Textbox instance from an object representation
|
|
|
|
|
* @static
|
|
|
|
|
* @memberOf fabric.Textbox
|
|
|
|
|
* @param {Object} object Object to create an instance from
|
|
|
|
|
* @return {fabric.Textbox} instance of fabric.Textbox
|
|
|
|
|
*/
|
|
|
|
|
fabric.Textbox.fromObject = function (object) {
|
|
|
|
|
return new fabric.Textbox(object.text, clone(object));
|
|
|
|
|
};
|
|
|
|
|
/**
|
|
|
|
|
* Returns the default controls visibility required for Textboxes.
|
|
|
|
|
* @returns {Object}
|
|
|
|
|
*/
|
|
|
|
|
fabric.Textbox.getTextboxControlVisibility = function () {
|
|
|
|
|
return {
|
|
|
|
|
tl: false,
|
|
|
|
|
tr: false,
|
|
|
|
|
br: false,
|
|
|
|
|
bl: false,
|
|
|
|
|
ml: true,
|
|
|
|
|
mt: false,
|
|
|
|
|
mr: true,
|
|
|
|
|
mb: false,
|
|
|
|
|
mtr: true
|
2015-05-27 18:47:13 +00:00
|
|
|
};
|
2015-05-27 20:59:56 +00:00
|
|
|
};
|
|
|
|
|
/**
|
|
|
|
|
* Contains all fabric.Textbox objects that have been created
|
|
|
|
|
* @static
|
2015-05-31 22:44:44 +00:00
|
|
|
* @memberOf fabric.Textbox
|
2015-05-27 20:59:56 +00:00
|
|
|
* @type Array
|
|
|
|
|
*/
|
|
|
|
|
fabric.Textbox.instances = [];
|
|
|
|
|
})();
|