From 8ad5e7e511ade641e9b1a1263317e73d015ff731 Mon Sep 17 00:00:00 2001 From: kangax Date: Wed, 30 Oct 2013 14:55:02 +0100 Subject: [PATCH] IText doc fixes --- build.js | 2 +- dist/all.js | 1811 ++++++++++++++-------------- dist/all.min.js | 4 +- dist/all.min.js.gz | Bin 57148 -> 57120 bytes dist/all.require.js | 1811 ++++++++++++++-------------- src/mixins/itext_behavior.mixin.js | 6 +- src/shapes/itext.class.js | 11 +- 7 files changed, 1827 insertions(+), 1818 deletions(-) diff --git a/build.js b/build.js index aea8fed0..82b2408a 100644 --- a/build.js +++ b/build.js @@ -231,8 +231,8 @@ var filesToInclude = [ ifSpecifiedInclude('text', 'src/shapes/text.class.js'), ifSpecifiedInclude('cufon', 'src/shapes/text.cufon.js'), - ifSpecifiedInclude('itext', 'src/mixins/itext_behavior.mixin.js'), ifSpecifiedInclude('itext', 'src/shapes/itext.class.js'), + ifSpecifiedInclude('itext', 'src/mixins/itext_behavior.mixin.js'), ifSpecifiedInclude('node', 'src/node.js'), diff --git a/dist/all.js b/dist/all.js index 764a7650..004526ab 100644 --- a/dist/all.js +++ b/dist/all.js @@ -19337,7 +19337,912 @@ fabric.util.object.extend(fabric.Text.prototype, { var clone = fabric.util.object.clone; - fabric.ITextBehavior = { /** @lends fabric.IText.prototype */ + /** + * IText class (introduced in v1.4) + * @class fabric.IText + * @extends fabric.Text + * @mixes fabric.Observable + * @fires text:changed + * @return {fabric.IText} thisArg + * @see {@link fabric.IText#initialize} for constructor definition + * + *

Supported key combinations:

+ *
+    *   Move cursor:                    left, right, up, down
+    *   Select character:               shift + left, shift + right
+    *   Select text vertically:         shift + up, shift + down
+    *   Move cursor by word:            alt + left, alt + right
+    *   Select words:                   shift + alt + left, shift + alt + right
+    *   Move cursor to line start/end:  cmd + left, cmd + right
+    *   Select till start/end of line:  cmd + shift + left, cmd + shift + right
+    *   Jump to start/end of text:      cmd + up, cmd + down
+    *   Select till start/end of text:  cmd + shift + up, cmd + shift + down
+    *   Delete character:               backspace
+    *   Delete word:                    alt + backspace
+    *   Delete line:                    cmd + backspace
+    *   Forward delete:                 delete
+    * 
+ */ + fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, /** @lends fabric.IText.prototype */ { + + /** + * Type of an object + * @type String + * @default + */ + type: 'i-text', + + /** + * Index where text selection starts (or where cursor is when there is no selection) + * @type Nubmer + * @default + */ + selectionStart: 0, + + /** + * Index where text selection ends + * @type Nubmer + * @default + */ + selectionEnd: 0, + + /** + * Color of text selection + * @type String + * @default + */ + selectionColor: 'rgba(17,119,255,0.3)', + + /** + * Indicates whether text is in editing mode + * @type Boolean + * @default + */ + isEditing: false, + + /** + * Indicates whether a text can be edited + * @type Boolean + * @default + */ + editable: true, + + /** + * Border color of text object while it's in editing mode + * @type String + * @default + */ + editingBorderColor: 'rgba(102,153,255,0.25)', + + /** + * Width of cursor (in px) + * @type Number + * @default + */ + cursorWidth: 2, + + /** + * Color of default cursor (when not overwritten by character style) + * @type String + * @default + */ + cursorColor: '#333', + + /** + * Delay between cursor blink (in ms) + * @type Number + * @default + */ + cursorDelay: 1000, + + /** + * Duration of cursor fadein (in ms) + * @type Number + * @default + */ + cursorDuration: 600, + + /** + * Object containing character styles + * (where top-level properties corresponds to line number and 2nd-level properties -- to char number in a line) + * @type Object + * @default + */ + styles: null, + + skipFillStrokeCheck: true, + + /** + * @private + */ + _reNewline: /\r?\n/, + + /** + * @private + */ + _reSpace: /\s|\n/, + + /** + * @private + */ + _fontSizeFraction: 4, + + /** + * @private + */ + _currentCursorOpacity: 0, + + /** + * @private + */ + _selectionDirection: null, + + /** + * @private + */ + _abortCursorAnimation: false, + + /** + * Constructor + * @param {String} text Text string + * @param {Object} [options] Options object + * @return {fabric.IText} thisArg + */ + initialize: function(text, options) { + this.styles = options.styles || { }; + this.callSuper('initialize', text, options); + this.initBehavior(); + }, + + /** + * Returns true if object has no styling + */ + isEmptyStyles: function() { + if (!this.styles) return true; + var obj = this.styles; + + for (var p1 in obj) { + for (var p2 in obj[p1]) { + /*jshint unused:false */ + for (var p3 in obj[p1][p2]) { + return false; + } + } + } + return true; + }, + + /** + * Sets selection start (left boundary of a selection) + * @param {Number} index Index to set selection start to + */ + setSelectionStart: function(index) { + this.selectionStart = index; + this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index); + }, + + /** + * Sets selection end (right boundary of a selection) + * @param {Number} index Index to set selection end to + */ + setSelectionEnd: function(index) { + this.selectionEnd = index; + this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index); + }, + + /** + * Gets style of a current selection/cursor (at the start position) + * @return {Object} styles Style object at a cursor position + */ + getSelectionStyles: function() { + var loc = this.get2DCursorLocation(); + if (this.styles[loc.lineIndex]) { + return this.styles[loc.lineIndex][loc.charIndex] || { }; + } + return { }; + }, + + /** + * Sets style of a current selection + * @param {Object} [styles] Styles object + * @return {fabric.IText} thisArg + * @chainable + */ + setSelectionStyles: function(styles) { + if (this.selectionStart === this.selectionEnd) { + this._extendStyles(this.selectionStart, styles); + } + else { + for (var i = this.selectionStart; i < this.selectionEnd; i++) { + this._extendStyles(i, styles); + } + } + return this; + }, + + /** + * @private + */ + _extendStyles: function(index, styles) { + var loc = this.get2DCursorLocation(index); + + if (!this.styles[loc.lineIndex]) { + this.styles[loc.lineIndex] = { }; + } + if (!this.styles[loc.lineIndex][loc.charIndex]) { + this.styles[loc.lineIndex][loc.charIndex] = { }; + } + + fabric.util.object.extend(this.styles[loc.lineIndex][loc.charIndex], styles); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _render: function(ctx) { + this.callSuper('_render', ctx); + this.isEditing && this.renderCursorOrSelection(ctx); + this.ctx = ctx; + }, + + /** + * Renders cursor or selection (depending on what exists) + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + renderCursorOrSelection: function(ctx) { + if (!this.active) return; + + var chars = this.text.split(''), + boundaries; + + if (this.selectionStart === this.selectionEnd) { + boundaries = this.getCursorBoundaries(ctx, chars, 'cursor'); + this.renderCursor(ctx, boundaries); + } + else { + boundaries = this.getCursorBoundaries(ctx, chars, 'selection'); + this.renderSelection(ctx, chars, boundaries); + } + }, + + /** + * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start) + * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used. + */ + get2DCursorLocation: function(selectionStart) { + if (typeof selectionStart === 'undefined') { + selectionStart = this.selectionStart; + } + var textBeforeCursor = this.text.slice(0, selectionStart); + var linesBeforeCursor = textBeforeCursor.split(this._reNewline); + + return { + lineIndex: linesBeforeCursor.length - 1, + charIndex: linesBeforeCursor[linesBeforeCursor.length - 1].length + }; + }, + + /** + * Returns fontSize of char at the current cursor + * @param {Number} lineIndex Line index + * @param {Number} charIndex Char index + * @return {Number} Character font size + */ + getCurrentCharFontSize: function(lineIndex, charIndex) { + return ( + this.styles[lineIndex] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fontSize) || this.fontSize; + }, + + /** + * Returns color (fill) of char at the current cursor + * @param {Number} lineIndex Line index + * @param {Number} charIndex Char index + * @return {String} Character color (fill) + */ + getCurrentCharColor: function(lineIndex, charIndex) { + return ( + this.styles[lineIndex] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fill) || this.cursorColor; + }, + + /** + * Returns cursor boundaries (left, top, leftOffset, topOffset) + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} chars Array of characters + */ + getCursorBoundaries: function(ctx, chars, typeOfBoundaries) { + + var cursorLocation = this.get2DCursorLocation(); + var lineIndex = cursorLocation.lineIndex; + var charIndex = cursorLocation.charIndex; + + var textLines = this.text.split(this._reNewline); + + var widthOfLine; + var lineLeftOffset; + + // left/top are left/top of entire text box + // leftOffset/topOffset are offset from that left/top point of a text box + var left = Math.round(this._getLeftOffset()); + var top = -this.height / 2; + + var leftOffset = 0; + var topOffset = typeOfBoundaries === 'cursor' + // selection starts at the very top of the line, + // whereas cursor starts at the padding created by line height + ? (this._getHeightOfLine(ctx, 0) - this.getCurrentCharFontSize(lineIndex, charIndex)) + : 0; + + lineIndex = 0; + charIndex = 0; + + for (var i = 0; i < this.selectionStart; i++) { + if (chars[i] === '\n') { + leftOffset = 0; + topOffset += this._getHeightOfLine(ctx, lineIndex + (typeOfBoundaries === 'cursor' ? 1 : 0)); + + lineIndex++; + charIndex = 0; + } + else { + leftOffset += this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); + charIndex++; + } + + widthOfLine = this._getWidthOfLine(ctx, lineIndex, textLines); + lineLeftOffset = this._getLineLeftOffset(widthOfLine); + } + + return { + left: left, + top: top, + leftOffset: leftOffset + (lineLeftOffset || 0), + topOffset: topOffset + }; + }, + + /** + * Renders cursor + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + renderCursor: function(ctx, boundaries) { + ctx.save(); + + var cursorLocation = this.get2DCursorLocation(); + var lineIndex = cursorLocation.lineIndex; + var charIndex = cursorLocation.charIndex; + + ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex); + ctx.globalAlpha = this._currentCursorOpacity; + + var charHeight = this.getCurrentCharFontSize(lineIndex, charIndex); + + ctx.fillRect( + boundaries.left + boundaries.leftOffset, + boundaries.top + boundaries.topOffset, + this.cursorWidth, + charHeight); + + ctx.restore(); + }, + + /** + * Renders text selection + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} chars Array of characters + * @param {Object} boundaries Object with left/top/leftOffset/topOffset + */ + renderSelection: function(ctx, chars, boundaries) { + ctx.save(); + + ctx.fillStyle = this.selectionColor; + + var cursorLocation = this.get2DCursorLocation(); + var lineIndex = cursorLocation.lineIndex; + var charIndex = cursorLocation.charIndex; + var textLines = this.text.split(this._reNewline); + var origLineIndex = lineIndex; + + for (var i = this.selectionStart; i < this.selectionEnd; i++) { + + if (chars[i] === '\n') { + boundaries.leftOffset = 0; + boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex); + lineIndex++; + charIndex = 0; + } + else if (i !== this.text.length) { + + var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); + var lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0; + + if (lineIndex === origLineIndex) { + // only offset the line if we're rendering selection of 2nd, 3rd, etc. line + lineOffset = 0; + } + + ctx.fillRect( + boundaries.left + boundaries.leftOffset + lineOffset, + boundaries.top + boundaries.topOffset, + charWidth, + this._getHeightOfLine(ctx, lineIndex)); + + boundaries.leftOffset += charWidth; + charIndex++; + } + } + ctx.restore(); + }, + + /** + * @private + * @param {String} method + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderChars: function(method, ctx, line, left, top, lineIndex) { + + if (this.isEmptyStyles()) { + return this._renderCharsFast(method, ctx, line, left, top); + } + + this.skipTextAlign = true; + + // set proper box offset + left -= this.textAlign === 'center' + ? (this.width / 2) + : (this.textAlign === 'right') + ? this.width + : 0; + + // set proper line offset + var textLines = this.text.split(this._reNewline); + var lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines); + var lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines); + var lineLeftOffset = this._getLineLeftOffset(lineWidth); + var chars = line.split(''); + + left += lineLeftOffset || 0; + + ctx.save(); + for (var i = 0, len = chars.length; i < len; i++) { + this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight); + } + ctx.restore(); + }, + + /** + * @private + * @param {String} method + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {String} line + */ + _renderCharsFast: function(method, ctx, line, left, top) { + this.skipTextAlign = false; + + if (method === 'fillText' && this.fill) { + this.callSuper('_renderChars', method, ctx, line, left, top); + } + if (method === 'strokeText' && this.stroke) { + this.callSuper('_renderChars', method, ctx, line, left, top); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderChar: function(method, ctx, lineIndex, i, _char, left, top, lineHeight) { + var decl, charWidth; + + if (this.styles && this.styles[lineIndex] && (decl = this.styles[lineIndex][i])) { + + var shouldStroke = decl.stroke || this.stroke; + var shouldFill = decl.fill || this.fill; + + ctx.save(); + charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i, decl); + + if (shouldFill) { + ctx.fillText(_char, left, top); + } + if (shouldStroke) { + ctx.strokeText(_char, left, top); + } + + this._renderCharDecoration(ctx, decl, left, top, charWidth, lineHeight); + ctx.restore(); + + ctx.translate(charWidth, 0); + } + else { + if (method === 'strokeText' && this.stroke) { + ctx[method](_char, left, top); + } + if (method === 'fillText' && this.fill) { + ctx[method](_char, left, top); + } + charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i); + this._renderCharDecoration(ctx, null, left, top, charWidth, lineHeight); + + ctx.translate(ctx.measureText(_char).width, 0); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight) { + var textDecoration = styleDeclaration + ? (styleDeclaration.textDecoration || this.textDecoration) + : this.textDecoration; + + if (!textDecoration) return; + + if (textDecoration.indexOf('underline') > -1) { + + this._renderCharDecorationAtOffset( + ctx, + left, + top + (this.fontSize / this._fontSizeFraction), + charWidth, + 0 + ); + } + if (textDecoration.indexOf('line-through') > -1) { + this._renderCharDecorationAtOffset( + ctx, + left, + top + (this.fontSize / this._fontSizeFraction), + charWidth, + (lineHeight / this._fontSizeFraction) + ); + } + if (textDecoration.indexOf('overline') > -1) { + this._renderCharDecorationAtOffset( + ctx, + left, + top, + charWidth, + lineHeight - (this.fontSize / this._fontSizeFraction) + ); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderCharDecorationAtOffset: function(ctx, left, top, charWidth, offset) { + ctx.fillRect(left, top - offset, charWidth, 1); + }, + + /** + * @private + * @param {String} method + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {String} line + */ + _renderTextLine: function(method, ctx, line, left, top, lineIndex) { + // to "cancel" this.fontSize subtraction in fabric.Text#_renderTextLine + top += this.fontSize / 4; + this.callSuper('_renderTextLine', method, ctx, line, left, top, lineIndex); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} textLines + */ + _renderTextDecoration: function(ctx, textLines) { + if (this.isEmptyStyles()) { + return this.callSuper('_renderTextDecoration', ctx, textLines); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} textLines Array of all text lines + */ + _renderTextLinesBackground: function(ctx, textLines) { + if (!this.textBackgroundColor && !this.styles) return; + + ctx.save(); + + if (this.textBackgroundColor) { + ctx.fillStyle = this.textBackgroundColor; + } + + var lineHeights = 0; + var fractionOfFontSize = this.fontSize / this._fontSizeFraction; + + for (var i = 0, len = textLines.length; i < len; i++) { + + var heightOfLine = this._getHeightOfLine(ctx, i, textLines); + if (textLines[i] === '') { + lineHeights += heightOfLine; + continue; + } + + var lineWidth = this._getWidthOfLine(ctx, i, textLines); + var lineLeftOffset = this._getLineLeftOffset(lineWidth); + + if (this.textBackgroundColor) { + ctx.fillStyle = this.textBackgroundColor; + + ctx.fillRect( + this._getLeftOffset() + lineLeftOffset, + this._getTopOffset() + lineHeights + fractionOfFontSize, + lineWidth, + heightOfLine + ); + } + if (this.styles[i]) { + for (var j = 0, jlen = textLines[i].length; j < jlen; j++) { + if (this.styles[i] && this.styles[i][j] && this.styles[i][j].textBackgroundColor) { + + var _char = textLines[i][j]; + + ctx.fillStyle = this.styles[i][j].textBackgroundColor; + + ctx.fillRect( + this._getLeftOffset() + lineLeftOffset + this._getWidthOfCharsAt(ctx, i, j, textLines), + this._getTopOffset() + lineHeights + fractionOfFontSize, + this._getWidthOfChar(ctx, _char, i, j, textLines) + 1, + heightOfLine + ); + } + } + } + lineHeights += heightOfLine; + } + ctx.restore(); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {String} _char + * @param {Number} lineIndex + * @param {Number} charIndex + * @param {Object} [decl] + */ + _applyCharStylesGetWidth: function(ctx, _char, lineIndex, charIndex, decl) { + var styleDeclaration = decl || (this.styles[lineIndex] && this.styles[lineIndex][charIndex]); + + if (styleDeclaration) { + // cloning so that original style object is not polluted with following font declarations + styleDeclaration = clone(styleDeclaration); + } + else { + styleDeclaration = { }; + } + + var fill = styleDeclaration.fill || this.fill; + ctx.fillStyle = fill.toLive + ? fill.toLive(ctx) + : fill; + + if (styleDeclaration.stroke) { + ctx.strokeStyle = (styleDeclaration.stroke && styleDeclaration.stroke.toLive) + ? styleDeclaration.stroke.toLive(ctx) + : styleDeclaration.stroke; + } + + ctx.lineWidth = styleDeclaration.strokeWidth || this.strokeWidth; + + this._applyFontStyles(styleDeclaration); + + if (typeof styleDeclaration.shadow === 'string') { + styleDeclaration.shadow = new fabric.Shadow(styleDeclaration.shadow); + } + + this._setShadow.call(styleDeclaration, ctx); + + ctx.font = this._getFontDeclaration.call(styleDeclaration); + + return ctx.measureText(_char).width; + }, + + /** + * @private + * @param {Object} styleDeclaration + */ + _applyFontStyles: function(styleDeclaration) { + if (!styleDeclaration.fontFamily) { + styleDeclaration.fontFamily = this.fontFamily; + } + if (!styleDeclaration.fontSize) { + styleDeclaration.fontSize = this.fontSize; + } + if (!styleDeclaration.fontWeight) { + styleDeclaration.fontWeight = this.fontWeight; + } + if (!styleDeclaration.fontStyle) { + styleDeclaration.fontStyle = this.fontStyle; + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfChar: function(ctx, _char, lineIndex, charIndex) { + ctx.save(); + var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex); + ctx.restore(); + return width; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getHeightOfChar: function(ctx, _char, lineIndex, charIndex) { + if (this.styles[lineIndex] && this.styles[lineIndex][charIndex]) { + return this.styles[lineIndex][charIndex].fontSize || this.fontSize; + } + return this.fontSize; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfCharAt: function(ctx, lineIndex, charIndex, lines) { + lines = lines || this.text.split(this._reNewline); + var _char = lines[lineIndex].split('')[charIndex]; + return this._getWidthOfChar(ctx, _char, lineIndex, charIndex); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getHeightOfCharAt: function(ctx, lineIndex, charIndex, lines) { + lines = lines || this.text.split(this._reNewline); + var _char = lines[lineIndex].split('')[charIndex]; + return this._getHeightOfChar(ctx, _char, lineIndex, charIndex); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfCharsAt: function(ctx, lineIndex, charIndex, lines) { + var width = 0; + for (var i = 0; i < charIndex; i++) { + width += this._getWidthOfCharAt(ctx, lineIndex, i, lines); + } + return width; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfLine: function(ctx, lineIndex, textLines) { + // if (!this.styles[lineIndex]) { + // return this.callSuper('_getLineWidth', ctx, textLines[lineIndex]); + // } + return this._getWidthOfCharsAt(ctx, lineIndex, textLines[lineIndex].length, textLines); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getTextWidth: function(ctx, textLines) { + + if (this.isEmptyStyles()) { + return this.callSuper('_getTextWidth', ctx, textLines); + } + + var maxWidth = this._getWidthOfLine(ctx, 0, textLines); + + for (var i = 1, len = textLines.length; i < len; i++) { + var currentLineWidth = this._getWidthOfLine(ctx, i, textLines); + if (currentLineWidth > maxWidth) { + maxWidth = currentLineWidth; + } + } + return maxWidth; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getHeightOfLine: function(ctx, lineIndex, textLines) { + + textLines = textLines || this.text.split(this._reNewline); + + var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0); + + var line = textLines[lineIndex]; + var chars = line.split(''); + + for (var i = 1, len = chars.length; i < len; i++) { + var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i); + if (currentCharHeight > maxHeight) { + maxHeight = currentCharHeight; + } + } + + return maxHeight * this.lineHeight; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getTextHeight: function(ctx, textLines) { + var height = 0; + for (var i = 0, len = textLines.length; i < len; i++) { + height += this._getHeightOfLine(ctx, i, textLines); + } + return height; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getTopOffset: function() { + var topOffset = fabric.Text.prototype._getTopOffset.call(this); + return topOffset - (this.fontSize / this._fontSizeFraction); + }, + + /** + * Returns object representation of an instance + * @methd toObject + * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output + * @return {Object} object representation of an instance + */ + toObject: function(propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + styles: clone(this.styles) + }); + }, + + /* _TO_SVG_START_ */ + /** + * Returns SVG representation of an instance + * @return {String} svg representation of an instance + */ + toSVG: function(reviver) { + if (this.isEmptyStyles()) { + return this.callSuper('toSVG', reviver); + } + // TODO: add support for styled text SVG output + } + /* _TO_SVG_END_ */ + }); + + /** + * Returns fabric.IText instance from an object representation + * @static + * @memberOf fabric.IText + * @param {Object} object Object to create an instance from + * @return {fabric.IText} instance of fabric.IText + */ + fabric.IText.fromObject = function(object) { + return new fabric.IText(object.text, clone(object)); + }; + +})(); + + +(function() { + + var clone = fabric.util.object.clone; + + fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ { /** * Initializes all the interactive behavior of IText @@ -20497,914 +21402,12 @@ fabric.util.object.extend(fabric.Text.prototype, { insertNewline: function() { this.insertChar('\n'); } - }; -})(); - - -(function() { - - var clone = fabric.util.object.clone; - - /** - * IText class - * @class fabric.IText - * @extends fabric.Text - * @mixes fabric.Observable - * @fires #text:changed - * @return {fabric.IText} thisArg - * @see {@link fabric.IText#initialize} for constructor definition - * - * Supported key combinations: - * - * Move cursor: left, right, up, down - * Select character: shift + left, shift + right - * Select text vertically: shift + up, shift + down - * Move cursor by word: alt + left, alt + right - * Select words: shift + alt + left, shift + alt + right - * Move cursor to line start/end: cmd + left, cmd + right - * Select till start/end of line: cmd + shift + left, cmd + shift + right - * Jump to start/end of text: cmd + up, cmd + down - * Select till start/end of text: cmd + shift + up, cmd + shift + down - * Delete character: backspace - * Delete word: alt + backspace - * Delete line: cmd + backspace - * Forward delete: delete - */ - fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, fabric.ITextBehavior, { - - /** - * Type of an object - * @type String - * @default - */ - type: 'i-text', - - /** - * Index where text selection starts (or where cursor is when there is no selection) - * @type Nubmer - * @default - */ - selectionStart: 0, - - /** - * Index where text selection ends - * @type Nubmer - * @default - */ - selectionEnd: 0, - - /** - * Color of text selection - * @type String - * @default - */ - selectionColor: 'rgba(17,119,255,0.3)', - - /** - * Indicates whether text is in editing mode - * @type Boolean - * @default - */ - isEditing: false, - - /** - * Indicates whether a text can be edited - * @type Boolean - * @default - */ - editable: true, - - /** - * Border color of text object while it's in editing mode - * @type String - * @default - */ - editingBorderColor: 'rgba(102,153,255,0.25)', - - /** - * Width of cursor (in px) - * @type Number - * @default - */ - cursorWidth: 2, - - /** - * Color of default cursor (when not overwritten by character style) - * @type String - * @default - */ - cursorColor: '#333', - - /** - * Delay between cursor blink (in ms) - * @type Number - * @default - */ - cursorDelay: 1000, - - /** - * Duration of cursor fadein (in ms) - * @type Number - * @default - */ - cursorDuration: 600, - - /** - * Object containing character styles - * (where top-level properties corresponds to line number and 2nd-level properties -- to char number in a line) - * @type Object - * @default - */ - styles: null, - - skipFillStrokeCheck: true, - - /** - * @private - */ - _reNewline: /\r?\n/, - - /** - * @private - */ - _reSpace: /\s|\n/, - - /** - * @private - */ - _fontSizeFraction: 4, - - /** - * @private - */ - _currentCursorOpacity: 0, - - /** - * @private - */ - _selectionDirection: null, - - /** - * @private - */ - _abortCursorAnimation: false, - - /** - * Constructor - * @param {String} text Text string - * @param {Object} [options] Options object - * @return {fabric.IText} thisArg - */ - initialize: function(text, options) { - this.styles = options.styles || { }; - this.callSuper('initialize', text, options); - this.initBehavior(); - }, - - /** - * Returns true if object has no styling - */ - isEmptyStyles: function() { - if (!this.styles) return true; - var obj = this.styles; - - for (var p1 in obj) { - for (var p2 in obj[p1]) { - /*jshint unused:false */ - for (var p3 in obj[p1][p2]) { - return false; - } - } - } - return true; - }, - - /** - * Sets selection start (left boundary of a selection) - * @param {Number} index Index to set selection start to - */ - setSelectionStart: function(index) { - this.selectionStart = index; - this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index); - }, - - /** - * Sets selection end (right boundary of a selection) - * @param {Number} index Index to set selection end to - */ - setSelectionEnd: function(index) { - this.selectionEnd = index; - this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index); - }, - - /** - * Gets style of a current selection/cursor (at the start position) - * @return {Object} styles Style object at a cursor position - */ - getSelectionStyles: function() { - var loc = this.get2DCursorLocation(); - if (this.styles[loc.lineIndex]) { - return this.styles[loc.lineIndex][loc.charIndex] || { }; - } - return { }; - }, - - /** - * Sets style of a current selection - * @param {Object} [styles] Styles object - * @return {fabric.IText} thisArg - * @chainable - */ - setSelectionStyles: function(styles) { - if (this.selectionStart === this.selectionEnd) { - this._extendStyles(this.selectionStart, styles); - } - else { - for (var i = this.selectionStart; i < this.selectionEnd; i++) { - this._extendStyles(i, styles); - } - } - return this; - }, - - /** - * @private - */ - _extendStyles: function(index, styles) { - var loc = this.get2DCursorLocation(index); - - if (!this.styles[loc.lineIndex]) { - this.styles[loc.lineIndex] = { }; - } - if (!this.styles[loc.lineIndex][loc.charIndex]) { - this.styles[loc.lineIndex][loc.charIndex] = { }; - } - - fabric.util.object.extend(this.styles[loc.lineIndex][loc.charIndex], styles); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx) { - this.callSuper('_render', ctx); - this.isEditing && this.renderCursorOrSelection(ctx); - this.ctx = ctx; - }, - - /** - * Renders cursor or selection (depending on what exists) - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - renderCursorOrSelection: function(ctx) { - if (!this.active) return; - - var chars = this.text.split(''), - boundaries; - - if (this.selectionStart === this.selectionEnd) { - boundaries = this.getCursorBoundaries(ctx, chars, 'cursor'); - this.renderCursor(ctx, boundaries); - } - else { - boundaries = this.getCursorBoundaries(ctx, chars, 'selection'); - this.renderSelection(ctx, chars, boundaries); - } - }, - - /** - * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start) - * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used. - */ - get2DCursorLocation: function(selectionStart) { - if (typeof selectionStart === 'undefined') { - selectionStart = this.selectionStart; - } - var textBeforeCursor = this.text.slice(0, selectionStart); - var linesBeforeCursor = textBeforeCursor.split(this._reNewline); - - return { - lineIndex: linesBeforeCursor.length - 1, - charIndex: linesBeforeCursor[linesBeforeCursor.length - 1].length - }; - }, - - /** - * Returns fontSize of char at the current cursor - * @param {Number} lineIndex Line index - * @param {Number} charIndex Char index - * @return {Number} Character font size - */ - getCurrentCharFontSize: function(lineIndex, charIndex) { - return ( - this.styles[lineIndex] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fontSize) || this.fontSize; - }, - - /** - * Returns color (fill) of char at the current cursor - * @param {Number} lineIndex Line index - * @param {Number} charIndex Char index - * @return {String} Character color (fill) - */ - getCurrentCharColor: function(lineIndex, charIndex) { - return ( - this.styles[lineIndex] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fill) || this.cursorColor; - }, - - /** - * Returns cursor boundaries (left, top, leftOffset, topOffset) - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} chars Array of characters - */ - getCursorBoundaries: function(ctx, chars, typeOfBoundaries) { - - var cursorLocation = this.get2DCursorLocation(); - var lineIndex = cursorLocation.lineIndex; - var charIndex = cursorLocation.charIndex; - - var textLines = this.text.split(this._reNewline); - - var widthOfLine; - var lineLeftOffset; - - // left/top are left/top of entire text box - // leftOffset/topOffset are offset from that left/top point of a text box - var left = Math.round(this._getLeftOffset()); - var top = -this.height / 2; - - var leftOffset = 0; - var topOffset = typeOfBoundaries === 'cursor' - // selection starts at the very top of the line, - // whereas cursor starts at the padding created by line height - ? (this._getHeightOfLine(ctx, 0) - this.getCurrentCharFontSize(lineIndex, charIndex)) - : 0; - - lineIndex = 0; - charIndex = 0; - - for (var i = 0; i < this.selectionStart; i++) { - if (chars[i] === '\n') { - leftOffset = 0; - topOffset += this._getHeightOfLine(ctx, lineIndex + (typeOfBoundaries === 'cursor' ? 1 : 0)); - - lineIndex++; - charIndex = 0; - } - else { - leftOffset += this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); - charIndex++; - } - - widthOfLine = this._getWidthOfLine(ctx, lineIndex, textLines); - lineLeftOffset = this._getLineLeftOffset(widthOfLine); - } - - return { - left: left, - top: top, - leftOffset: leftOffset + (lineLeftOffset || 0), - topOffset: topOffset - }; - }, - - /** - * Renders cursor - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - renderCursor: function(ctx, boundaries) { - ctx.save(); - - var cursorLocation = this.get2DCursorLocation(); - var lineIndex = cursorLocation.lineIndex; - var charIndex = cursorLocation.charIndex; - - ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex); - ctx.globalAlpha = this._currentCursorOpacity; - - var charHeight = this.getCurrentCharFontSize(lineIndex, charIndex); - - ctx.fillRect( - boundaries.left + boundaries.leftOffset, - boundaries.top + boundaries.topOffset, - this.cursorWidth, - charHeight); - - ctx.restore(); - }, - - /** - * Renders text selection - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} chars Array of characters - * @param {Object} boundaries Object with left/top/leftOffset/topOffset - */ - renderSelection: function(ctx, chars, boundaries) { - ctx.save(); - - ctx.fillStyle = this.selectionColor; - - var cursorLocation = this.get2DCursorLocation(); - var lineIndex = cursorLocation.lineIndex; - var charIndex = cursorLocation.charIndex; - var textLines = this.text.split(this._reNewline); - var origLineIndex = lineIndex; - - for (var i = this.selectionStart; i < this.selectionEnd; i++) { - - if (chars[i] === '\n') { - boundaries.leftOffset = 0; - boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex); - lineIndex++; - charIndex = 0; - } - else if (i !== this.text.length) { - - var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); - var lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0; - - if (lineIndex === origLineIndex) { - // only offset the line if we're rendering selection of 2nd, 3rd, etc. line - lineOffset = 0; - } - - ctx.fillRect( - boundaries.left + boundaries.leftOffset + lineOffset, - boundaries.top + boundaries.topOffset, - charWidth, - this._getHeightOfLine(ctx, lineIndex)); - - boundaries.leftOffset += charWidth; - charIndex++; - } - } - ctx.restore(); - }, - - /** - * @private - * @param {String} method - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderChars: function(method, ctx, line, left, top, lineIndex) { - - if (this.isEmptyStyles()) { - return this._renderCharsFast(method, ctx, line, left, top); - } - - this.skipTextAlign = true; - - // set proper box offset - left -= this.textAlign === 'center' - ? (this.width / 2) - : (this.textAlign === 'right') - ? this.width - : 0; - - // set proper line offset - var textLines = this.text.split(this._reNewline); - var lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines); - var lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines); - var lineLeftOffset = this._getLineLeftOffset(lineWidth); - var chars = line.split(''); - - left += lineLeftOffset || 0; - - ctx.save(); - for (var i = 0, len = chars.length; i < len; i++) { - this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight); - } - ctx.restore(); - }, - - /** - * @private - * @param {String} method - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} line - */ - _renderCharsFast: function(method, ctx, line, left, top) { - this.skipTextAlign = false; - - if (method === 'fillText' && this.fill) { - this.callSuper('_renderChars', method, ctx, line, left, top); - } - if (method === 'strokeText' && this.stroke) { - this.callSuper('_renderChars', method, ctx, line, left, top); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderChar: function(method, ctx, lineIndex, i, _char, left, top, lineHeight) { - var decl, charWidth; - - if (this.styles && this.styles[lineIndex] && (decl = this.styles[lineIndex][i])) { - - var shouldStroke = decl.stroke || this.stroke; - var shouldFill = decl.fill || this.fill; - - ctx.save(); - charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i, decl); - - if (shouldFill) { - ctx.fillText(_char, left, top); - } - if (shouldStroke) { - ctx.strokeText(_char, left, top); - } - - this._renderCharDecoration(ctx, decl, left, top, charWidth, lineHeight); - ctx.restore(); - - ctx.translate(charWidth, 0); - } - else { - if (method === 'strokeText' && this.stroke) { - ctx[method](_char, left, top); - } - if (method === 'fillText' && this.fill) { - ctx[method](_char, left, top); - } - charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i); - this._renderCharDecoration(ctx, null, left, top, charWidth, lineHeight); - - ctx.translate(ctx.measureText(_char).width, 0); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight) { - var textDecoration = styleDeclaration - ? (styleDeclaration.textDecoration || this.textDecoration) - : this.textDecoration; - - if (!textDecoration) return; - - if (textDecoration.indexOf('underline') > -1) { - - this._renderCharDecorationAtOffset( - ctx, - left, - top + (this.fontSize / this._fontSizeFraction), - charWidth, - 0 - ); - } - if (textDecoration.indexOf('line-through') > -1) { - this._renderCharDecorationAtOffset( - ctx, - left, - top + (this.fontSize / this._fontSizeFraction), - charWidth, - (lineHeight / this._fontSizeFraction) - ); - } - if (textDecoration.indexOf('overline') > -1) { - this._renderCharDecorationAtOffset( - ctx, - left, - top, - charWidth, - lineHeight - (this.fontSize / this._fontSizeFraction) - ); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderCharDecorationAtOffset: function(ctx, left, top, charWidth, offset) { - ctx.fillRect(left, top - offset, charWidth, 1); - }, - - /** - * @private - * @param {String} method - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} line - */ - _renderTextLine: function(method, ctx, line, left, top, lineIndex) { - // to "cancel" this.fontSize subtraction in fabric.Text#_renderTextLine - top += this.fontSize / 4; - this.callSuper('_renderTextLine', method, ctx, line, left, top, lineIndex); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines - */ - _renderTextDecoration: function(ctx, textLines) { - if (this.isEmptyStyles()) { - return this.callSuper('_renderTextDecoration', ctx, textLines); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _renderTextLinesBackground: function(ctx, textLines) { - if (!this.textBackgroundColor && !this.styles) return; - - ctx.save(); - - if (this.textBackgroundColor) { - ctx.fillStyle = this.textBackgroundColor; - } - - var lineHeights = 0; - var fractionOfFontSize = this.fontSize / this._fontSizeFraction; - - for (var i = 0, len = textLines.length; i < len; i++) { - - var heightOfLine = this._getHeightOfLine(ctx, i, textLines); - if (textLines[i] === '') { - lineHeights += heightOfLine; - continue; - } - - var lineWidth = this._getWidthOfLine(ctx, i, textLines); - var lineLeftOffset = this._getLineLeftOffset(lineWidth); - - if (this.textBackgroundColor) { - ctx.fillStyle = this.textBackgroundColor; - - ctx.fillRect( - this._getLeftOffset() + lineLeftOffset, - this._getTopOffset() + lineHeights + fractionOfFontSize, - lineWidth, - heightOfLine - ); - } - if (this.styles[i]) { - for (var j = 0, jlen = textLines[i].length; j < jlen; j++) { - if (this.styles[i] && this.styles[i][j] && this.styles[i][j].textBackgroundColor) { - - var _char = textLines[i][j]; - - ctx.fillStyle = this.styles[i][j].textBackgroundColor; - - ctx.fillRect( - this._getLeftOffset() + lineLeftOffset + this._getWidthOfCharsAt(ctx, i, j, textLines), - this._getTopOffset() + lineHeights + fractionOfFontSize, - this._getWidthOfChar(ctx, _char, i, j, textLines) + 1, - heightOfLine - ); - } - } - } - lineHeights += heightOfLine; - } - ctx.restore(); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} _char - * @param {Number} lineIndex - * @param {Number} charIndex - * @param {Object} [decl] - */ - _applyCharStylesGetWidth: function(ctx, _char, lineIndex, charIndex, decl) { - var styleDeclaration = decl || (this.styles[lineIndex] && this.styles[lineIndex][charIndex]); - - if (styleDeclaration) { - // cloning so that original style object is not polluted with following font declarations - styleDeclaration = clone(styleDeclaration); - } - else { - styleDeclaration = { }; - } - - var fill = styleDeclaration.fill || this.fill; - ctx.fillStyle = fill.toLive - ? fill.toLive(ctx) - : fill; - - if (styleDeclaration.stroke) { - ctx.strokeStyle = (styleDeclaration.stroke && styleDeclaration.stroke.toLive) - ? styleDeclaration.stroke.toLive(ctx) - : styleDeclaration.stroke; - } - - ctx.lineWidth = styleDeclaration.strokeWidth || this.strokeWidth; - - this._applyFontStyles(styleDeclaration); - - if (typeof styleDeclaration.shadow === 'string') { - styleDeclaration.shadow = new fabric.Shadow(styleDeclaration.shadow); - } - - this._setShadow.call(styleDeclaration, ctx); - - ctx.font = this._getFontDeclaration.call(styleDeclaration); - - return ctx.measureText(_char).width; - }, - - /** - * @private - * @param {Object} styleDeclaration - */ - _applyFontStyles: function(styleDeclaration) { - if (!styleDeclaration.fontFamily) { - styleDeclaration.fontFamily = this.fontFamily; - } - if (!styleDeclaration.fontSize) { - styleDeclaration.fontSize = this.fontSize; - } - if (!styleDeclaration.fontWeight) { - styleDeclaration.fontWeight = this.fontWeight; - } - if (!styleDeclaration.fontStyle) { - styleDeclaration.fontStyle = this.fontStyle; - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfChar: function(ctx, _char, lineIndex, charIndex) { - ctx.save(); - var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex); - ctx.restore(); - return width; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getHeightOfChar: function(ctx, _char, lineIndex, charIndex) { - if (this.styles[lineIndex] && this.styles[lineIndex][charIndex]) { - return this.styles[lineIndex][charIndex].fontSize || this.fontSize; - } - return this.fontSize; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfCharAt: function(ctx, lineIndex, charIndex, lines) { - lines = lines || this.text.split(this._reNewline); - var _char = lines[lineIndex].split('')[charIndex]; - return this._getWidthOfChar(ctx, _char, lineIndex, charIndex); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getHeightOfCharAt: function(ctx, lineIndex, charIndex, lines) { - lines = lines || this.text.split(this._reNewline); - var _char = lines[lineIndex].split('')[charIndex]; - return this._getHeightOfChar(ctx, _char, lineIndex, charIndex); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfCharsAt: function(ctx, lineIndex, charIndex, lines) { - var width = 0; - for (var i = 0; i < charIndex; i++) { - width += this._getWidthOfCharAt(ctx, lineIndex, i, lines); - } - return width; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfLine: function(ctx, lineIndex, textLines) { - // if (!this.styles[lineIndex]) { - // return this.callSuper('_getLineWidth', ctx, textLines[lineIndex]); - // } - return this._getWidthOfCharsAt(ctx, lineIndex, textLines[lineIndex].length, textLines); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getTextWidth: function(ctx, textLines) { - - if (this.isEmptyStyles()) { - return this.callSuper('_getTextWidth', ctx, textLines); - } - - var maxWidth = this._getWidthOfLine(ctx, 0, textLines); - - for (var i = 1, len = textLines.length; i < len; i++) { - var currentLineWidth = this._getWidthOfLine(ctx, i, textLines); - if (currentLineWidth > maxWidth) { - maxWidth = currentLineWidth; - } - } - return maxWidth; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getHeightOfLine: function(ctx, lineIndex, textLines) { - - textLines = textLines || this.text.split(this._reNewline); - - var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0); - - var line = textLines[lineIndex]; - var chars = line.split(''); - - for (var i = 1, len = chars.length; i < len; i++) { - var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i); - if (currentCharHeight > maxHeight) { - maxHeight = currentCharHeight; - } - } - - return maxHeight * this.lineHeight; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getTextHeight: function(ctx, textLines) { - var height = 0; - for (var i = 0, len = textLines.length; i < len; i++) { - height += this._getHeightOfLine(ctx, i, textLines); - } - return height; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getTopOffset: function() { - var topOffset = fabric.Text.prototype._getTopOffset.call(this); - return topOffset - (this.fontSize / this._fontSizeFraction); - }, - - /** - * Returns object representation of an instance - * @methd toObject - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {Object} object representation of an instance - */ - toObject: function(propertiesToInclude) { - return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { - styles: clone(this.styles) - }); - }, - - /* _TO_SVG_START_ */ - /** - * Returns SVG representation of an instance - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - if (this.isEmptyStyles()) { - return this.callSuper('toSVG', reviver); - } - // TODO: add support for styled text SVG output - } - /* _TO_SVG_END_ */ }); - /** - * Returns fabric.IText instance from an object representation - * @static - * @memberOf fabric.IText - * @param {Object} object Object to create an instance from - * @return {fabric.IText} instance of fabric.IText - */ - fabric.IText.fromObject = function(object) { - return new fabric.IText(object.text, clone(object)); - }; - })(); + (function() { if (typeof document !== 'undefined' && typeof window !== 'undefined') { diff --git a/dist/all.min.js b/dist/all.min.js index 1cf1b711..9458678c 100644 --- a/dist/all.min.js +++ b/dist/all.min.js @@ -3,5 +3,5 @@ u=r.scrollTop||i.scrollTop||0):(o+=n.scrollLeft||0,u+=n.scrollTop||0);return{x:d t+'" stdDeviation="'+(this.blur?this.blur/3:0)+'">'+''+""+""+''+""+""},toObject:function(){if(this.includeDefaultValues)return{color:this.color,blur:this.blur,offsetX:this.offsetX,offsetY:this.offsetY};var e={},n=t.Shadow.prototype;return this.color!==n.color&&(e.color=this.color),this.blur!==n.blur&&(e.blur=this.blur),this.offsetX!==n.offsetX&&(e.offsetX=this.offsetX),this.offsetY!==n.offsetY&&(e.offsetY=this.offsetY),e}}),t.Shadow.reOffsetsAndBlur=/(?:\s|^)(-?\d+(?:px)?(?:\s?|$))?(-?\d+(?:px)?(?:\s?|$))?(\d+(?:px)?)?(?:\s?|$)(?:$|\s)/}(typeof exports!="undefined"?exports:this),function(){"use strict";if(fabric.StaticCanvas){fabric.warn("fabric.StaticCanvas is already defined.");return}var e=fabric.util.object.extend,t=fabric.util.getElementOffset,n=fabric.util.removeFromArray,r=fabric.util.removeListener,i=new Error("Could not initialize `canvas` element");fabric.StaticCanvas=fabric.util.createClass({initialize:function(e,t){t||(t={}),this._initStatic(e,t),fabric.StaticCanvas.activeInstance=this},backgroundColor:"",backgroundImage:"",backgroundImageOpacity:1,backgroundImageStretch:!0,overlayImage:"",overlayImageLeft:0,overlayImageTop:0,includeDefaultValues:!0,stateful:!0,renderOnAddRemove:!0,clipTo:null,controlsAboveOverlay:!1,allowTouchScrolling:!1,onBeforeScaleRotate:function(){},_initStatic:function(e,t){this._objects=[],this._createLowerCanvas(e),this._initOptions(t),t.overlayImage&&this.setOverlayImage(t.overlayImage,this.renderAll.bind(this)),t.backgroundImage&&this.setBackgroundImage(t.backgroundImage,this.renderAll.bind(this)),t.backgroundColor&&this.setBackgroundColor(t.backgroundColor,this.renderAll.bind(this)),this.calcOffset()},calcOffset:function(){return this._offset=t(this.lowerCanvasEl),this},setOverlayImage:function(e,t,n){return fabric.util.loadImage(e,function(e){this.overlayImage=e,n&&"overlayImageLeft"in n&&(this.overlayImageLeft=n.overlayImageLeft),n&&"overlayImageTop"in n&&(this.overlayImageTop=n.overlayImageTop),t&&t()},this),this},setBackgroundImage:function(e,t,n){return fabric.util.loadImage(e,function(e){this.backgroundImage=e,n&&"backgroundImageOpacity"in n&&(this.backgroundImageOpacity=n.backgroundImageOpacity),n&&"backgroundImageStretch"in n&&(this.backgroundImageStretch=n.backgroundImageStretch),t&&t()},this),this},setBackgroundColor:function(e,t){if(e.source){var n=this;fabric.util.loadImage(e.source,function(r){n.backgroundColor=new fabric.Pattern({source:r,repeat:e.repeat}),t&&t()})}else this.backgroundColor=e,t&&t();return this},_createCanvasElement:function(){var e=fabric.document.createElement("canvas");e.style||(e.style={});if(!e)throw i;return this._initCanvasElement(e),e},_initCanvasElement:function(e){fabric.util.createCanvasElement(e);if(typeof e.getContext=="undefined")throw i},_initOptions:function(e){for(var t in e)this[t]=e[t];this.width=parseInt(this.lowerCanvasEl.width,10)||0,this.height=parseInt(this.lowerCanvasEl.height,10)||0;if(!this.lowerCanvasEl.style)return;this.lowerCanvasEl.style.width=this.width+"px",this.lowerCanvasEl.style.height=this.height+"px"},_createLowerCanvas:function(e){this.lowerCanvasEl=fabric.util.getById(e)||this._createCanvasElement(),this._initCanvasElement(this.lowerCanvasEl),fabric.util.addClass(this.lowerCanvasEl,"lower-canvas"),this.interactive&&this._applyCanvasStyle(this.lowerCanvasEl),this.contextContainer=this.lowerCanvasEl.getContext("2d")},getWidth:function(){return this.width},getHeight:function(){return this.height},setWidth:function(e){return this._setDimension("width",e)},setHeight:function(e){return this._setDimension("height",e)},setDimensions:function(e){for(var t in e)this._setDimension(t,e[t]);return this},_setDimension:function(e,t){return this.lowerCanvasEl[e]=t,this.lowerCanvasEl.style[e]=t+"px",this.upperCanvasEl&&(this.upperCanvasEl[e]=t,this.upperCanvasEl.style[e]=t+"px"),this.cacheCanvasEl&&(this.cacheCanvasEl[e]=t),this.wrapperEl&&(this.wrapperEl.style[e]=t+"px"),this[e]=t,this.calcOffset(),this.renderAll(),this},getElement:function(){return this.lowerCanvasEl},getActiveObject:function(){return null},getActiveGroup:function(){return null},_draw:function(e,t){if(!t)return;if(this.controlsAboveOverlay){var n=t.hasBorders,r=t.hasControls;t.hasBorders=t.hasControls=!1,t.render(e),t.hasBorders=n,t.hasControls=r}else t.render(e)},_onObjectAdded:function(e){this.stateful&&e.setupState(),e.setCoords(),e.canvas=this,this.fire("object:added",{target:e}),e.fire("added")},_onObjectRemoved:function(e){this.fire("object:removed",{target:e}),e.fire("removed")},getObjects:function(){return this._objects},clearContext:function(e){return e.clearRect(0,0,this.width,this.height),this},getContext:function(){return this.contextContainer},clear:function(){return this._objects.length=0,this.discardActiveGroup&&this.discardActiveGroup(),this.discardActiveObject&&this.discardActiveObject(),this.clearContext(this.contextContainer),this.contextTop&&this.clearContext(this.contextTop),this.fire("canvas:cleared"),this.renderAll(),this},renderAll:function(e){var t=this[e===!0&&this.interactive?"contextTop":"contextContainer"],n=this.getActiveGroup();return this.contextTop&&this.selection&&!this._groupSelector&&this.clearContext(this.contextTop),e||this.clearContext(t),this.fire("before:render"),this.clipTo&&fabric.util.clipContext(this,t),this._renderBackground(t),this._renderObjects(t,n),this._renderActiveGroup(t,n),this.clipTo&&t.restore(),this.overlayImage&&t.drawImage(this.overlayImage,this.overlayImageLeft,this.overlayImageTop),this.controlsAboveOverlay&&this.interactive&&this.drawControls(t),this.fire("after:render"),this},_renderObjects:function(e,t){for(var n=0,r=this._objects.length;n','\n')},_setSVGHeader:function(e,t){e.push("',"Created with Fabric.js ",fabric.version,"","",fabric.createSVGFontFacesMarkup(this.getObjects()),fabric.createSVGRefElementsMarkup(this),"")},_setSVGBackgroundColor:function(e){this.backgroundColor&&this.backgroundColor.source&&e.push('")},_setSVGBackgroundImage:function(e){this.backgroundImage&&e.push('')},_setSVGOverlayImage:function(e){this.overlayImage&&e.push('')},_setSVGObjects:function(e,t){var n=this.getActiveGroup();n&&this.discardActiveGroup();for(var r=0,i=this.getObjects(),s=i.length;r")},remove:function(e){return this.getActiveObject()===e&&(this.fire("before:selection:cleared",{target:e}),this.discardActiveObject(),this.fire("selection:cleared")),fabric.Collection.remove.call(this,e)},sendToBack:function(e){return n(this._objects,e),this._objects.unshift(e),this.renderAll&&this.renderAll()},bringToFront:function(e){return n(this._objects,e),this._objects.push(e),this.renderAll&&this.renderAll()},sendBackwards:function(e,t){var r=this._objects.indexOf(e);if(r!==0){var i;if(t){i=r;for(var s=r-1;s>=0;--s){var o=e.intersectsWithObject(this._objects[s])||e.isContainedWithinObject(this._objects[s])||this._objects[s].isContainedWithinObject(e);if(o){i=s;break}}}else i=r-1;n(this._objects,e),this._objects.splice(i,0,e),this.renderAll&&this.renderAll()}return this},bringForward:function(e,t){var r=this._objects.indexOf(e);if(r!==this._objects.length-1){var i;if(t){i=r;for(var s=r+1;s"}}),e(fabric.StaticCanvas.prototype,fabric.Observable),e(fabric.StaticCanvas.prototype,fabric.Collection),e(fabric.StaticCanvas.prototype,fabric.DataURLExporter),e(fabric.StaticCanvas,{EMPTY_JSON:'{"objects": [], "background": "white"}',supports:function(e){var t=fabric.util.createCanvasElement();if(!t||!t.getContext)return null;var n=t.getContext("2d");if(!n)return null;switch(e){case"getImageData":return typeof n.getImageData!="undefined";case"setLineDash":return typeof n.setLineDash!="undefined";case"toDataURL":return typeof t.toDataURL!="undefined";case"toDataURLWithQuality":try{return t.toDataURL("image/jpeg",0),!0}catch(r){}return!1;default:return null}}}),fabric.StaticCanvas.prototype.toJSON=fabric.StaticCanvas.prototype.toObject}(),fabric.BaseBrush=fabric.util.createClass({color:"rgb(0, 0, 0)",width:1,shadow:null,strokeLineCap:"round",strokeLineJoin:"round",setShadow:function(e){return this.shadow=new fabric.Shadow(e),this},_setBrushStyles:function(){var e=this.canvas.contextTop;e.strokeStyle=this.color,e.lineWidth=this.width,e.lineCap=this.strokeLineCap,e.lineJoin=this.strokeLineJoin},_setShadow:function(){if(!this.shadow)return;var e=this.canvas.contextTop;e.shadowColor=this.shadow.color,e.shadowBlur=this.shadow.blur,e.shadowOffsetX=this.shadow.offsetX,e.shadowOffsetY=this.shadow.offsetY},_resetShadow:function(){var e=this.canvas.contextTop;e.shadowColor="",e.shadowBlur=e.shadowOffsetX=e.shadowOffsetY=0}}),function(){var e=fabric.util.array.min,t=fabric.util.array.max;fabric.PencilBrush=fabric.util.createClass(fabric.BaseBrush,{initialize:function(e){this.canvas=e,this._points=[]},onMouseDown:function(e){this._prepareForDrawing(e),this._captureDrawingPath(e),this._render()},onMouseMove:function(e){this._captureDrawingPath(e),this.canvas.clearContext(this.canvas.contextTop),this._render()},onMouseUp:function(){this._finalizeAndAddPath()},_prepareForDrawing:function(e){var t=new fabric.Point(e.x,e.y);this._reset(),this._addPoint(t),this.canvas.contextTop.moveTo(t.x,t.y)},_addPoint:function(e){this._points.push(e)},_reset:function(){this._points.length=0,this._setBrushStyles(),this._setShadow()},_captureDrawingPath:function(e){var t=new fabric.Point(e.x,e.y);this._addPoint(t)},_render:function(){var e=this.canvas.contextTop;e.beginPath();var t=this._points[0],n=this._points[1];this._points.length===2&&t.x===n.x&&t.y===n.y&&(t.x-=.5,n.x+=.5),e.moveTo(t.x,t.y);for(var r=1,i=this._points.length;r0&&(t>this.targetFindTolerance?t-=this.targetFindTolerance:t=0,n>this.targetFindTolerance?n-=this.targetFindTolerance:n=0);var o=!0,u=r.getImageData(t,n,this.targetFindTolerance*2||1,this.targetFindTolerance*2||1);for(var a=3,f=u.data.length;an.padding?e.x<0?e.x+=n.padding:e.x-=n.padding:e.x=0,i(e.y)>n.padding?e.y<0?e.y+=n.padding:e.y-=n.padding:e.y=0},_rotateObject:function(e,t){var i=this._currentTransform,s=this._offset;if(i.target.get("lockRotation"))return;var o=r(i.ey-i.top-s.top,i.ex-i.left-s.left),u=r(t-i.top-s.top,e-i.left-s.left),a=n(u-o+i.theta);a<0&&(a=360+a),i.target.angle=a},_setCursor:function(e){this.upperCanvasEl.style.cursor=e},_resetObjectTransform:function(e){e.scaleX=1,e.scaleY=1,e.setAngle(0)},_drawSelection:function(){var e=this.contextTop,t=this._groupSelector,n=t.left,r=t.top,s=i(n),o=i(r);e.fillStyle=this.selectionColor,e.fillRect(t.ex-(n>0?0:-n),t.ey-(r>0?0:-r),s,o),e.lineWidth=this.selectionLineWidth,e.strokeStyle=this.selectionBorderColor;if(this.selectionDashArray.length>1){var a=t.ex+u-(n>0?0:s),f=t.ey+u-(r>0?0:o);e.beginPath(),fabric.util.drawDashedLine(e,a,f,a+s,f,this.selectionDashArray),fabric.util.drawDashedLine(e,a,f+o-1,a+s,f+o-1,this.selectionDashArray),fabric.util.drawDashedLine(e,a,f,a,f+o,this.selectionDashArray),fabric.util.drawDashedLine(e,a+s-1,f,a+s-1,f+o,this.selectionDashArray),e.closePath(),e.stroke()}else e.strokeRect(t.ex+u-(n>0?0:s),t.ey+u-(r>0?0:o),s,o)},_findSelectedObjects:function(e){if(!this.selection)return;var t=this._collectObjects();t.length===1?this.setActiveObject(t[0],e):t.length>1&&(t=new fabric.Group(t.reverse()),this.setActiveGroup(t),t.saveCoords(),this.fire("selection:created",{target:t}),this.renderAll())},_collectObjects:function(){var e=[],t,n=this._groupSelector.ex,r=this._groupSelector.ey,i=n+this._groupSelector.left,u=r+this._groupSelector.top,a=new fabric.Point(s(n,i),s(r,u)),f=new fabric.Point(o(n,i),o(r,u)),l=n===i&&r===u;for(var c=this._objects.length;c--;){t=this._objects[c];if(!t||!t.selectable||!t.visible)continue;if(t.intersectsWithRect(a,f)||t.isContainedWithinRect(a,f)||t.containsPoint(a)||t.containsPoint(f)){t.set("active",!0),e.push(t);if(l)break}}return e},findTarget:function(e,t){if(this.skipTargetFind)return;var n,r=this.getPointer(e);if(this.controlsAboveOverlay&&this.lastRenderedObjectWithControlsAboveOverlay&&this.lastRenderedObjectWithControlsAboveOverlay.visible&&this.containsPoint(e,this.lastRenderedObjectWithControlsAboveOverlay)&&this.lastRenderedObjectWithControlsAboveOverlay._findTargetCorner(e,this._offset))return n=this.lastRenderedObjectWithControlsAboveOverlay,n;var i=this.getActiveGroup();if(i&&!t&&this.containsPoint(e,i))return n=i,n;var s=[];for(var o=this._objects.length;o--;)if(this._objects[o]&&this._objects[o].visible&&this._objects[o].evented&&this.containsPoint(e,this._objects[o])){if(!this.perPixelTargetFind&&!this._objects[o].perPixelTargetFind){n=this._objects[o],this.relatedTarget=n;break}s[s.length]=this._objects[o]}for(var u=0,a=s.length;u"},get:function(e){return this[e]},set:function(e,t){if(typeof e=="object")for(var n in e)this._set(n,e[n]);else typeof t=="function"&&e!=="clipTo"?this._set(e,t(this.get(e))):this._set(e,t);return this},_set:function(e,n){var i=e==="scaleX"||e==="scaleY";return i&&(n=this._constrainScale(n)),e==="scaleX"&&n<0?(this.flipX=!this.flipX,n*=-1):e==="scaleY"&&n<0?(this.flipY=!this.flipY,n*=-1):e==="width"||e==="height"?this.minScaleLimit=r(Math.min(.1,1/Math.max(this.width,this.height)),2):e==="shadow"&&n&&!(n instanceof t.Shadow)&&(n=new t.Shadow(n)),this[e]=n,this},toggle:function(e){var t=this.get(e);return typeof t=="boolean"&&this.set(e,!t),this},setSourcePath:function(e){return this.sourcePath=e,this},render:function(e,n){if(this.width===0||this.height===0||!this.visible)return;e.save(),this._transform(e,n),this._setStrokeStyles(e),this._setFillStyles(e);var r=this.transformMatrix;r&&this.group&&(e.translate(-this.group.width/2,-this.group.height/2),e.transform(r[0],r[1],r[2],r[3],r[4],r[5])),this._setShadow(e),this.clipTo&&t.util.clipContext(this,e),this._render(e,n),this.clipTo&&e.restore(),this._removeShadow(e),this.active&&!n&&(this.drawBorders(e),this.drawControls(e)),e.restore()},_transform:function(e,t){var n=this.transformMatrix;n&&!this.group&&e.setTransform(n[0],n[1],n[2],n[3],n[4],n[5]),t||this.transform(e)},_setStrokeStyles:function(e){this.stroke&&(e.lineWidth=this.strokeWidth,e.lineCap=this.strokeLineCap,e.lineJoin=this.strokeLineJoin,e.miterLimit=this.strokeMiterLimit,e.strokeStyle=this.stroke.toLive?this.stroke.toLive(e):this.stroke)},_setFillStyles:function(e){this.fill&&(e.fillStyle=this.fill.toLive?this.fill.toLive(e):this.fill)},_setShadow:function(e){if(!this.shadow)return;e.shadowColor=this.shadow.color,e.shadowBlur=this.shadow.blur,e.shadowOffsetX=this.shadow.offsetX,e.shadowOffsetY=this.shadow.offsetY},_removeShadow:function(e){e.shadowColor="",e.shadowBlur=e.shadowOffsetX=e.shadowOffsetY=0},_renderFill:function(e){if(!this.fill)return;this.fill.toLive&&(e.save(),e.translate(-this.width/2+this.fill.offsetX||0,-this.height/2+this.fill.offsetY||0)),e.fill(),this.fill.toLive&&e.restore(),this.shadow&&!this.shadow.affectStroke&&this._removeShadow(e)},_renderStroke:function(e){if(!this.stroke)return;e.save(),this.strokeDashArray?(1&this.strokeDashArray.length&&this.strokeDashArray.push.apply(this.strokeDashArray,this.strokeDashArray),o?(e.setLineDash(this.strokeDashArray),this._stroke&&this._stroke(e)):this._renderDashedStroke&&this._renderDashedStroke(e),e.stroke()):this._stroke?this._stroke(e):e.stroke(),this._removeShadow(e),e.restore()},clone:function(e,n){return this.constructor.fromObject?this.constructor.fromObject(this.toObject(n),e):new t.Object(this.toObject(n))},cloneAsImage:function(e){var n=this.toDataURL();return t.util.loadImage(n,function(n){e&&e(new t.Image(n))}),this},toDataURL:function(e){e||(e={});var n=t.util.createCanvasElement(),r=this.getBoundingRect();n.width=r.width,n.height=r.height,t.util.wrapElement(n,"div");var i=new t.Canvas(n);e.format==="jpg"&&(e.format="jpeg"),e.format==="jpeg"&&(i.backgroundColor="#fff");var s={active:this.get("active"),left:this.getLeft(),top:this.getTop()};this.set("active",!1),this.setPositionByOrigin(new t.Point(n.width/2,n.height/2),"center","center"),i.add(this);var o=i.toDataURL(e);return this.set(s).setCoords(),i.dispose(),i=null,o},isType:function(e){return this.type===e},complexity:function(){return 0},toJSON:function(e){return this.toObject(e)},setGradient:function(e,n){n||(n={});var r={colorStops:[]};r.type=n.type||(n.r1||n.r2?"radial":"linear"),r.coords={x1:n.x1,y1:n.y1,x2:n.x2,y2:n.y2};if(n.r1||n.r2)r.coords.r1=n.r1,r.coords.r2=n.r2;for(var i in n.colorStops){var s=new t.Color(n.colorStops[i]);r.colorStops.push({offset:i,color:s.toRgb(),opacity:s.getAlpha()})}return this.set(e,t.Gradient.forObject(this,r))},setPatternFill:function(e){return this.set("fill",new t.Pattern(e))},setShadow:function(e){return this.set("shadow",new t.Shadow(e))},setColor:function(e){return this.set("fill",e),this},centerH:function(){return this.canvas.centerObjectH(this),this},centerV:function(){return this.canvas.centerObjectV(this),this},center:function(){return this.centerH().centerV()},remove:function(){return this.canvas.remove(this)},sendToBack:function(){return this.group?t.StaticCanvas.prototype.sendToBack.call(this.group,this):this.canvas.sendToBack(this),this},bringToFront:function(){return this.group?t.StaticCanvas.prototype.bringToFront.call(this.group,this):this.canvas.bringToFront(this),this},sendBackwards:function(e){return this.group?t.StaticCanvas.prototype.sendBackwards.call(this.group,this,e):this.canvas.sendBackwards(this,e),this},bringForward:function(e){return this.group?t.StaticCanvas.prototype.bringForward.call(this.group,this,e):this.canvas.bringForward(this,e),this},moveTo:function(e){return this.group?t.StaticCanvas.prototype.moveTo.call(this.group,this,e):this.canvas.moveTo(this,e),this}}),t.util.createAccessors(t.Object),t.Object.prototype.rotate=t.Object.prototype.setAngle,n(t.Object.prototype,t.Observable),t.Object.NUM_FRACTION_DIGITS=2,t.Object.__uid=0}(typeof exports!="undefined"?exports:this),function(){var e=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{translateToCenterPoint:function(t,n,r){var i=t.x,s=t.y;return n==="left"?i=t.x+(this.getWidth()+this.strokeWidth*this.scaleX)/2:n==="right"&&(i=t.x-(this.getWidth()+this.strokeWidth*this.scaleX)/2),r==="top"?s=t.y+(this.getHeight()+this.strokeWidth*this.scaleY)/2:r==="bottom"&&(s=t.y-(this.getHeight()+this.strokeWidth*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(i,s),t,e(this.angle))},translateToOriginPoint:function(t,n,r){var i=t.x,s=t.y;return n==="left"?i=t.x-(this.getWidth()+this.strokeWidth*this.scaleX)/2:n==="right"&&(i=t.x+(this.getWidth()+this.strokeWidth*this.scaleX)/2),r==="top"?s=t.y-(this.getHeight()+this.strokeWidth*this.scaleY)/2:r==="bottom"&&(s=t.y+(this.getHeight()+this.strokeWidth*this.scaleY)/2),fabric.util.rotatePoint(new fabric.Point(i,s),t,e(this.angle))},getCenterPoint:function(){var e=new fabric.Point(this.left,this.top);return this.translateToCenterPoint(e,this.originX,this.originY)},getPointByOrigin:function(e,t){var n=this.getCenterPoint();return this.translateToOriginPoint(n,e,t)},toLocalPoint:function(t,n,r){var i=this.getCenterPoint(),s,o;return n!==undefined&&r!==undefined?(n==="left"?s=i.x-(this.getWidth()+this.strokeWidth*this.scaleX)/2:n==="right"?s=i.x+(this.getWidth()+this.strokeWidth*this.scaleX)/2:s=i.x,r==="top"?o=i.y-(this.getHeight()+this.strokeWidth*this.scaleY)/2:r==="bottom"?o=i.y+(this.getHeight()+this.strokeWidth*this.scaleY)/2:o=i.y):(s=this.left,o=this.top),fabric.util.rotatePoint(new fabric.Point(t.x,t.y),i,-e(this.angle)).subtractEquals(new fabric.Point(s,o))},setPositionByOrigin:function(e,t,n){var r=this.translateToCenterPoint(e,t,n),i=this.translateToOriginPoint(r,this.originX,this.originY);this.set("left",i.x),this.set("top",i.y)},adjustPosition:function(t){var n=e(this.angle),r=this.getWidth()/2,i=Math.cos(n)*r,s=Math.sin(n)*r,o=this.getWidth(),u=Math.cos(n)*o,a=Math.sin(n)*o;this.originX==="center"&&t==="left"||this.originX==="right"&&t==="center"?(this.left-=i,this.top-=s):this.originX==="left"&&t==="center"||this.originX==="center"&&t==="right"?(this.left+=i,this.top+=s):this.originX==="left"&&t==="right"?(this.left+=u,this.top+=a):this.originX==="right"&&t==="left"&&(this.left-=u,this.top-=a),this.setCoords(),this.originX=t},_getLeftTopCoords:function(){return this.translateToOriginPoint(this.getCenterPoint(),"left","center")}})}(),function(){var e=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{oCoords:null,intersectsWithRect:function(e,t){var n=this.oCoords,r=new fabric.Point(n.tl.x,n.tl.y),i=new fabric.Point(n.tr.x,n.tr.y),s=new fabric.Point(n.bl.x,n.bl.y),o=new fabric.Point(n.br.x,n.br.y),u=fabric.Intersection.intersectPolygonRectangle([r,i,o,s],e,t);return u.status==="Intersection"},intersectsWithObject:function(e){function t(e){return{tl:new fabric.Point(e.tl.x,e.tl.y),tr:new fabric.Point(e.tr.x,e.tr.y),bl:new fabric.Point(e.bl.x,e.bl.y),br:new fabric.Point(e.br.x,e.br.y)}}var n=t(this.oCoords),r=t(e.oCoords),i=fabric.Intersection.intersectPolygonPolygon([n.tl,n.tr,n.br,n.bl],[r.tl,r.tr,r.br,r.bl]);return i.status==="Intersection"},isContainedWithinObject:function(e){var t=e.getBoundingRect(),n=new fabric.Point(t.left,t.top),r=new fabric.Point(t.left+t.width,t.top+t.height);return this.isContainedWithinRect(n,r)},isContainedWithinRect:function(e,t){var n=this.getBoundingRect();return n.left>e.x&&n.left+n.widthe.y&&n.top+n.height=e.y&&f.d.y>=e.y)continue;f.o.x===f.d.x&&f.o.x>=e.x?(o=f.o.x,u=e.y):(n=0,r=(f.d.y-f.o.y)/(f.d.x-f.o.x),i=e.y-n*e.x,s=f.o.y-r*f.o.x,o=-(i-s)/(n-r),u=i+n*o),o>=e.x&&(a+=1);if(a===2)break}return a},getBoundingRectWidth:function(){return this.getBoundingRect().width},getBoundingRectHeight:function(){return this.getBoundingRect().height},getBoundingRect:function(){this.oCoords||this.setCoords();var e=[this.oCoords.tl.x,this.oCoords.tr.x,this.oCoords.br.x,this.oCoords.bl.x],t=fabric.util.array.min(e),n=fabric.util.array.max(e),r=Math.abs(t-n),i=[this.oCoords.tl.y,this.oCoords.tr.y,this.oCoords.br.y,this.oCoords.bl.y],s=fabric.util.array.min(i),o=fabric.util.array.max(i),u=Math.abs(s-o);return{left:t,top:s,width:r,height:u}},getWidth:function(){return this.width*this.scaleX},getHeight:function(){return this.height*this.scaleY},_constrainScale:function(e){return Math.abs(e)1?this.strokeWidth:0,n=this.padding,r=e(this.angle);this.currentWidth=(this.width+t)*this.scaleX+n*2,this.currentHeight=(this.height+t)*this.scaleY+n*2,this.currentWidth<0&&(this.currentWidth=Math.abs(this.currentWidth));var i=Math.sqrt(Math.pow(this.currentWidth/2,2)+Math.pow(this.currentHeight/2,2)),s=Math.atan(isFinite(this.currentHeight/this.currentWidth)?this.currentHeight/this.currentWidth:0),o=Math.cos(s+r)*i,u=Math.sin(s+r)*i,a=Math.sin(r),f=Math.cos(r),l=this.getCenterPoint(),c={x:l.x-o,y:l.y-u},h={x:c.x+this.currentWidth*f,y:c.y+this.currentWidth*a},p={x:h.x-this.currentHeight*a,y:h.y+this.currentHeight*f},d={x:c.x-this.currentHeight*a,y:c.y+this.currentHeight*f},v={x:c.x-this.currentHeight/2*a,y:c.y+this.currentHeight/2*f},m={x:c.x+this.currentWidth/2*f,y:c.y+this.currentWidth/2*a},g={x:h.x-this.currentHeight/2*a,y:h.y+this.currentHeight/2*f},y={x:d.x+this.currentWidth/2*f,y:d.y+this.currentWidth/2*a},b={x:m.x,y:m.y};return this.oCoords={tl:c,tr:h,br:p,bl:d,ml:v,mt:m,mr:g,mb:y,mtr:b},this._setCornerCoords&&this._setCornerCoords(),this}})}(),fabric.util.object.extend(fabric.Object.prototype,{hasStateChanged:function(){return this.stateProperties.some(function(e){return this.get(e)!==this.originalState[e]},this)},saveState:function(e){return this.stateProperties.forEach(function(e){this.originalState[e]=this.get(e)},this),e&&e.stateProperties&&e.stateProperties.forEach(function(e){this.originalState[e]=this.get(e)},this),this},setupState:function(){return this.originalState={},this.saveState(),this}}),function(){var e=fabric.util.getPointer,t=fabric.util.degreesToRadians;fabric.util.object.extend(fabric.Object.prototype,{_findTargetCorner:function(t,n){if(!this.hasControls||!this.active)return!1;var r=e(t,this.canvas.upperCanvasEl),i=r.x-n.left,s=r.y-n.top,o,u;for(var a in this.oCoords){if(a==="mtr"&&!this.hasRotatingPoint)continue;if(!(!this.get("lockUniScaling")||a!=="mt"&&a!=="mr"&&a!=="mb"&&a!=="ml"))continue;u=this._getImageLines(this.oCoords[a].corner),o=this._findCrossPoints({x:i,y:s},u);if(o!==0&&o%2===1)return this.__corner=a,a}return!1},_setCornerCoords:function(){var e=this.oCoords,n=t(this.angle),r=t(45-this.angle),i=Math.sqrt(2*Math.pow(this.cornerSize,2))/2,s=i*Math.cos(r),o=i*Math.sin(r),u=Math.sin(n),a=Math.cos(n);e.tl.corner={tl:{x:e.tl.x-o,y:e.tl.y-s},tr:{x:e.tl.x+s,y:e.tl.y-o},bl:{x:e.tl.x-s,y:e.tl.y+o},br:{x:e.tl.x+o,y:e.tl.y+s}},e.tr.corner={tl:{x:e.tr.x-o,y:e.tr.y-s},tr:{x:e.tr.x+s,y:e.tr.y-o},br:{x:e.tr.x+o,y:e.tr.y+s},bl:{x:e.tr.x-s,y:e.tr.y+o}},e.bl.corner={tl:{x:e.bl.x-o,y:e.bl.y-s},bl:{x:e.bl.x-s,y:e.bl.y+o},br:{x:e.bl.x+o,y:e.bl.y+s},tr:{x:e.bl.x+s,y:e.bl.y-o}},e.br.corner={tr:{x:e.br.x+s,y:e.br.y-o},bl:{x:e.br.x-s,y:e.br.y+o},br:{x:e.br.x+o,y:e.br.y+s},tl:{x:e.br.x-o,y:e.br.y-s}},e.ml.corner={tl:{x:e.ml.x-o,y:e.ml.y-s},tr:{x:e.ml.x+s,y:e.ml.y-o},bl:{x:e.ml.x-s,y:e.ml.y+o},br:{x:e.ml.x+o,y:e.ml.y+s}},e.mt.corner={tl:{x:e.mt.x-o,y:e.mt.y-s},tr:{x:e.mt.x+s,y:e.mt.y-o},bl:{x:e.mt.x-s,y:e.mt.y+o},br:{x:e.mt.x+o,y:e.mt.y+s}},e.mr.corner={tl:{x:e.mr.x-o,y:e.mr.y-s},tr:{x:e.mr.x+s,y:e.mr.y-o},bl:{x:e.mr.x-s,y:e.mr.y+o},br:{x:e.mr.x+o,y:e.mr.y+s}},e.mb.corner={tl:{x:e.mb.x-o,y:e.mb.y-s},tr:{x:e.mb.x+s,y:e.mb.y-o},bl:{x:e.mb.x-s,y:e.mb.y+o},br:{x:e.mb.x+o,y:e.mb.y+s}},e.mtr.corner={tl:{x:e.mtr.x-o+u*this.rotatingPointOffset,y:e.mtr.y-s-a*this.rotatingPointOffset},tr:{x:e.mtr.x+s+u*this.rotatingPointOffset,y:e.mtr.y-o-a*this.rotatingPointOffset},bl:{x:e.mtr.x-s+u*this.rotatingPointOffset,y:e.mtr.y+o-a*this.rotatingPointOffset},br:{x:e.mtr.x+o+u*this.rotatingPointOffset,y:e.mtr.y+s-a*this.rotatingPointOffset}}},drawBorders:function(e){if(!this.hasBorders)return this;var t=this.padding,n=t*2,r=~~(this.strokeWidth/2)*2;e.save(),e.globalAlpha=this.isMoving?this.borderOpacityWhenMoving:1,e.strokeStyle=this.borderColor;var i=1/this._constrainScale(this.scaleX),s=1/this._constrainScale(this.scaleY);e.lineWidth=1/this.borderScaleFactor,e.scale(i,s);var o=this.getWidth(),u=this.getHeight();e.strokeRect (~~(-(o/2)-t-r/2*this.scaleX)-.5,~~(-(u/2)-t-r/2*this.scaleY)-.5,~~(o+n+r*this.scaleX)+1,~~(u+n+r*this.scaleY)+1);if(this.hasRotatingPoint&&!this.get("lockRotation")&&this.hasControls){var a=(this.flipY?u+r*this.scaleY+t*2:-u-r*this.scaleY-t*2)/2;e.beginPath(),e.moveTo(0,a),e.lineTo(0,a+(this.flipY?this.rotatingPointOffset:-this.rotatingPointOffset)),e.closePath(),e.stroke()}return e.restore(),this},drawControls:function(e){if(!this.hasControls)return this;var t=this.cornerSize,n=t/2,r=~~(this.strokeWidth/2),i=-(this.width/2),s=-(this.height/2),o,u,a=t/this.scaleX,f=t/this.scaleY,l=this.padding/this.scaleX,c=this.padding/this.scaleY,h=n/this.scaleY,p=n/this.scaleX,d=(n-t)/this.scaleX,v=(n-t)/this.scaleY,m=this.height,g=this.width,y=this.transparentCorners?"strokeRect":"fillRect",b=this.transparentCorners,w=typeof G_vmlCanvasManager!="undefined";return e.save(),e.lineWidth=1/Math.max(this.scaleX,this.scaleY),e.globalAlpha=this.isMoving?this.borderOpacityWhenMoving:1,e.strokeStyle=e.fillStyle=this.cornerColor,o=i-p-r-l,u=s-h-r-c,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f),o=i+g-p+r+l,u=s-h-r-c,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f),o=i-p-r-l,u=s+m+v+r+c,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f),o=i+g+d+r+l,u=s+m+v+r+c,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f),this.get("lockUniScaling")||(o=i+g/2-p,u=s-h-r-c,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f),o=i+g/2-p,u=s+m+v+r+c,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f),o=i+g+d+r+l,u=s+m/2-h,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f),o=i-p-r-l,u=s+m/2-h,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f)),this.hasRotatingPoint&&(o=i+g/2-p,u=this.flipY?s+m+this.rotatingPointOffset/this.scaleY-f/2+r+c:s-this.rotatingPointOffset/this.scaleY-f/2-r-c,w||b||e.clearRect(o,u,a,f),e[y](o,u,a,f)),e.restore(),this}})}(),fabric.util.object.extend(fabric.StaticCanvas.prototype,{FX_DURATION:500,fxCenterObjectH:function(e,t){t=t||{};var n=function(){},r=t.onComplete||n,i=t.onChange||n,s=this;return fabric.util.animate({startValue:e.get("left"),endValue:this.getCenter().left,duration:this.FX_DURATION,onChange:function(t){e.set("left",t),s.renderAll(),i()},onComplete:function(){e.setCoords(),r()}}),this},fxCenterObjectV:function(e,t){t=t||{};var n=function(){},r=t.onComplete||n,i=t.onChange||n,s=this;return fabric.util.animate({startValue:e.get("top"),endValue:this.getCenter().top,duration:this.FX_DURATION,onChange:function(t){e.set("top",t),s.renderAll(),i()},onComplete:function(){e.setCoords(),r()}}),this},fxRemove:function(e,t){t=t||{};var n=function(){},r=t.onComplete||n,i=t.onChange||n,s=this;return fabric.util.animate({startValue:e.get("opacity"),endValue:0,duration:this.FX_DURATION,onStart:function(){e.set("active",!1)},onChange:function(t){e.set("opacity",t),s.renderAll(),i()},onComplete:function(){s.remove(e),r()}}),this}}),fabric.util.object.extend(fabric.Object.prototype,{animate:function(){if(arguments[0]&&typeof arguments[0]=="object"){var e=[],t,n;for(t in arguments[0])e.push(t);for(var r=0,i=e.length;r'),e?e(t.join("")):t.join("")},complexity:function(){return 1}}),t.Line.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("x1 y1 x2 y2".split(" ")),t.Line.fromElement=function(e,r){var i=t.parseAttributes(e,t.Line.ATTRIBUTE_NAMES),s=[i.x1||0,i.y1||0,i.x2||0,i.y2||0];return new t.Line(s,n(i,r))},t.Line.fromObject=function(e){var n=[e.x1,e.y1,e.x2,e.y2];return new t.Line(n,e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";function i(e){return"radius"in e&&e.radius>0}var t=e.fabric||(e.fabric={}),n=Math.PI*2,r=t.util.object.extend;if(t.Circle){t.warn("fabric.Circle is already defined.");return}t.Circle=t.util.createClass(t.Object,{type:"circle",initialize:function(e){e=e||{},this.set("radius",e.radius||0),this.callSuper("initialize",e)},_set:function(e,t){return this.callSuper("_set",e,t),e==="radius"&&this.setRadius(t),this},toObject:function(e){return r(this.callSuper("toObject",e),{radius:this.get("radius")})},toSVG:function(e){var t=this._createBaseSVGMarkup();return t.push("'),e?e(t.join("")):t.join("")},_render:function(e,t){e.beginPath(),e.globalAlpha=this.group?e.globalAlpha*this.opacity:this.opacity,e.arc(t?this.left:0,t?this.top:0,this.radius,0,n,!1),e.closePath(),this._renderFill(e),this._renderStroke(e)},getRadiusX:function(){return this.get("radius")*this.get("scaleX")},getRadiusY:function(){return this.get("radius")*this.get("scaleY")},setRadius:function(e){this.radius=e,this.set("width",e*2).set("height",e*2)},complexity:function(){return 1}}),t.Circle.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("cx cy r".split(" ")),t.Circle.fromElement=function(e,n){n||(n={});var s=t.parseAttributes(e,t.Circle.ATTRIBUTE_NAMES);if(!i(s))throw new Error("value of `r` attribute is required and can not be negative");"left"in s&&(s.left-=n.width/2||0),"top"in s&&(s.top-=n.height/2||0);var o=new t.Circle(r(s,n));return o.cx=parseFloat(e.getAttribute("cx"))||0,o.cy=parseFloat(e.getAttribute("cy"))||0,o},t.Circle.fromObject=function(e){return new t.Circle(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={});if(t.Triangle){t.warn("fabric.Triangle is already defined");return}t.Triangle=t.util.createClass(t.Object,{type:"triangle",initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("width",e.width||100).set("height",e.height||100)},_render:function(e){var t=this.width/2,n=this.height/2;e.beginPath(),e.moveTo(-t,n),e.lineTo(0,-n),e.lineTo(t,n),e.closePath(),this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var n=this.width/2,r=this.height/2;e.beginPath(),t.util.drawDashedLine(e,-n,r,0,-r,this.strokeDashArray),t.util.drawDashedLine(e,0,-r,n,r,this.strokeDashArray),t.util.drawDashedLine(e,n,r,-n,r,this.strokeDashArray),e.closePath()},toSVG:function(e){var t=this._createBaseSVGMarkup(),n=this.width/2,r=this.height/2,i=[-n+" "+r,"0 "+ -r,n+" "+r].join(",");return t.push("'),e?e(t.join("")):t.join("")},complexity:function(){return 1}}),t.Triangle.fromObject=function(e){return new t.Triangle(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=Math.PI*2,r=t.util.object.extend;if(t.Ellipse){t.warn("fabric.Ellipse is already defined.");return}t.Ellipse=t.util.createClass(t.Object,{type:"ellipse",rx:0,ry:0,initialize:function(e){e=e||{},this.callSuper("initialize",e),this.set("rx",e.rx||0),this.set("ry",e.ry||0),this.set("width",this.get("rx")*2),this.set("height",this.get("ry")*2)},toObject:function(e){return r(this.callSuper("toObject",e),{rx:this.get("rx"),ry:this.get("ry")})},toSVG:function(e){var t=this._createBaseSVGMarkup();return t.push("'),e?e(t.join("")):t.join("")},render:function(e,t){if(this.rx===0||this.ry===0)return;return this.callSuper("render",e,t)},_render:function(e,t){e.beginPath(),e.save(),e.globalAlpha=this.group?e.globalAlpha*this.opacity:this.opacity,this.transformMatrix&&this.group&&e.translate(this.cx,this.cy),e.transform(1,0,0,this.ry/this.rx,0,0),e.arc(t?this.left:0,t?this.top:0,this.rx,0,n,!1),this._renderFill(e),this._renderStroke(e),e.restore()},complexity:function(){return 1}}),t.Ellipse.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("cx cy rx ry".split(" ")),t.Ellipse.fromElement=function(e,n){n||(n={});var i=t.parseAttributes(e,t.Ellipse.ATTRIBUTE_NAMES),s=i.left,o=i.top;"left"in i&&(i.left-=n.width/2||0),"top"in i&&(i.top-=n.height/2||0);var u=new t.Ellipse(r(i,n));return u.cx=s||0,u.cy=o||0,u},t.Ellipse.fromObject=function(e){return new t.Ellipse(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";function i(e){return e.left=e.left||0,e.top=e.top||0,e}var t=e.fabric||(e.fabric={}),n=t.util.object.extend;if(t.Rect){console.warn("fabric.Rect is already defined");return}var r=t.Object.prototype.stateProperties.concat();r.push("rx","ry","x","y"),t.Rect=t.util.createClass(t.Object,{stateProperties:r,type:"rect",rx:0,ry:0,x:0,y:0,strokeDashArray:null,initialize:function(e){e=e||{},this.callSuper("initialize",e),this._initRxRy(),this.x=e.x||0,this.y=e.y||0},_initRxRy:function(){this.rx&&!this.ry?this.ry=this.rx:this.ry&&!this.rx&&(this.rx=this.ry)},_render:function(e){var t=this.rx||0,n=this.ry||0,r=-this.width/2,i=-this.height/2,s=this.width,o=this.height,u=this.group&&this.group.type!=="group";e.beginPath(),e.globalAlpha=u?e.globalAlpha*this.opacity:this.opacity,this.transformMatrix&&u&&e.translate(this.width/2+this.x,this.height/2+this.y),!this.transformMatrix&&u&&e.translate(-this.group.width/2+this.width/2+this.x,-this.group.height/2+this.height/2+this.y);var a=t!==0||n!==0;e.moveTo(r+t,i),e.lineTo(r+s-t,i),a&&e.quadraticCurveTo(r+s,i,r+s,i+n,r+s,i+n),e.lineTo(r+s,i+o-n),a&&e.quadraticCurveTo(r+s,i+o,r+s-t,i+o,r+s-t,i+o),e.lineTo(r+t,i+o),a&&e.quadraticCurveTo(r,i+o,r,i+o-n,r,i+o-n),e.lineTo(r,i+n),a&&e.quadraticCurveTo(r,i,r+t,i,r+t,i),e.closePath(),this._renderFill(e),this._renderStroke(e)},_renderDashedStroke:function(e){var n=-this.width/2,r=-this.height/2,i=this.width,s=this.height;e.beginPath(),t.util.drawDashedLine(e,n,r,n+i,r,this.strokeDashArray),t.util.drawDashedLine(e,n+i,r,n+i,r+s,this.strokeDashArray),t.util.drawDashedLine(e,n+i,r+s,n,r+s,this.strokeDashArray),t.util.drawDashedLine(e,n,r+s,n,r,this.strokeDashArray),e.closePath()},_normalizeLeftTopProperties:function(e){return"left"in e&&this.set("left",e.left+this.getWidth()/2),this.set("x",e.left||0),"top"in e&&this.set("top",e.top+this.getHeight()/2),this.set("y",e.top||0),this},toObject:function(e){var t=n(this.callSuper("toObject",e),{rx:this.get("rx")||0,ry:this.get("ry")||0,x:this.get("x"),y:this.get("y")});return this.includeDefaultValues||this._removeDefaultValues(t),t},toSVG:function(e){var t=this._createBaseSVGMarkup();return t.push("'),e?e(t.join("")):t.join("")},complexity:function(){return 1}}),t.Rect.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("x y rx ry width height".split(" ")),t.Rect.fromElement=function(e,r){if(!e)return null;var s=t.parseAttributes(e,t.Rect.ATTRIBUTE_NAMES);s=i(s);var o=new t.Rect(n(r?t.util.object.clone(r):{},s));return o._normalizeLeftTopProperties(s),o},t.Rect.fromObject=function(e){return new t.Rect(e)}}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.toFixed;if(t.Polyline){t.warn("fabric.Polyline is already defined");return}t.Polyline=t.util.createClass(t.Object,{type:"polyline",initialize:function(e,t,n){t=t||{},this.set("points",e),this.callSuper("initialize",t),this._calcDimensions(n)},_calcDimensions:function(e){return t.Polygon.prototype._calcDimensions.call(this,e)},toObject:function(e){return t.Polygon.prototype.toObject.call(this,e)},toSVG:function(e){var t=[],r=this._createBaseSVGMarkup();for(var i=0,s=this.points.length;i'),e?e(r.join("")):r.join("")},_render:function(e){var t;e.beginPath(),e.moveTo(this.points[0].x,this.points[0].y);for(var n=0,r=this.points.length;n'),e?e(n.join("")):n.join("")},_render:function(e){var t;e.beginPath(),e.moveTo(this.points[0].x,this.points[0].y);for(var n=0,r=this.points.length;n"},toObject:function(e){var t=s(this.callSuper("toObject",e),{path:this.path,pathOffset:this.pathOffset});return this.sourcePath&&(t.sourcePath=this.sourcePath),this.transformMatrix&&(t.transformMatrix=this.transformMatrix),t},toDatalessObject:function(e){var t=this.toObject(e);return this.sourcePath&&(t.path=this.sourcePath),delete t.sourcePath,t},toSVG:function(e){var t=[],n=this._createBaseSVGMarkup();for(var r=0,i=this.path.length;r',"",""),e?e(n.join("")):n.join("")},complexity:function(){return this.path.length},_parsePath:function(){var e=[],n=[],r,i,s=/(-?\.\d+)|(-?\d+(\.\d+)?)/g,o,u;for(var a=0,f,l=this.path.length;ad)for(var v=1,m=f.length;v"];for(var r=0,i=t.length;r"),e?e(n.join("")):n.join("")},toString:function(){return"#"},isSameColor:function(){var e=this.getObjects()[0].get("fill");return this.getObjects().every(function(t){return t.get("fill")===e})},complexity:function(){return this.paths.reduce(function(e,t){return e+(t&&t.complexity?t.complexity():0)},0)},getObjects:function(){return this.paths}}),t.PathGroup.fromObject=function(e,n){typeof e.paths=="string"?t.loadSVGFromURL(e.paths,function(r){var i=e.paths;delete e.paths;var s=t.util.groupSVGElements(r,e,i);n(s)}):t.util.enlivenObjects(e.paths,function(r){delete e.paths,n(new t.PathGroup(r,e))})},t.PathGroup.async=!0}(typeof exports!="undefined"?exports:this),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend,r=t.util.array.min,i=t.util.array.max,s=t.util.array.invoke;if(t.Group)return;var o={lockMovementX:!0,lockMovementY:!0,lockRotation:!0,lockScalingX:!0,lockScalingY:!0,lockUniScaling:!0};t.Group=t.util.createClass(t.Object,t.Collection,{type:"group",initialize:function(e,t){t=t||{},this._objects=e||[];for(var r=this._objects.length;r--;)this._objects[r].group=this;this.originalState={},this.callSuper("initialize"),this._calcBounds(),this._updateObjectsCoords(),t&&n(this,t),this._setOpacityIfSame(),this.setCoords(!0),this.saveCoords()},_updateObjectsCoords:function(){var e=this.left,t=this.top;this.forEachObject(function(n){var r=n.get("left"),i=n.get("top");n.set("originalLeft",r),n.set("originalTop",i),n.set("left",r-e),n.set("top",i-t),n.setCoords(),n.__origHasControls=n.hasControls,n.hasControls=!1},this)},toString:function(){return"#"},getObjects:function(){return this._objects},addWithUpdate:function(e){return this._restoreObjectsState(),this._objects.push(e),e.group=this,this.forEachObject(function(e){e.set("active",!0),e.group=this},this),this._calcBounds(),this._updateObjectsCoords(),this},removeWithUpdate:function(e){return this._moveFlippedObject(e),this._restoreObjectsState(),this.forEachObject(function(e){e.set("active",!0),e.group=this},this),this.remove(e),this._calcBounds(),this._updateObjectsCoords(),this},_onObjectAdded:function(e){e.group=this},_onObjectRemoved:function(e){delete e.group,e.set("active",!1)},delegatedProperties:{fill:!0,opacity:!0,fontFamily:!0,fontWeight:!0,fontSize:!0,fontStyle:!0,lineHeight:!0,textDecoration:!0,textAlign:!0,backgroundColor:!0},_set:function(e,t){if(e in this.delegatedProperties){var n=this._objects.length;this[e]=t;while(n--)this._objects[n].set(e,t)}else this[e]=t},toObject:function(e){return n(this.callSuper("toObject",e),{objects:s(this._objects,"toObject",e)})},render:function(e,n){if(!this.visible)return;e.save(),this.transform(e);var r=Math.max(this.scaleX,this.scaleY);this.clipTo&&t.util.clipContext(this,e);for(var i=0,s=this._objects.length;i'];for(var n=0,r=this._objects.length;n"),e?e(t.join("")):t.join("")},get:function(e){if(e in o){if(this[e])return this[e];for(var t=0,n=this._objects.length;t','');if(this.stroke||this.strokeDashArray){var n=this.fill;this.fill=null,t.push("'),this.fill=n}return t.push(""),e?e(t.join("")):t.join("")},getSrc:function(){return this.getElement().src||this.getElement()._src},toString:function(){return'#'},clone:function(e,t){this.constructor.fromObject -(this.toObject(t),e)},applyFilters:function(e){if(this.filters.length===0){this._element=this._originalElement,e&&e();return}var t=this._originalElement,n=fabric.util.createCanvasElement(),r=fabric.util.createImage(),i=this;return n.width=t.width,n.height=t.height,n.getContext("2d").drawImage(t,0,0,t.width,t.height),this.filters.forEach(function(e){e&&e.applyTo(n)}),r.width=t.width,r.height=t.height,fabric.isLikelyNode?(r.src=n.toBuffer(undefined,fabric.Image.pngCompression),i._element=r,e&&e()):(r.onload=function(){i._element=r,e&&e(),r.onload=n=t=null},r.src=n.toDataURL("image/png")),this},_render:function(e){e.drawImage(this._element,-this.width/2,-this.height/2,this.width,this.height)},_resetWidthHeight:function(){var e=this.getElement();this.set("width",e.width),this.set("height",e.height)},_initElement:function(e){this.setElement(fabric.util.getById(e)),fabric.util.addClass(this.getElement(),fabric.Image.CSS_CANVAS)},_initConfig:function(e){e||(e={}),this.setOptions(e),this._setWidthHeight(e)},_initFilters:function(e,t){e.filters&&e.filters.length?fabric.util.enlivenObjects(e.filters,function(e){t&&t(e)},"fabric.Image.filters"):t&&t()},_setWidthHeight:function(e){this.width="width"in e?e.width:this.getElement().width||0,this.height="height"in e?e.height:this.getElement().height||0},complexity:function(){return 1}}),fabric.Image.CSS_CANVAS="canvas-img",fabric.Image.prototype.getSvgSrc=fabric.Image.prototype.getSrc,fabric.Image.fromObject=function(e,t){fabric.util.loadImage(e.src,function(n){fabric.Image.prototype._initFilters.call(e,e,function(r){e.filters=r||[];var i=new fabric.Image(n,e);t&&t(i)})})},fabric.Image.fromURL=function(e,t,n){fabric.util.loadImage(e,function(e){t(new fabric.Image(e,n))})},fabric.Image.ATTRIBUTE_NAMES=fabric.SHARED_ATTRIBUTES.concat("x y width height xlink:href".split(" ")),fabric.Image.fromElement=function(e,n,r){var i=fabric.parseAttributes(e,fabric.Image.ATTRIBUTE_NAMES);fabric.Image.fromURL(i["xlink:href"],n,t(r?fabric.util.object.clone(r):{},i))},fabric.Image.async=!0,fabric.Image.pngCompression=1}(typeof exports!="undefined"?exports:this),fabric.util.object.extend(fabric.Object.prototype,{_getAngleValueForStraighten:function(){var e=this.getAngle()%360;return e>0?Math.round((e-1)/90)*90:Math.round(e/90)*90},straighten:function(){return this.setAngle(this._getAngleValueForStraighten()),this},fxStraighten:function(e){e=e||{};var t=function(){},n=e.onComplete||t,r=e.onChange||t,i=this;return fabric.util.animate({startValue:this.get("angle"),endValue:this._getAngleValueForStraighten(),duration:this.FX_DURATION,onChange:function(e){i.setAngle(e),r()},onComplete:function(){i.setCoords(),n()},onStart:function(){i.set("active",!1)}}),this}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{straightenObject:function(e){return e.straighten(),this.renderAll(),this},fxStraightenObject:function(e){return e.fxStraighten({onChange:this.renderAll.bind(this)}),this}}),fabric.Image.filters=fabric.Image.filters||{},fabric.Image.filters.BaseFilter=fabric.util.createClass({type:"BaseFilter",toObject:function(){return{type:this.type}},toJSON:function(){return this.toObject()}}),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.Brightness=t.util.createClass(t.Image.filters.BaseFilter,{type:"Brightness",initialize:function(e){e=e||{},this.brightness=e.brightness||100},applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=this.brightness;for(var s=0,o=r.length;s=0&&N=0&&C-1?e.channel:0},applyTo:function(e){if(!this.mask)return;var n=e.getContext("2d"),r=n.getImageData(0,0,e.width,e.height),i=r.data,s=this.mask.getElement(),o=t.util.createCanvasElement(),u=this.channel,a,f=r.width*r.height*4;o.width=s.width,o.height=s.height,o.getContext("2d").drawImage(s,0,0,s.width,s.height);var l=o.getContext("2d").getImageData(0,0,s.width,s.height),c=l.data;for(a=0;ao&&f>o&&l>o&&u(a-f)'},_render:function(e){var t=this.group&&this.group.type!=="group";t&&!this.transformMatrix?e.translate(-this.group.width/2+this.left,-this.group.height/2+this.top):t&&this.transformMatrix&&e.translate(-this.group.width/2,-this.group.height/2),typeof Cufon=="undefined"||this.useNative===!0?this._renderViaNative(e):this._renderViaCufon(e)},_renderViaNative:function(e){var n=this.text.split(/\r?\n/);this.transform(e,t.isLikelyNode),this._setTextStyles(e),this.width=this._getTextWidth(e,n),this.height=this._getTextHeight(e,n),this.clipTo&&t.util.clipContext(this,e),this._renderTextBackground(e,n),this._translateForTextAlign(e),e.save(),this._setShadow(e),this._renderTextFill(e,n),this._renderTextStroke(e,n),this._removeShadow(e),e.restore(),this.textAlign!=="left"&&this.textAlign!=="justify"&&e.restore(),this._renderTextDecoration(e,n),this.clipTo&&e.restore(),this._setBoundaries(e,n),this._totalLineHeight=0},_translateForTextAlign:function(e){this.textAlign!=="left"&&this.textAlign!=="justify"&&(e.save(),e.translate(this.textAlign==="center"?this.width/2:this.width,0))},_setBoundaries:function(e,t){this._boundaries=[];for(var n=0,r=t.length;nn&&(n=s)}return n},_renderChars:function(e,t,n,r,i){t[e](n,r,i)},_renderTextLine:function(e,t,n,r,i,s){i-=this.fontSize/4;if(this.textAlign!=="justify"){this._renderChars(e,t,n,r,i,s);return}var o=t.measureText(n).width,u=this.width;if(u>o){var a=n.split(/\s+/),f=t.measureText(n.replace(/\s+/g,"")).width,l=u-f,c=a.length-1,h=l/c,p=0;for(var d=0,v=a.length;d-1&&i(this.fontSize*this.lineHeight),this.textDecoration.indexOf("line-through")>-1&&i(this.fontSize*this.lineHeight-this.fontSize/2),this.textDecoration.indexOf("overline")>-1&&i(this.fontSize*this.lineHeight-this.fontSize)},_getFontDeclaration:function(){return[t.isLikelyNode?this.fontWeight:this.fontStyle,t.isLikelyNode?this.fontStyle:this.fontWeight,this.fontSize+"px",t.isLikelyNode?'"'+this.fontFamily+'"':this.fontFamily].join(" ")},render:function(e,t){if(!this.visible)return;e.save(),this._render(e),!t&&this.active&&(this.drawBorders(e),this.drawControls(e)),e.restore()},toObject:function(e){var t=n(this.callSuper("toObject",e),{text:this.text,fontSize:this.fontSize,fontWeight:this.fontWeight,fontFamily:this.fontFamily,fontStyle:this.fontStyle,lineHeight:this.lineHeight,textDecoration:this.textDecoration,textAlign:this.textAlign,path:this.path,textBackgroundColor:this.textBackgroundColor,useNative:this.useNative});return this.includeDefaultValues||this._removeDefaultValues(t),t},toSVG:function(e){var t=[],n=this.text.split(/\r?\n/),r=this._getSVGLeftTopOffsets(n),i=this._getSVGTextAndBg(r.lineTop,r.textLeft,n),s=this._getSVGShadows(r.lineTop,n);return r.textTop+=this._fontAscent?this._fontAscent/5*this.lineHeight:0,this._setSVGTextAndBg(t,i,s,r),e?e(t.join("")):t.join("")},_getSVGLeftTopOffsets:function(e){var t=this.useNative?this.fontSize*this.lineHeight:-this._fontAscent-this._fontAscent/5*this.lineHeight,n=-(this.width/2),r=this.useNative?this.fontSize-1:this.height/2-e.length*this.fontSize-this._totalLineHeight;return{textLeft:n,textTop:r,lineTop:t}},_setSVGTextAndBg:function(e,t,n,r){e.push('',t.textBgRects.join(""),"',n.join(""),t.textSpans.join(""),"","")},_getSVGShadows:function(e,n){var r=[],s,o,u=1;if(!this.shadow||!this._boundaries)return r;for(s=0,o=n.length;s",t.util.string.escapeXml(n[s]),""),u=1}else u++;return r},_getSVGTextAndBg:function(e,t,n){var r=[],i=[],s=1;this._setSVGBg(i);for(var o=0,u=n.length;o",t.util.string.escapeXml(e),"")},_setSVGTextLineBg:function(e,t,n,r){e.push("')},_setSVGBg:function(e){this.backgroundColor&&this._boundaries&&e.push("')},_getFillAttributes:function(e){var n=e&&typeof e=="string"?new t.Color(e):"";return!n||!n.getSource()||n.getAlpha()===1?'fill="'+e+'"':'opacity="'+n.getAlpha()+'" fill="'+n.setAlpha(1).toRgb()+'"'},_set:function(e,t){e==="fontFamily"&&this.path&&(this.path=this.path.replace(/(.*?)([^\/]*)(\.font\.js)/,"$1"+t+"$3")),this.callSuper("_set",e,t),e in this._dimensionAffectingProps&&(this._initDimensions(),this.setCoords())},complexity:function(){return 1}}),t.Text.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("x y font-family font-style font-weight font-size text-decoration".split(" ")),t.Text.fromElement=function(e,n){if(!e)return null;var r=t.parseAttributes(e,t.Text.ATTRIBUTE_NAMES);n=t.util.object.extend(n?t.util.object.clone(n):{},r);var i=new t.Text(e.textContent,n);return i.set({left:i.getLeft()+i.getWidth()/2,top:i.getTop()-i.getHeight()/2}),i},t.Text.fromObject=function(e){return new t.Text(e.text,r(e))},t.util.createAccessors(t.Text)}(typeof exports!="undefined"?exports:this),fabric.util.object.extend(fabric.Text.prototype,{_renderViaCufon:function(e){var t=Cufon.textOptions||(Cufon.textOptions={});t.left=this.left,t.top=this.top,t.context=e,t.color=this.fill;var n=this._initDummyElementForCufon();this.transform(e),Cufon.replaceElement(n,{engine:"canvas",separate:"none",fontFamily:this.fontFamily,fontWeight:this.fontWeight,textDecoration:this.textDecoration,textShadow:this.shadow&&this.shadow.toString(),textAlign:this.textAlign,fontStyle:this.fontStyle,lineHeight:this.lineHeight,stroke:this.stroke,strokeWidth:this.strokeWidth,backgroundColor:this.backgroundColor,textBackgroundColor:this.textBackgroundColor}),this.width=t.width,this.height=t.height,this._totalLineHeight=t.totalLineHeight,this._fontAscent=t.fontAscent,this._boundaries=t.boundaries,n=null,this.setCoords()},_initDummyElementForCufon:function(){var e=fabric.document.createElement("pre"),t=fabric.document.createElement("div");return t.appendChild(e),typeof G_vmlCanvasManager=="undefined"?e.innerHTML=this.text:e.innerText=this.text.replace(/\r?\n/gi,"\r"),e.style.fontSize=this.fontSize+"px",e.style.letterSpacing="normal",e}}),function(){var e=fabric.util.object.clone;fabric.ITextBehavior={initBehavior:function(){this.initKeyHandlers(),this.initCursorSelectionHandlers(),this.initDblClickSimulation(),this.initHiddenTextarea()},initKeyHandlers:function(){fabric.util.addListener(document,"keydown",this.onKeyDown.bind(this)),fabric.util.addListener(document,"keypress",this.onKeyPress.bind(this))},initHiddenTextarea:function(){if(!/(iPad|iPhone|iPod)/g.test(navigator.userAgent))return;this.hiddenTextarea=fabric.document.createElement("textarea"),this.hiddenTextarea.setAttribute("autocapitalize","off"),this.hiddenTextarea.style.cssText="position: absolute; top: 0; left: -9999px",fabric.document.body.appendChild(this.hiddenTextarea)},initDblClickSimulation:function(){var e=+(new Date),t;this.on("mousedown",function(n){t=+(new Date);if(t-e<500){this.fire("dblclick",n);var r=n.e;r.preventDefault&&r.preventDefault(),r.stopPropagation&&r.stopPropagation()}e=t})},initCursorSelectionHandlers:function(){this.initSelectedHandler(),this.initMousedownHandler(),this.initMousemoveHandler(),this.initMouseupHandler(),this.on("dblclick",function(e){this.selectWord(this.getSelectionStartFromPointer(e.e))})},initMousedownHandler:function(){this.on("mousedown",function(e){var t=this.canvas.getPointer(e.e);this.__mousedownX=t.x,this.__mousedownY=t.y,this.__isMousedown=!0,this.hiddenTextarea&&this.canvas&&this.canvas.wrapperEl.appendChild(this.hiddenTextarea),this.isEditing&&this.setCursorByClick(e.e)})},initMousemoveHandler:function(){this.on("mousemove",function(){this.__isMousedown&&this.isEditing&&console.log("mousemove: need to select text")})},initMouseupHandler:function(){this.on("mouseup",function(e){this.__isMousedown=!1;var t=this.canvas.getPointer(e.e),n=this.__mousedownX!==t.x||this.__mousedownY!==t.y;if(n)return;this.selected&&this.enterEditing()})},initSelectedHandler:function(){this.on("selected",function(){var e=this;setTimeout(function(){e.selected=!0},100),this._hasClearSelectionListener||(this.canvas.on("selection:cleared",function(t){if(t.e&&e.canvas.findTarget(t.e))return;e.exitEditing()}),this._hasClearSelectionListener=!0)})},_tick:function(){var e=this;if(this._abortCursorAnimation)return;this.animate("_currentCursorOpacity",1,{duration:this.cursorDuration,onComplete:function(){e._onTickComplete()},onChange:function(){e.canvas&&e.canvas.renderAll()},abort:function(){return e._abortCursorAnimation}})},_onTickComplete:function(){if(this._abortCursorAnimation)return;var e=this;this._cursorTimeout1&&clearTimeout(this._cursorTimeout1),this._cursorTimeout1=setTimeout(function(){e.animate("_currentCursorOpacity",0,{duration:this.cursorDuration/2,onComplete:function(){e._tick()},onChange:function(){e.canvas&&e.canvas.renderAll()},abort:function(){return e._abortCursorAnimation}})},100)},initDelayedCursor:function(){var e=this;this._cursorTimeout2&&clearTimeout(this._cursorTimeout2),this._cursorTimeout2=setTimeout(function(){e._abortCursorAnimation=!1,e._tick()},this.cursorDelay)},abortCursorAnimation:function(){this._abortCursorAnimation=!0,clearTimeout(this._cursorTimeout1),clearTimeout(this._cursorTimeout2),this._currentCursorOpacity=0,this.canvas.renderAll();var e=this;setTimeout(function(){e._abortCursorAnimation=!1},10)},onKeyDown:function(e){if(!this.isEditing||e.ctrlKey)return;if(e.keyCode===39)this.moveCursorRight(e);else if(e.keyCode===37)this.moveCursorLeft(e);else if(e.keyCode===38)this.moveCursorUp(e);else if(e.keyCode===40)this.moveCursorDown(e);else if(e.keyCode===8)this.removeChars(e);else if(e.keyCode===46)this.moveCursorRight(e),this.removeChars(e);else{if(e.keyCode!==13)return;this.insertNewline()}e.preventDefault(),e.stopPropagation(),this.canvas&&this.canvas.renderAll()},onKeyPress:function(e){if(!this.isEditing||e.metaKey||e.ctrlKey||e.keyCode===8||e.keyCode===13||e.keyCode===37||e.keyCode===38||e.keyCode===39||e.keyCode===40)return;this.insertChar(String.fromCharCode(e.which)),e.preventDefault(),e.stopPropagation()},getSelectionStartOffset:function(){var e=this.text.slice(0,this.selectionStart),t=this.text.slice(this.selectionStart),n=e.slice(e.lastIndexOf("\n")+1),r=t.match(/(.*)\n?/)[1],i=(t.match(/.*\n(.*)\n?/)||{})[1]||"";return n.length>i.length?(i+r).length+1:(n+r).length+1},moveCursorDown:function(e){this.abortCursorAnimation(),this._currentCursorOpacity=1;var t=this.getSelectionStartOffset();e.metaKey&&(t=this.text.length-this.selectionStart),e.shiftKey?this.moveCursorDownWithShift(t):this.moveCursorDownWithoutShift(t),this.initDelayedCursor()},moveCursorDownWithoutShift:function(e){this._selectionDirection="right",this.selectionStart+=e,this.selectionStart>this.text.length&&(this.selectionStart=this.text.length),this.selectionEnd=this.selectionStart},moveCursorDownWithShift:function(e){this._selectionDirection==="left"&&this.selectionStart!==this.selectionEnd?(this.selectionStart=this.selectionEnd,this._selectionDirection="right"):(this._selectionDirection="right",this.selectionEnd+=e,this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length))},moveCursorUp:function(e){var t=this.text.slice(0,this.selectionStart);this.abortCursorAnimation(),this._currentCursorOpacity=1;var n=t.slice(t.lastIndexOf("\n")+1),r=(t.match(/\n?(.*)\n.*$/)||{})[1]||"",i;n.length>r.length?i=n.length+1:i=r.length+1,e.metaKey&&(i=this.selectionStart),e.shiftKey?this.moveCursorUpWithShift(i):this.moveCursorUpWithoutShift(i),this.initDelayedCursor()},moveCursorUpWithShift:function(e){this.selectionStart===this.selectionEnd?this.selectionStart-=e:this._selectionDirection==="right"?this.selectionEnd=this.selectionStart:this.selectionStart-=e,this.selectionStart<0&&(this.selectionStart=0),this._selectionDirection="left"},moveCursorUpWithoutShift:function(e){this.selectionStart===this.selectionEnd&&(this.selectionStart-=e),this.selectionStart<0&&(this.selectionStart=0),this.selectionEnd=this.selectionStart,this._selectionDirection="left"},moveCursorLeft:function(e){if(this.selectionStart===0&&this.selectionEnd===0)return;this.abortCursorAnimation(),this._currentCursorOpacity=1,e.shiftKey?this.moveCursorLeftWithShift(e):this.moveCursorLeftWithoutShift(e),this.initDelayedCursor()},findWordBoundaryLeft:function(e){var t=0,n=e-1;if(this._reSpace.test(this.text.charAt(n)))while(this._reSpace.test(this.text.charAt(n)))t++,n--;while(/\S/.test(this.text.charAt(n))&&n>-1)t++,n--;return e-t},findWordBoundaryRight:function(e){var t=0,n=e;if(this._reSpace.test(this.text.charAt(n)))while(this._reSpace.test(this.text.charAt(n)))t++,n++;while(/\S/.test(this.text.charAt(n))&&n-1)t++,n--;return e-t},findLineBoundaryRight:function(e){var t=0,n=e;while(!/\n/.test(this.text.charAt(n))&&n=this.text.length&&this.selectionEnd>=this.text.length)return;this.abortCursorAnimation(),this._currentCursorOpacity=1,e.shiftKey?this.moveCursorRightWithShift(e):this.moveCursorRightWithoutShift(e),this.initDelayedCursor()},moveCursorRightWithShift:function(e){this._selectionDirection==="left"&&this.selectionStart!==this.selectionEnd?this._moveRight(e,"selectionStart"):(this._selectionDirection="right",this._moveRight(e,"selectionEnd"),this.text.charAt(this.selectionEnd-1)==="\n"&&this.selectionEnd++,this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length))},moveCursorRightWithoutShift:function(e){this._selectionDirection="right",this.selectionStart===this.selectionEnd?(this._moveRight(e,"selectionStart"),this.selectionEnd=this.selectionStart):(this.selectionEnd+=this.getNumNewLinesInSelectedText(),this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length),this.selectionStart=this.selectionEnd)},getNumNewLinesInSelectedText:function(){var e=this.text.slice(this.selectionStart,this.selectionEnd),t=0;for(var n=0,r=e.split(""),i=r.length;n0&&nr&&o>n){var g=n-s,y=o-n;return y>g?f=a+l:f=a+l+1,f>this.text.length&&(f=this.text.length),f}a++}}if(typeof f=="undefined")return this.text.length},enterEditing:function(){if(this.isEditing||!this.editable)return;return this.isEditing=!0,this.hiddenTextarea&&(this.hiddenTextarea.value=this.text,this.hiddenTextarea.selectionStart=this.selectionStart,this.hiddenTextarea.focus()),this._saveProps(),this.hoverCursor="text",this.canvas.defaultCursor="text",this.canvas.moveCursor="text",this.hasControls=!1,this.borderColor=this.editingBorderColor,this.selectable=!1,this.lockMovementX=!0,this.lockMovementY=!0,this._tick(),this.canvas.renderAll(),this},_saveProps:function(){this._savedProps={hasControls:this.hasControls,borderColor:this.borderColor,lockMovementX:this.lockMovementX,lockMovementY:this.lockMovementY,hoverCursor:this.hoverCursor,defaultCursor:this.canvas.defaultCursor,moveCursor:this.canvas.moveCursor}},_restoreProps:function(){if(!this._savedProps)return;this.hoverCursor=this._savedProps.overCursor,this.canvas.defaultCursor=this._savedProps.defaultCursor,this.canvas.moveCursor=this._savedProps.moveCursor,this.hasControls=this._savedProps.hasControls,this.borderColor=this._savedProps.borderColor,this.lockMovementX=this._savedProps.lockMovementX,this.lockMovementY=this._savedProps.lockMovementY},exitEditing:function(){return this.selected=!1,this.isEditing=!1,this.selectable=!0,this.hiddenTextarea&&this.hiddenTextarea.blur(),this.abortCursorAnimation(),this._restoreProps(),this._currentCursorOpacity=0,this},removeChars:function(e){if(this.selectionStart===this.selectionEnd){if(this.selectionStart!==0)if(e.metaKey){var t=this.findLineBoundaryLeft(this.selectionStart);this._removeCharsFromTo(t,this.selectionStart),this.selectionStart=t}else if(e.altKey){var n=this.findWordBoundaryLeft(this.selectionStart);this._removeCharsFromTo(n,this.selectionStart),this.selectionStart=n}else{var r=this.text.slice(this.selectionStart-1,this.selectionStart)==="\n";this.removeStyleObject(r),this.selectionStart--,this.text=this.text.slice(0,this.selectionStart)+this.text.slice(this.selectionStart+1)}}else this._removeCharsFromTo(this.selectionStart,this.selectionEnd);this.selectionEnd=this.selectionStart;var i=this.text.split(this._reNewline);for(var s in this.styles)i[s]||delete this.styles[s];this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("text:changed")},_removeCharsFromTo:function(e,t){var n=t;while(n!==e)n--,this.removeStyleObject(!1,n);this.text=this.text.slice(0,e)+this.text.slice(t)},insertChar:function(e){var t=this.text.slice(this.selectionStart,this.selectionStart+1)==="\n";this.text=this.text.slice(0,this.selectionStart)+e+this.text.slice(this.selectionEnd),this.selectionStart===this.selectionEnd?this.insertStyleObject(e,t):this.selectionEnd-this.selectionStart>1,this.selectionStart++,this.selectionEnd=this.selectionStart,this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("text:changed")},insertNewlineStyleObject:function(t,n,r){this.shiftLineStyles(t,1),this.styles[t+1]||(this.styles[t+1]={});var i=this.styles[t][n-1],s={};if(r)s[0]=e(i),this.styles[t+1]=s;else{for(var o in this.styles[t])parseInt(o,10)>=n&&(s[parseInt(o,10)-n]=this.styles[t][o],delete this.styles[t][o]);this.styles[t+1]=s}},insertCharStyleObject:function(t,n){var r=this.styles[t],i=e(r);n===0&&(n=1);for(var s in i){var o=parseInt(s,10);o>=n&&(r[o+1]=i[o])}this.styles[t][n]=e(r[n-1])},insertStyleObject:function(e,t){if(this.isEmptyStyles())return;var n=this.get2DCursorLocation(),r=n.lineIndex,i=n.charIndex;this.styles[r]||(this.styles[r]={}),e==="\n"?this.insertNewlineStyleObject(r,i,t):this.insertCharStyleObject(r,i)},shiftLineStyles:function(t,n){var r=e(this.styles);for(var i in this.styles){var s=parseInt(i,10);s>t&&(this.styles[s+n]=r[s])}},removeStyleObject:function(t,n){var r=this.get2DCursorLocation(n),i=r.lineIndex,s=r.charIndex;if(t){var o=this.text.split(this._reNewline),u=o[i-1],a=u.length;this.styles[i-1]||(this.styles[i-1]={});for(s in this.styles[i])this.styles[i-1][parseInt(s,10)+a]=this.styles[i][s];this.shiftLineStyles(i,-1)}else{var f=this.styles[i];if(f){var l=this.selectionStart===this.selectionEnd?-1:0;delete f[s+l]}var c=e(f);for(var h in c){var p=parseInt(h,10);p>=s&&p!==0&&(f[p-1]=c[p],delete f[p])}}},insertNewline:function(){this.insertChar("\n")}}}(),function(){var e=fabric.util.object.clone;fabric.IText=fabric.util.createClass(fabric.Text,fabric.Observable,fabric.ITextBehavior,{type:"i-text",selectionStart:0,selectionEnd:0,selectionColor:"rgba(17,119,255,0.3)",isEditing:!1,editable:!0,editingBorderColor:"rgba(102,153,255,0.25)",cursorWidth:2,cursorColor:"#333",cursorDelay:1e3,cursorDuration:600,styles:null,skipFillStrokeCheck:!0,_reNewline:/\r?\n/,_reSpace:/\s|\n/,_fontSizeFraction:4,_currentCursorOpacity:0,_selectionDirection:null,_abortCursorAnimation:!1,initialize:function(e,t){this.styles=t.styles||{},this.callSuper("initialize",e,t),this.initBehavior()},isEmptyStyles:function(){if(!this.styles)return!0;var e=this.styles;for(var t in e)for(var n in e[t])for(var r in e[t][n])return!1;return!0},setSelectionStart:function(e){this.selectionStart=e,this.hiddenTextarea&&(this.hiddenTextarea.selectionStart=e)},setSelectionEnd:function(e){this.selectionEnd=e,this.hiddenTextarea&&(this.hiddenTextarea.selectionEnd=e)},getSelectionStyles:function(){var e=this.get2DCursorLocation();return this.styles[e.lineIndex]?this.styles[e.lineIndex][e.charIndex]||{}:{}},setSelectionStyles:function(e){if(this.selectionStart===this.selectionEnd)this._extendStyles(this.selectionStart,e);else for(var t=this.selectionStart;t-1&&this._renderCharDecorationAtOffset(e,n,r+this.fontSize/this._fontSizeFraction,i,0),o.indexOf("line-through")>-1&&this._renderCharDecorationAtOffset(e,n,r+this.fontSize/this._fontSizeFraction,i,s/this._fontSizeFraction),o.indexOf("overline")>-1&&this._renderCharDecorationAtOffset(e,n,r,i,s-this.fontSize/this._fontSizeFraction)},_renderCharDecorationAtOffset:function(e,t,n,r,i){e.fillRect(t,n-i,r,1)},_renderTextLine:function(e,t,n,r,i,s){i+=this.fontSize/4,this.callSuper("_renderTextLine",e,t,n,r,i,s)},_renderTextDecoration:function(e,t){if(this.isEmptyStyles())return this.callSuper("_renderTextDecoration",e,t)},_renderTextLinesBackground:function(e,t){if(!this.textBackgroundColor&&!this.styles)return;e.save(),this.textBackgroundColor&&(e.fillStyle=this.textBackgroundColor);var n=0,r=this.fontSize/this._fontSizeFraction;for(var i=0,s=t.length;in&&(n=s)}return n},_getHeightOfLine:function(e,t,n){n=n||this.text.split(this._reNewline);var r=this._getHeightOfChar(e,n[t][0],t,0),i=n[t],s=i.split("");for(var o=1,u=s.length;or&&(r=a)}return r*this.lineHeight},_getTextHeight:function(e,t){var n=0;for(var r=0,i=t.length;r0?Math.round((e-1)/90)*90:Math.round(e/90)*90},straighten:function(){return this.setAngle(this._getAngleValueForStraighten()),this},fxStraighten:function(e){e=e||{};var t=function(){},n=e.onComplete||t,r=e.onChange||t,i=this;return fabric.util.animate({startValue:this.get("angle"),endValue:this._getAngleValueForStraighten(),duration:this.FX_DURATION,onChange:function(e){i.setAngle(e),r()},onComplete:function(){i.setCoords(),n()},onStart:function(){i.set("active",!1)}}),this}}),fabric.util.object.extend(fabric.StaticCanvas.prototype,{straightenObject:function(e){return e.straighten(),this.renderAll(),this},fxStraightenObject:function(e){return e.fxStraighten({onChange:this.renderAll.bind(this)}),this}}),fabric.Image.filters=fabric.Image.filters||{},fabric.Image.filters.BaseFilter=fabric.util.createClass({type:"BaseFilter",toObject:function(){return{type:this.type}},toJSON:function(){return this.toObject()}}),function(e){"use strict";var t=e.fabric||(e.fabric={}),n=t.util.object.extend;t.Image.filters.Brightness=t.util.createClass(t.Image.filters.BaseFilter,{type:"Brightness",initialize:function(e){e=e||{},this.brightness=e.brightness||100},applyTo:function(e){var t=e.getContext("2d"),n=t.getImageData(0,0,e.width,e.height),r=n.data,i=this.brightness;for(var s=0,o=r.length;s=0&&N=0&&C-1?e.channel:0},applyTo:function(e){if(!this.mask)return;var n=e.getContext("2d"),r=n.getImageData(0,0,e.width,e.height),i=r.data,s=this.mask.getElement(),o=t.util.createCanvasElement(),u=this.channel,a,f=r.width*r.height*4;o.width=s.width,o.height=s.height,o.getContext("2d").drawImage(s,0,0,s.width,s.height);var l=o.getContext("2d").getImageData(0,0,s.width,s.height),c=l.data;for(a=0;ao&&f>o&&l>o&&u(a-f)'},_render:function(e){var t=this.group&&this.group.type!=="group";t&&!this.transformMatrix?e.translate(-this.group.width/2+this.left,-this.group.height/2+this.top):t&&this.transformMatrix&&e.translate(-this.group.width/2,-this.group.height/2),typeof Cufon=="undefined"||this.useNative===!0?this._renderViaNative(e):this._renderViaCufon(e)},_renderViaNative:function(e){var n=this.text.split(/\r?\n/);this.transform(e,t.isLikelyNode),this._setTextStyles(e),this.width=this._getTextWidth(e,n),this.height=this._getTextHeight(e,n),this.clipTo&&t.util.clipContext(this,e),this._renderTextBackground(e,n),this._translateForTextAlign(e),e.save(),this._setShadow(e),this._renderTextFill(e,n),this._renderTextStroke(e,n),this._removeShadow(e),e.restore(),this.textAlign!=="left"&&this.textAlign!=="justify"&&e.restore(),this._renderTextDecoration(e,n),this.clipTo&&e.restore(),this._setBoundaries(e,n),this._totalLineHeight=0},_translateForTextAlign:function(e){this.textAlign!=="left"&&this.textAlign!=="justify"&&(e.save(),e.translate(this.textAlign==="center"?this.width/2:this.width,0))},_setBoundaries:function(e,t){this._boundaries=[];for(var n=0,r=t.length;nn&&(n=s)}return n},_renderChars:function(e,t,n,r,i){t[e](n,r,i)},_renderTextLine:function(e,t,n,r,i,s){i-=this.fontSize/4;if(this.textAlign!=="justify"){this._renderChars(e,t,n,r,i,s);return}var o=t.measureText(n).width,u=this.width;if(u>o){var a=n.split(/\s+/),f=t.measureText(n.replace(/\s+/g,"")).width,l=u-f,c=a.length-1,h=l/c,p=0;for(var d=0,v=a.length;d-1&&i(this.fontSize*this.lineHeight),this.textDecoration.indexOf("line-through")>-1&&i(this.fontSize*this.lineHeight-this.fontSize/2),this.textDecoration.indexOf("overline")>-1&&i(this.fontSize*this.lineHeight-this.fontSize)},_getFontDeclaration:function(){return[t.isLikelyNode?this.fontWeight:this.fontStyle,t.isLikelyNode?this.fontStyle:this.fontWeight,this.fontSize+"px",t.isLikelyNode?'"'+this.fontFamily+'"':this.fontFamily].join(" ")},render:function(e,t){if(!this.visible)return;e.save(),this._render(e),!t&&this.active&&(this.drawBorders(e),this.drawControls(e)),e.restore()},toObject:function(e){var t=n(this.callSuper("toObject",e),{text:this.text,fontSize:this.fontSize,fontWeight:this.fontWeight,fontFamily:this.fontFamily,fontStyle:this.fontStyle,lineHeight:this.lineHeight,textDecoration:this.textDecoration,textAlign:this.textAlign,path:this.path,textBackgroundColor:this.textBackgroundColor,useNative:this.useNative});return this.includeDefaultValues||this._removeDefaultValues(t),t},toSVG:function(e){var t=[],n=this.text.split(/\r?\n/),r=this._getSVGLeftTopOffsets(n),i=this._getSVGTextAndBg(r.lineTop,r.textLeft,n),s=this._getSVGShadows(r.lineTop,n);return r.textTop+=this._fontAscent?this._fontAscent/5*this.lineHeight:0,this._setSVGTextAndBg(t,i,s,r),e?e(t.join("")):t.join("")},_getSVGLeftTopOffsets:function(e){var t=this.useNative?this.fontSize*this.lineHeight:-this._fontAscent-this._fontAscent/5*this.lineHeight,n=-(this.width/2),r=this.useNative?this.fontSize-1:this.height/2-e.length*this.fontSize-this._totalLineHeight;return{textLeft:n,textTop:r,lineTop:t}},_setSVGTextAndBg:function(e,t,n,r){e.push('',t.textBgRects.join(""),"',n.join(""),t.textSpans.join(""),"","")},_getSVGShadows:function(e,n){var r=[],s,o,u=1;if(!this.shadow||!this._boundaries)return r;for(s=0,o=n.length;s",t.util.string.escapeXml(n[s]),""),u=1}else u++;return r},_getSVGTextAndBg:function(e,t,n){var r=[],i=[],s=1;this._setSVGBg(i);for(var o=0,u=n.length;o",t.util.string.escapeXml(e),"")},_setSVGTextLineBg:function(e,t,n,r){e.push("')},_setSVGBg:function(e){this.backgroundColor&&this._boundaries&&e.push("')},_getFillAttributes:function(e){var n=e&&typeof e=="string"?new t.Color(e):"";return!n||!n.getSource()||n.getAlpha()===1?'fill="'+e+'"':'opacity="'+n.getAlpha()+'" fill="'+n.setAlpha(1).toRgb()+'"'},_set:function(e,t){e==="fontFamily"&&this.path&&(this.path=this.path.replace(/(.*?)([^\/]*)(\.font\.js)/,"$1"+t+"$3")),this.callSuper("_set",e,t),e in this._dimensionAffectingProps&&(this._initDimensions(),this.setCoords())},complexity:function(){return 1}}),t.Text.ATTRIBUTE_NAMES=t.SHARED_ATTRIBUTES.concat("x y font-family font-style font-weight font-size text-decoration".split(" ")),t.Text.fromElement=function(e,n){if(!e)return null;var r=t.parseAttributes(e,t.Text.ATTRIBUTE_NAMES);n=t.util.object.extend(n?t.util.object.clone(n):{},r);var i=new t.Text(e.textContent,n);return i.set({left:i.getLeft()+i.getWidth()/2,top:i.getTop()-i.getHeight()/2}),i},t.Text.fromObject=function(e){return new t.Text(e.text,r(e))},t.util.createAccessors(t.Text)}(typeof exports!="undefined"?exports:this),fabric.util.object.extend(fabric.Text.prototype,{_renderViaCufon:function(e){var t=Cufon.textOptions||(Cufon.textOptions={});t.left=this.left,t.top=this.top,t.context=e,t.color=this.fill;var n=this._initDummyElementForCufon();this.transform(e),Cufon.replaceElement(n,{engine:"canvas",separate:"none",fontFamily:this.fontFamily,fontWeight:this.fontWeight,textDecoration:this.textDecoration,textShadow:this.shadow&&this.shadow.toString(),textAlign:this.textAlign,fontStyle:this.fontStyle,lineHeight:this.lineHeight,stroke:this.stroke,strokeWidth:this.strokeWidth,backgroundColor:this.backgroundColor,textBackgroundColor:this.textBackgroundColor}),this.width=t.width,this.height=t.height,this._totalLineHeight=t.totalLineHeight,this._fontAscent=t.fontAscent,this._boundaries=t.boundaries,n=null,this.setCoords()},_initDummyElementForCufon:function(){var e=fabric.document.createElement("pre"),t=fabric.document.createElement("div");return t.appendChild(e),typeof G_vmlCanvasManager=="undefined"?e.innerHTML=this.text:e.innerText=this.text.replace(/\r?\n/gi,"\r"),e.style.fontSize=this.fontSize+"px",e.style.letterSpacing="normal",e}}),function(){var e=fabric.util.object.clone;fabric.IText=fabric.util.createClass(fabric.Text,fabric.Observable,{type:"i-text",selectionStart:0,selectionEnd:0,selectionColor:"rgba(17,119,255,0.3)",isEditing:!1,editable:!0,editingBorderColor:"rgba(102,153,255,0.25)",cursorWidth:2,cursorColor:"#333",cursorDelay:1e3,cursorDuration:600,styles:null,skipFillStrokeCheck:!0,_reNewline:/\r?\n/,_reSpace:/\s|\n/,_fontSizeFraction:4,_currentCursorOpacity:0,_selectionDirection:null,_abortCursorAnimation:!1,initialize:function(e,t){this.styles=t.styles||{},this.callSuper("initialize",e,t),this.initBehavior()},isEmptyStyles:function(){if(!this.styles)return!0;var e=this.styles;for(var t in e)for(var n in e[t])for(var r in e[t][n])return!1;return!0},setSelectionStart:function(e){this.selectionStart=e,this.hiddenTextarea&&(this.hiddenTextarea.selectionStart=e)},setSelectionEnd:function(e){this.selectionEnd=e,this.hiddenTextarea&&(this.hiddenTextarea.selectionEnd=e)},getSelectionStyles:function(){var e=this.get2DCursorLocation();return this.styles[e.lineIndex]?this.styles[e.lineIndex][e.charIndex]||{}:{}},setSelectionStyles:function(e){if(this.selectionStart===this.selectionEnd)this._extendStyles(this.selectionStart,e);else for(var t=this.selectionStart;t-1&&this._renderCharDecorationAtOffset(e,n,r+this.fontSize/this._fontSizeFraction,i,0),o.indexOf("line-through")>-1&&this._renderCharDecorationAtOffset(e,n,r+this.fontSize/this._fontSizeFraction,i,s/this._fontSizeFraction),o.indexOf("overline")>-1&&this._renderCharDecorationAtOffset(e,n,r,i,s-this.fontSize/this._fontSizeFraction)},_renderCharDecorationAtOffset:function(e,t,n,r,i){e.fillRect(t,n-i,r,1)},_renderTextLine:function(e,t,n,r,i,s){i+=this.fontSize/4,this.callSuper("_renderTextLine",e,t,n,r,i,s)},_renderTextDecoration:function(e,t){if(this.isEmptyStyles())return this.callSuper("_renderTextDecoration",e,t)},_renderTextLinesBackground:function(e,t){if(!this.textBackgroundColor&&!this.styles)return;e.save(),this.textBackgroundColor&&(e.fillStyle=this.textBackgroundColor);var n=0,r=this.fontSize/this._fontSizeFraction;for(var i=0,s=t.length;in&&(n=s)}return n},_getHeightOfLine:function(e,t,n){n=n||this.text.split(this._reNewline);var r=this._getHeightOfChar(e,n[t][0],t,0),i=n[t],s=i.split("");for(var o=1,u=s.length;or&&(r=a)}return r*this.lineHeight},_getTextHeight:function(e,t){var n=0;for(var r=0,i=t.length;ri.length?(i+r).length+1:(n+r).length+1},moveCursorDown:function(e){this.abortCursorAnimation(),this._currentCursorOpacity=1;var t=this.getSelectionStartOffset();e.metaKey&&(t=this.text.length-this.selectionStart),e.shiftKey?this.moveCursorDownWithShift(t):this.moveCursorDownWithoutShift(t),this.initDelayedCursor()},moveCursorDownWithoutShift:function(e){this._selectionDirection="right",this.selectionStart+=e,this.selectionStart>this.text.length&&(this.selectionStart=this.text.length),this.selectionEnd=this.selectionStart},moveCursorDownWithShift:function(e){this._selectionDirection==="left"&&this.selectionStart!==this.selectionEnd?(this.selectionStart=this.selectionEnd,this._selectionDirection="right"):(this._selectionDirection="right",this.selectionEnd+=e,this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length))},moveCursorUp:function(e){var t=this.text.slice(0,this.selectionStart);this.abortCursorAnimation(),this._currentCursorOpacity=1;var n=t.slice(t.lastIndexOf("\n")+1),r=(t.match(/\n?(.*)\n.*$/)||{})[1]||"",i;n.length>r.length?i=n.length+1:i=r.length+1,e.metaKey&&(i=this.selectionStart),e.shiftKey?this.moveCursorUpWithShift(i):this.moveCursorUpWithoutShift(i),this.initDelayedCursor()},moveCursorUpWithShift:function(e){this.selectionStart===this.selectionEnd?this.selectionStart-=e:this._selectionDirection==="right"?this.selectionEnd=this.selectionStart:this.selectionStart-=e,this.selectionStart<0&&(this.selectionStart=0),this._selectionDirection="left"},moveCursorUpWithoutShift:function(e){this.selectionStart===this.selectionEnd&&(this.selectionStart-=e),this.selectionStart<0&&(this.selectionStart=0),this.selectionEnd=this.selectionStart,this._selectionDirection="left"},moveCursorLeft:function(e){if(this.selectionStart===0&&this.selectionEnd===0)return;this.abortCursorAnimation(),this._currentCursorOpacity=1,e.shiftKey?this.moveCursorLeftWithShift(e):this.moveCursorLeftWithoutShift(e),this.initDelayedCursor()},findWordBoundaryLeft:function(e){var t=0,n=e-1;if(this._reSpace.test(this.text.charAt(n)))while(this._reSpace.test(this.text.charAt(n)))t++,n--;while(/\S/.test(this.text.charAt(n))&&n>-1)t++,n--;return e-t},findWordBoundaryRight:function(e){var t=0,n=e;if(this._reSpace.test(this.text.charAt(n)))while(this._reSpace.test(this.text.charAt(n)))t++,n++;while(/\S/.test(this.text.charAt(n))&&n-1)t++,n--;return e-t},findLineBoundaryRight:function(e){var t=0,n=e;while(!/\n/.test(this.text.charAt(n))&&n=this.text.length&&this.selectionEnd>=this.text.length)return;this.abortCursorAnimation(),this._currentCursorOpacity=1,e.shiftKey?this.moveCursorRightWithShift(e):this.moveCursorRightWithoutShift(e),this.initDelayedCursor()},moveCursorRightWithShift:function(e){this._selectionDirection==="left"&&this.selectionStart!==this.selectionEnd?this._moveRight(e,"selectionStart"):(this._selectionDirection="right",this._moveRight(e,"selectionEnd"),this.text.charAt(this.selectionEnd-1)==="\n"&&this.selectionEnd++,this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length))},moveCursorRightWithoutShift:function(e){this._selectionDirection="right",this.selectionStart===this.selectionEnd?(this._moveRight(e,"selectionStart"),this.selectionEnd=this.selectionStart):(this.selectionEnd+=this.getNumNewLinesInSelectedText(),this.selectionEnd>this.text.length&&(this.selectionEnd=this.text.length),this.selectionStart=this.selectionEnd)},getNumNewLinesInSelectedText:function(){var e=this.text.slice(this.selectionStart,this.selectionEnd),t=0;for(var n=0,r=e.split(""),i=r.length;n0&&nr&&o>n){var g=n-s,y=o-n;return y>g?f=a+l:f=a+l+1,f>this.text.length&&(f=this.text.length),f}a++}}if(typeof f=="undefined")return this.text.length},enterEditing:function(){if(this.isEditing||!this.editable)return;return this.isEditing=!0,this.hiddenTextarea&&(this.hiddenTextarea.value=this.text,this.hiddenTextarea.selectionStart=this.selectionStart,this.hiddenTextarea.focus()),this._saveProps(),this.hoverCursor="text",this.canvas.defaultCursor="text",this.canvas.moveCursor="text",this.hasControls=!1,this.borderColor=this.editingBorderColor,this.selectable=!1,this.lockMovementX=!0,this.lockMovementY=!0,this._tick(),this.canvas.renderAll(),this},_saveProps:function(){this._savedProps={hasControls:this.hasControls,borderColor:this.borderColor,lockMovementX:this.lockMovementX,lockMovementY:this.lockMovementY,hoverCursor:this.hoverCursor,defaultCursor:this.canvas.defaultCursor,moveCursor:this.canvas.moveCursor}},_restoreProps:function(){if(!this._savedProps)return;this.hoverCursor=this._savedProps.overCursor,this.canvas.defaultCursor=this._savedProps.defaultCursor,this.canvas.moveCursor=this._savedProps.moveCursor,this.hasControls=this._savedProps.hasControls,this.borderColor=this._savedProps.borderColor,this.lockMovementX=this._savedProps.lockMovementX,this.lockMovementY=this._savedProps.lockMovementY},exitEditing:function(){return this.selected=!1,this.isEditing=!1,this.selectable=!0,this.hiddenTextarea&&this.hiddenTextarea.blur(),this.abortCursorAnimation(),this._restoreProps(),this._currentCursorOpacity=0,this},removeChars:function(e){if(this.selectionStart===this.selectionEnd){if(this.selectionStart!==0)if(e.metaKey){var t=this.findLineBoundaryLeft(this.selectionStart);this._removeCharsFromTo(t,this.selectionStart),this.selectionStart=t}else if(e.altKey){var n=this.findWordBoundaryLeft(this.selectionStart);this._removeCharsFromTo(n,this.selectionStart),this.selectionStart=n}else{var r=this.text.slice(this.selectionStart-1,this.selectionStart)==="\n";this.removeStyleObject(r),this.selectionStart--,this.text=this.text.slice(0,this.selectionStart)+this.text.slice(this.selectionStart+1)}}else this._removeCharsFromTo(this.selectionStart,this.selectionEnd);this.selectionEnd=this.selectionStart;var i=this.text.split(this._reNewline);for(var s in this.styles)i[s]||delete this.styles[s];this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("text:changed")},_removeCharsFromTo:function(e,t){var n=t;while(n!==e)n--,this.removeStyleObject(!1,n);this.text=this.text.slice(0,e)+this.text.slice(t)},insertChar:function(e){var t=this.text.slice(this.selectionStart,this.selectionStart+1)==="\n";this.text=this.text.slice(0,this.selectionStart)+e+this.text.slice(this.selectionEnd),this.selectionStart===this.selectionEnd?this.insertStyleObject(e,t):this.selectionEnd-this.selectionStart>1,this.selectionStart++,this.selectionEnd=this.selectionStart,this.canvas&&this.canvas.renderAll().renderAll(),this.setCoords(),this.fire("text:changed")},insertNewlineStyleObject:function(t,n,r){this.shiftLineStyles(t,1),this.styles[t+1]||(this.styles[t+1]={});var i=this.styles[t][n-1],s={};if(r)s[0]=e(i),this.styles[t+1]=s;else{for(var o in this.styles[t])parseInt(o,10)>=n&&(s[parseInt(o,10)-n]=this.styles[t][o],delete this.styles[t][o]);this.styles[t+1]=s}},insertCharStyleObject:function(t,n){var r=this.styles[t],i=e(r);n===0&&(n=1);for(var s in i){var o=parseInt(s,10);o>=n&&(r[o+1]=i[o])}this.styles[t][n]=e(r[n-1])},insertStyleObject:function(e,t){if(this.isEmptyStyles())return;var n=this.get2DCursorLocation(),r=n.lineIndex,i=n.charIndex;this.styles[r]||(this.styles[r]={}),e==="\n"?this.insertNewlineStyleObject(r,i,t):this.insertCharStyleObject(r,i)},shiftLineStyles:function(t,n){var r=e(this.styles);for(var i in this.styles){var s=parseInt(i,10);s>t&&(this.styles[s+n]=r[s])}},removeStyleObject:function(t,n){var r=this.get2DCursorLocation(n),i=r.lineIndex,s=r.charIndex;if(t){var o=this.text.split(this._reNewline),u=o[i-1],a=u.length;this.styles[i-1]||(this.styles[i-1]={});for(s in this.styles[i])this.styles[i-1][parseInt(s,10)+a]=this.styles[i][s];this.shiftLineStyles(i,-1)}else{var f=this.styles[i];if(f){var l=this.selectionStart===this.selectionEnd?-1:0;delete f[s+l]}var c=e(f);for(var h in c){var p=parseInt(h,10);p>=s&&p!==0&&(f[p-1]=c[p],delete f[p])}}},insertNewline:function(){this.insertChar("\n")}})}(),function(){function request(e,t,n){var r=URL.parse(e);r.port||(r.port=r.protocol.indexOf("https:")===0?443:80);var i=r.port===443?HTTPS:HTTP,s=i.request({hostname:r.hostname,port:r.port,path:r.path,method:"GET"},function(e){var r="";t&&e.setEncoding(t),e.on("end",function(){n(r)}),e.on("data",function(t){e.statusCode===200&&(r+=t)})});s.on("error",function(e){e.errno===process.ECONNREFUSED?fabric.log("ECONNREFUSED: connection refused to "+r.hostname+":"+r.port):fabric.log(e.message)}),s.end()}function request_fs(e,t){var n=require("fs");n.readFile(e,function(e,n){if(e)throw fabric.log(e),e;t(n)})}if(typeof document!="undefined"&&typeof window!="undefined")return;var DOMParser=(new require("xmldom")).DOMParser,URL=require("url"),HTTP=require("http"),HTTPS=require("https"),Canvas=require("canvas"),Image=require("canvas").Image;fabric.util.loadImage=function(e,t,n){var r=function(r){i.src=new Buffer(r,"binary"),i._src=e,t&&t.call(n,i)},i=new Image;e&&(e instanceof Buffer||e.indexOf("data")===0)?(i.src=i._src=e,t&&t.call(n,i)):e&&e.indexOf("http")!==0?request_fs(e,r):e&&request(e,"binary",r)},fabric.loadSVGFromURL=function(e,t,n){e=e.replace(/^\n\s*/,"").replace(/\?.*$/,"").trim(),e.indexOf("http")!==0?request_fs(e,function(e){fabric.loadSVGFromString(e,t,n)}):request(e,"",function(e){fabric.loadSVGFromString(e,t,n)})},fabric.loadSVGFromString=function(e,t,n){var r=(new DOMParser).parseFromString(e);fabric.parseSVGDocument(r.documentElement,function(e,n){t&&t(e,n)},n)},fabric.util.getScript=function(url,callback){request(url,"",function(body){eval(body),callback&&callback()})},fabric.Image.fromObject=function(e,t){fabric.util.loadImage(e.src,function(n){var r=new fabric.Image(n);r._initConfig(e),r._initFilters(e,function(e){r.filters=e||[],t&&t(r)})})},fabric.createCanvasForNode=function(e,t){var n=fabric.document.createElement("canvas"),r=new Canvas(e||600,t||600);n.style={},n.width=r.width,n.height=r.height;var i=fabric.Canvas||fabric.StaticCanvas,s=new i(n);return s.contextContainer=r.getContext("2d"),s.nodeCanvas=r,s.Font=Canvas.Font,s},fabric.StaticCanvas.prototype.createPNGStream=function(){return this.nodeCanvas.createPNGStream()},fabric.StaticCanvas.prototype.createJPEGStream=function(e){return this.nodeCanvas.createJPEGStream(e)};var origSetWidth=fabric.StaticCanvas.prototype.setWidth;fabric.StaticCanvas.prototype.setWidth=function(e){return origSetWidth.call(this,e),this.nodeCanvas.width=e,this},fabric.Canvas&&(fabric.Canvas.prototype.setWidth=fabric.StaticCanvas.prototype.setWidth);var origSetHeight=fabric.StaticCanvas.prototype.setHeight;fabric.StaticCanvas.prototype.setHeight=function(e){return origSetHeight.call(this,e),this.nodeCanvas.height=e,this},fabric.Canvas&&(fabric.Canvas.prototype.setHeight=fabric.StaticCanvas.prototype.setHeight)}(); \ No newline at end of file diff --git a/dist/all.min.js.gz b/dist/all.min.js.gz index 7fcbe0c3bb2d5a5db7f74a96db4841e7aabdbe92..250ab66e0917999dc8dbde27728daa7dc70b27bc 100644 GIT binary patch delta 31018 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 delta 31046 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 RP7EgR;=hD5Fr8aK0|443f_VS{ diff --git a/dist/all.require.js b/dist/all.require.js index ad7d5cce..bbafb70a 100644 --- a/dist/all.require.js +++ b/dist/all.require.js @@ -19337,7 +19337,912 @@ fabric.util.object.extend(fabric.Text.prototype, { var clone = fabric.util.object.clone; - fabric.ITextBehavior = { /** @lends fabric.IText.prototype */ + /** + * IText class (introduced in v1.4) + * @class fabric.IText + * @extends fabric.Text + * @mixes fabric.Observable + * @fires text:changed + * @return {fabric.IText} thisArg + * @see {@link fabric.IText#initialize} for constructor definition + * + *

Supported key combinations:

+ *
+    *   Move cursor:                    left, right, up, down
+    *   Select character:               shift + left, shift + right
+    *   Select text vertically:         shift + up, shift + down
+    *   Move cursor by word:            alt + left, alt + right
+    *   Select words:                   shift + alt + left, shift + alt + right
+    *   Move cursor to line start/end:  cmd + left, cmd + right
+    *   Select till start/end of line:  cmd + shift + left, cmd + shift + right
+    *   Jump to start/end of text:      cmd + up, cmd + down
+    *   Select till start/end of text:  cmd + shift + up, cmd + shift + down
+    *   Delete character:               backspace
+    *   Delete word:                    alt + backspace
+    *   Delete line:                    cmd + backspace
+    *   Forward delete:                 delete
+    * 
+ */ + fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, /** @lends fabric.IText.prototype */ { + + /** + * Type of an object + * @type String + * @default + */ + type: 'i-text', + + /** + * Index where text selection starts (or where cursor is when there is no selection) + * @type Nubmer + * @default + */ + selectionStart: 0, + + /** + * Index where text selection ends + * @type Nubmer + * @default + */ + selectionEnd: 0, + + /** + * Color of text selection + * @type String + * @default + */ + selectionColor: 'rgba(17,119,255,0.3)', + + /** + * Indicates whether text is in editing mode + * @type Boolean + * @default + */ + isEditing: false, + + /** + * Indicates whether a text can be edited + * @type Boolean + * @default + */ + editable: true, + + /** + * Border color of text object while it's in editing mode + * @type String + * @default + */ + editingBorderColor: 'rgba(102,153,255,0.25)', + + /** + * Width of cursor (in px) + * @type Number + * @default + */ + cursorWidth: 2, + + /** + * Color of default cursor (when not overwritten by character style) + * @type String + * @default + */ + cursorColor: '#333', + + /** + * Delay between cursor blink (in ms) + * @type Number + * @default + */ + cursorDelay: 1000, + + /** + * Duration of cursor fadein (in ms) + * @type Number + * @default + */ + cursorDuration: 600, + + /** + * Object containing character styles + * (where top-level properties corresponds to line number and 2nd-level properties -- to char number in a line) + * @type Object + * @default + */ + styles: null, + + skipFillStrokeCheck: true, + + /** + * @private + */ + _reNewline: /\r?\n/, + + /** + * @private + */ + _reSpace: /\s|\n/, + + /** + * @private + */ + _fontSizeFraction: 4, + + /** + * @private + */ + _currentCursorOpacity: 0, + + /** + * @private + */ + _selectionDirection: null, + + /** + * @private + */ + _abortCursorAnimation: false, + + /** + * Constructor + * @param {String} text Text string + * @param {Object} [options] Options object + * @return {fabric.IText} thisArg + */ + initialize: function(text, options) { + this.styles = options.styles || { }; + this.callSuper('initialize', text, options); + this.initBehavior(); + }, + + /** + * Returns true if object has no styling + */ + isEmptyStyles: function() { + if (!this.styles) return true; + var obj = this.styles; + + for (var p1 in obj) { + for (var p2 in obj[p1]) { + /*jshint unused:false */ + for (var p3 in obj[p1][p2]) { + return false; + } + } + } + return true; + }, + + /** + * Sets selection start (left boundary of a selection) + * @param {Number} index Index to set selection start to + */ + setSelectionStart: function(index) { + this.selectionStart = index; + this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index); + }, + + /** + * Sets selection end (right boundary of a selection) + * @param {Number} index Index to set selection end to + */ + setSelectionEnd: function(index) { + this.selectionEnd = index; + this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index); + }, + + /** + * Gets style of a current selection/cursor (at the start position) + * @return {Object} styles Style object at a cursor position + */ + getSelectionStyles: function() { + var loc = this.get2DCursorLocation(); + if (this.styles[loc.lineIndex]) { + return this.styles[loc.lineIndex][loc.charIndex] || { }; + } + return { }; + }, + + /** + * Sets style of a current selection + * @param {Object} [styles] Styles object + * @return {fabric.IText} thisArg + * @chainable + */ + setSelectionStyles: function(styles) { + if (this.selectionStart === this.selectionEnd) { + this._extendStyles(this.selectionStart, styles); + } + else { + for (var i = this.selectionStart; i < this.selectionEnd; i++) { + this._extendStyles(i, styles); + } + } + return this; + }, + + /** + * @private + */ + _extendStyles: function(index, styles) { + var loc = this.get2DCursorLocation(index); + + if (!this.styles[loc.lineIndex]) { + this.styles[loc.lineIndex] = { }; + } + if (!this.styles[loc.lineIndex][loc.charIndex]) { + this.styles[loc.lineIndex][loc.charIndex] = { }; + } + + fabric.util.object.extend(this.styles[loc.lineIndex][loc.charIndex], styles); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _render: function(ctx) { + this.callSuper('_render', ctx); + this.isEditing && this.renderCursorOrSelection(ctx); + this.ctx = ctx; + }, + + /** + * Renders cursor or selection (depending on what exists) + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + renderCursorOrSelection: function(ctx) { + if (!this.active) return; + + var chars = this.text.split(''), + boundaries; + + if (this.selectionStart === this.selectionEnd) { + boundaries = this.getCursorBoundaries(ctx, chars, 'cursor'); + this.renderCursor(ctx, boundaries); + } + else { + boundaries = this.getCursorBoundaries(ctx, chars, 'selection'); + this.renderSelection(ctx, chars, boundaries); + } + }, + + /** + * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start) + * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used. + */ + get2DCursorLocation: function(selectionStart) { + if (typeof selectionStart === 'undefined') { + selectionStart = this.selectionStart; + } + var textBeforeCursor = this.text.slice(0, selectionStart); + var linesBeforeCursor = textBeforeCursor.split(this._reNewline); + + return { + lineIndex: linesBeforeCursor.length - 1, + charIndex: linesBeforeCursor[linesBeforeCursor.length - 1].length + }; + }, + + /** + * Returns fontSize of char at the current cursor + * @param {Number} lineIndex Line index + * @param {Number} charIndex Char index + * @return {Number} Character font size + */ + getCurrentCharFontSize: function(lineIndex, charIndex) { + return ( + this.styles[lineIndex] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fontSize) || this.fontSize; + }, + + /** + * Returns color (fill) of char at the current cursor + * @param {Number} lineIndex Line index + * @param {Number} charIndex Char index + * @return {String} Character color (fill) + */ + getCurrentCharColor: function(lineIndex, charIndex) { + return ( + this.styles[lineIndex] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && + this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fill) || this.cursorColor; + }, + + /** + * Returns cursor boundaries (left, top, leftOffset, topOffset) + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} chars Array of characters + */ + getCursorBoundaries: function(ctx, chars, typeOfBoundaries) { + + var cursorLocation = this.get2DCursorLocation(); + var lineIndex = cursorLocation.lineIndex; + var charIndex = cursorLocation.charIndex; + + var textLines = this.text.split(this._reNewline); + + var widthOfLine; + var lineLeftOffset; + + // left/top are left/top of entire text box + // leftOffset/topOffset are offset from that left/top point of a text box + var left = Math.round(this._getLeftOffset()); + var top = -this.height / 2; + + var leftOffset = 0; + var topOffset = typeOfBoundaries === 'cursor' + // selection starts at the very top of the line, + // whereas cursor starts at the padding created by line height + ? (this._getHeightOfLine(ctx, 0) - this.getCurrentCharFontSize(lineIndex, charIndex)) + : 0; + + lineIndex = 0; + charIndex = 0; + + for (var i = 0; i < this.selectionStart; i++) { + if (chars[i] === '\n') { + leftOffset = 0; + topOffset += this._getHeightOfLine(ctx, lineIndex + (typeOfBoundaries === 'cursor' ? 1 : 0)); + + lineIndex++; + charIndex = 0; + } + else { + leftOffset += this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); + charIndex++; + } + + widthOfLine = this._getWidthOfLine(ctx, lineIndex, textLines); + lineLeftOffset = this._getLineLeftOffset(widthOfLine); + } + + return { + left: left, + top: top, + leftOffset: leftOffset + (lineLeftOffset || 0), + topOffset: topOffset + }; + }, + + /** + * Renders cursor + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + renderCursor: function(ctx, boundaries) { + ctx.save(); + + var cursorLocation = this.get2DCursorLocation(); + var lineIndex = cursorLocation.lineIndex; + var charIndex = cursorLocation.charIndex; + + ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex); + ctx.globalAlpha = this._currentCursorOpacity; + + var charHeight = this.getCurrentCharFontSize(lineIndex, charIndex); + + ctx.fillRect( + boundaries.left + boundaries.leftOffset, + boundaries.top + boundaries.topOffset, + this.cursorWidth, + charHeight); + + ctx.restore(); + }, + + /** + * Renders text selection + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} chars Array of characters + * @param {Object} boundaries Object with left/top/leftOffset/topOffset + */ + renderSelection: function(ctx, chars, boundaries) { + ctx.save(); + + ctx.fillStyle = this.selectionColor; + + var cursorLocation = this.get2DCursorLocation(); + var lineIndex = cursorLocation.lineIndex; + var charIndex = cursorLocation.charIndex; + var textLines = this.text.split(this._reNewline); + var origLineIndex = lineIndex; + + for (var i = this.selectionStart; i < this.selectionEnd; i++) { + + if (chars[i] === '\n') { + boundaries.leftOffset = 0; + boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex); + lineIndex++; + charIndex = 0; + } + else if (i !== this.text.length) { + + var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); + var lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0; + + if (lineIndex === origLineIndex) { + // only offset the line if we're rendering selection of 2nd, 3rd, etc. line + lineOffset = 0; + } + + ctx.fillRect( + boundaries.left + boundaries.leftOffset + lineOffset, + boundaries.top + boundaries.topOffset, + charWidth, + this._getHeightOfLine(ctx, lineIndex)); + + boundaries.leftOffset += charWidth; + charIndex++; + } + } + ctx.restore(); + }, + + /** + * @private + * @param {String} method + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderChars: function(method, ctx, line, left, top, lineIndex) { + + if (this.isEmptyStyles()) { + return this._renderCharsFast(method, ctx, line, left, top); + } + + this.skipTextAlign = true; + + // set proper box offset + left -= this.textAlign === 'center' + ? (this.width / 2) + : (this.textAlign === 'right') + ? this.width + : 0; + + // set proper line offset + var textLines = this.text.split(this._reNewline); + var lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines); + var lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines); + var lineLeftOffset = this._getLineLeftOffset(lineWidth); + var chars = line.split(''); + + left += lineLeftOffset || 0; + + ctx.save(); + for (var i = 0, len = chars.length; i < len; i++) { + this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight); + } + ctx.restore(); + }, + + /** + * @private + * @param {String} method + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {String} line + */ + _renderCharsFast: function(method, ctx, line, left, top) { + this.skipTextAlign = false; + + if (method === 'fillText' && this.fill) { + this.callSuper('_renderChars', method, ctx, line, left, top); + } + if (method === 'strokeText' && this.stroke) { + this.callSuper('_renderChars', method, ctx, line, left, top); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderChar: function(method, ctx, lineIndex, i, _char, left, top, lineHeight) { + var decl, charWidth; + + if (this.styles && this.styles[lineIndex] && (decl = this.styles[lineIndex][i])) { + + var shouldStroke = decl.stroke || this.stroke; + var shouldFill = decl.fill || this.fill; + + ctx.save(); + charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i, decl); + + if (shouldFill) { + ctx.fillText(_char, left, top); + } + if (shouldStroke) { + ctx.strokeText(_char, left, top); + } + + this._renderCharDecoration(ctx, decl, left, top, charWidth, lineHeight); + ctx.restore(); + + ctx.translate(charWidth, 0); + } + else { + if (method === 'strokeText' && this.stroke) { + ctx[method](_char, left, top); + } + if (method === 'fillText' && this.fill) { + ctx[method](_char, left, top); + } + charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i); + this._renderCharDecoration(ctx, null, left, top, charWidth, lineHeight); + + ctx.translate(ctx.measureText(_char).width, 0); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight) { + var textDecoration = styleDeclaration + ? (styleDeclaration.textDecoration || this.textDecoration) + : this.textDecoration; + + if (!textDecoration) return; + + if (textDecoration.indexOf('underline') > -1) { + + this._renderCharDecorationAtOffset( + ctx, + left, + top + (this.fontSize / this._fontSizeFraction), + charWidth, + 0 + ); + } + if (textDecoration.indexOf('line-through') > -1) { + this._renderCharDecorationAtOffset( + ctx, + left, + top + (this.fontSize / this._fontSizeFraction), + charWidth, + (lineHeight / this._fontSizeFraction) + ); + } + if (textDecoration.indexOf('overline') > -1) { + this._renderCharDecorationAtOffset( + ctx, + left, + top, + charWidth, + lineHeight - (this.fontSize / this._fontSizeFraction) + ); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _renderCharDecorationAtOffset: function(ctx, left, top, charWidth, offset) { + ctx.fillRect(left, top - offset, charWidth, 1); + }, + + /** + * @private + * @param {String} method + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {String} line + */ + _renderTextLine: function(method, ctx, line, left, top, lineIndex) { + // to "cancel" this.fontSize subtraction in fabric.Text#_renderTextLine + top += this.fontSize / 4; + this.callSuper('_renderTextLine', method, ctx, line, left, top, lineIndex); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} textLines + */ + _renderTextDecoration: function(ctx, textLines) { + if (this.isEmptyStyles()) { + return this.callSuper('_renderTextDecoration', ctx, textLines); + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {Array} textLines Array of all text lines + */ + _renderTextLinesBackground: function(ctx, textLines) { + if (!this.textBackgroundColor && !this.styles) return; + + ctx.save(); + + if (this.textBackgroundColor) { + ctx.fillStyle = this.textBackgroundColor; + } + + var lineHeights = 0; + var fractionOfFontSize = this.fontSize / this._fontSizeFraction; + + for (var i = 0, len = textLines.length; i < len; i++) { + + var heightOfLine = this._getHeightOfLine(ctx, i, textLines); + if (textLines[i] === '') { + lineHeights += heightOfLine; + continue; + } + + var lineWidth = this._getWidthOfLine(ctx, i, textLines); + var lineLeftOffset = this._getLineLeftOffset(lineWidth); + + if (this.textBackgroundColor) { + ctx.fillStyle = this.textBackgroundColor; + + ctx.fillRect( + this._getLeftOffset() + lineLeftOffset, + this._getTopOffset() + lineHeights + fractionOfFontSize, + lineWidth, + heightOfLine + ); + } + if (this.styles[i]) { + for (var j = 0, jlen = textLines[i].length; j < jlen; j++) { + if (this.styles[i] && this.styles[i][j] && this.styles[i][j].textBackgroundColor) { + + var _char = textLines[i][j]; + + ctx.fillStyle = this.styles[i][j].textBackgroundColor; + + ctx.fillRect( + this._getLeftOffset() + lineLeftOffset + this._getWidthOfCharsAt(ctx, i, j, textLines), + this._getTopOffset() + lineHeights + fractionOfFontSize, + this._getWidthOfChar(ctx, _char, i, j, textLines) + 1, + heightOfLine + ); + } + } + } + lineHeights += heightOfLine; + } + ctx.restore(); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + * @param {String} _char + * @param {Number} lineIndex + * @param {Number} charIndex + * @param {Object} [decl] + */ + _applyCharStylesGetWidth: function(ctx, _char, lineIndex, charIndex, decl) { + var styleDeclaration = decl || (this.styles[lineIndex] && this.styles[lineIndex][charIndex]); + + if (styleDeclaration) { + // cloning so that original style object is not polluted with following font declarations + styleDeclaration = clone(styleDeclaration); + } + else { + styleDeclaration = { }; + } + + var fill = styleDeclaration.fill || this.fill; + ctx.fillStyle = fill.toLive + ? fill.toLive(ctx) + : fill; + + if (styleDeclaration.stroke) { + ctx.strokeStyle = (styleDeclaration.stroke && styleDeclaration.stroke.toLive) + ? styleDeclaration.stroke.toLive(ctx) + : styleDeclaration.stroke; + } + + ctx.lineWidth = styleDeclaration.strokeWidth || this.strokeWidth; + + this._applyFontStyles(styleDeclaration); + + if (typeof styleDeclaration.shadow === 'string') { + styleDeclaration.shadow = new fabric.Shadow(styleDeclaration.shadow); + } + + this._setShadow.call(styleDeclaration, ctx); + + ctx.font = this._getFontDeclaration.call(styleDeclaration); + + return ctx.measureText(_char).width; + }, + + /** + * @private + * @param {Object} styleDeclaration + */ + _applyFontStyles: function(styleDeclaration) { + if (!styleDeclaration.fontFamily) { + styleDeclaration.fontFamily = this.fontFamily; + } + if (!styleDeclaration.fontSize) { + styleDeclaration.fontSize = this.fontSize; + } + if (!styleDeclaration.fontWeight) { + styleDeclaration.fontWeight = this.fontWeight; + } + if (!styleDeclaration.fontStyle) { + styleDeclaration.fontStyle = this.fontStyle; + } + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfChar: function(ctx, _char, lineIndex, charIndex) { + ctx.save(); + var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex); + ctx.restore(); + return width; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getHeightOfChar: function(ctx, _char, lineIndex, charIndex) { + if (this.styles[lineIndex] && this.styles[lineIndex][charIndex]) { + return this.styles[lineIndex][charIndex].fontSize || this.fontSize; + } + return this.fontSize; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfCharAt: function(ctx, lineIndex, charIndex, lines) { + lines = lines || this.text.split(this._reNewline); + var _char = lines[lineIndex].split('')[charIndex]; + return this._getWidthOfChar(ctx, _char, lineIndex, charIndex); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getHeightOfCharAt: function(ctx, lineIndex, charIndex, lines) { + lines = lines || this.text.split(this._reNewline); + var _char = lines[lineIndex].split('')[charIndex]; + return this._getHeightOfChar(ctx, _char, lineIndex, charIndex); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfCharsAt: function(ctx, lineIndex, charIndex, lines) { + var width = 0; + for (var i = 0; i < charIndex; i++) { + width += this._getWidthOfCharAt(ctx, lineIndex, i, lines); + } + return width; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getWidthOfLine: function(ctx, lineIndex, textLines) { + // if (!this.styles[lineIndex]) { + // return this.callSuper('_getLineWidth', ctx, textLines[lineIndex]); + // } + return this._getWidthOfCharsAt(ctx, lineIndex, textLines[lineIndex].length, textLines); + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getTextWidth: function(ctx, textLines) { + + if (this.isEmptyStyles()) { + return this.callSuper('_getTextWidth', ctx, textLines); + } + + var maxWidth = this._getWidthOfLine(ctx, 0, textLines); + + for (var i = 1, len = textLines.length; i < len; i++) { + var currentLineWidth = this._getWidthOfLine(ctx, i, textLines); + if (currentLineWidth > maxWidth) { + maxWidth = currentLineWidth; + } + } + return maxWidth; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getHeightOfLine: function(ctx, lineIndex, textLines) { + + textLines = textLines || this.text.split(this._reNewline); + + var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0); + + var line = textLines[lineIndex]; + var chars = line.split(''); + + for (var i = 1, len = chars.length; i < len; i++) { + var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i); + if (currentCharHeight > maxHeight) { + maxHeight = currentCharHeight; + } + } + + return maxHeight * this.lineHeight; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getTextHeight: function(ctx, textLines) { + var height = 0; + for (var i = 0, len = textLines.length; i < len; i++) { + height += this._getHeightOfLine(ctx, i, textLines); + } + return height; + }, + + /** + * @private + * @param {CanvasRenderingContext2D} ctx Context to render on + */ + _getTopOffset: function() { + var topOffset = fabric.Text.prototype._getTopOffset.call(this); + return topOffset - (this.fontSize / this._fontSizeFraction); + }, + + /** + * Returns object representation of an instance + * @methd toObject + * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output + * @return {Object} object representation of an instance + */ + toObject: function(propertiesToInclude) { + return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + styles: clone(this.styles) + }); + }, + + /* _TO_SVG_START_ */ + /** + * Returns SVG representation of an instance + * @return {String} svg representation of an instance + */ + toSVG: function(reviver) { + if (this.isEmptyStyles()) { + return this.callSuper('toSVG', reviver); + } + // TODO: add support for styled text SVG output + } + /* _TO_SVG_END_ */ + }); + + /** + * Returns fabric.IText instance from an object representation + * @static + * @memberOf fabric.IText + * @param {Object} object Object to create an instance from + * @return {fabric.IText} instance of fabric.IText + */ + fabric.IText.fromObject = function(object) { + return new fabric.IText(object.text, clone(object)); + }; + +})(); + + +(function() { + + var clone = fabric.util.object.clone; + + fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ { /** * Initializes all the interactive behavior of IText @@ -20497,914 +21402,12 @@ fabric.util.object.extend(fabric.Text.prototype, { insertNewline: function() { this.insertChar('\n'); } - }; -})(); - - -(function() { - - var clone = fabric.util.object.clone; - - /** - * IText class - * @class fabric.IText - * @extends fabric.Text - * @mixes fabric.Observable - * @fires #text:changed - * @return {fabric.IText} thisArg - * @see {@link fabric.IText#initialize} for constructor definition - * - * Supported key combinations: - * - * Move cursor: left, right, up, down - * Select character: shift + left, shift + right - * Select text vertically: shift + up, shift + down - * Move cursor by word: alt + left, alt + right - * Select words: shift + alt + left, shift + alt + right - * Move cursor to line start/end: cmd + left, cmd + right - * Select till start/end of line: cmd + shift + left, cmd + shift + right - * Jump to start/end of text: cmd + up, cmd + down - * Select till start/end of text: cmd + shift + up, cmd + shift + down - * Delete character: backspace - * Delete word: alt + backspace - * Delete line: cmd + backspace - * Forward delete: delete - */ - fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, fabric.ITextBehavior, { - - /** - * Type of an object - * @type String - * @default - */ - type: 'i-text', - - /** - * Index where text selection starts (or where cursor is when there is no selection) - * @type Nubmer - * @default - */ - selectionStart: 0, - - /** - * Index where text selection ends - * @type Nubmer - * @default - */ - selectionEnd: 0, - - /** - * Color of text selection - * @type String - * @default - */ - selectionColor: 'rgba(17,119,255,0.3)', - - /** - * Indicates whether text is in editing mode - * @type Boolean - * @default - */ - isEditing: false, - - /** - * Indicates whether a text can be edited - * @type Boolean - * @default - */ - editable: true, - - /** - * Border color of text object while it's in editing mode - * @type String - * @default - */ - editingBorderColor: 'rgba(102,153,255,0.25)', - - /** - * Width of cursor (in px) - * @type Number - * @default - */ - cursorWidth: 2, - - /** - * Color of default cursor (when not overwritten by character style) - * @type String - * @default - */ - cursorColor: '#333', - - /** - * Delay between cursor blink (in ms) - * @type Number - * @default - */ - cursorDelay: 1000, - - /** - * Duration of cursor fadein (in ms) - * @type Number - * @default - */ - cursorDuration: 600, - - /** - * Object containing character styles - * (where top-level properties corresponds to line number and 2nd-level properties -- to char number in a line) - * @type Object - * @default - */ - styles: null, - - skipFillStrokeCheck: true, - - /** - * @private - */ - _reNewline: /\r?\n/, - - /** - * @private - */ - _reSpace: /\s|\n/, - - /** - * @private - */ - _fontSizeFraction: 4, - - /** - * @private - */ - _currentCursorOpacity: 0, - - /** - * @private - */ - _selectionDirection: null, - - /** - * @private - */ - _abortCursorAnimation: false, - - /** - * Constructor - * @param {String} text Text string - * @param {Object} [options] Options object - * @return {fabric.IText} thisArg - */ - initialize: function(text, options) { - this.styles = options.styles || { }; - this.callSuper('initialize', text, options); - this.initBehavior(); - }, - - /** - * Returns true if object has no styling - */ - isEmptyStyles: function() { - if (!this.styles) return true; - var obj = this.styles; - - for (var p1 in obj) { - for (var p2 in obj[p1]) { - /*jshint unused:false */ - for (var p3 in obj[p1][p2]) { - return false; - } - } - } - return true; - }, - - /** - * Sets selection start (left boundary of a selection) - * @param {Number} index Index to set selection start to - */ - setSelectionStart: function(index) { - this.selectionStart = index; - this.hiddenTextarea && (this.hiddenTextarea.selectionStart = index); - }, - - /** - * Sets selection end (right boundary of a selection) - * @param {Number} index Index to set selection end to - */ - setSelectionEnd: function(index) { - this.selectionEnd = index; - this.hiddenTextarea && (this.hiddenTextarea.selectionEnd = index); - }, - - /** - * Gets style of a current selection/cursor (at the start position) - * @return {Object} styles Style object at a cursor position - */ - getSelectionStyles: function() { - var loc = this.get2DCursorLocation(); - if (this.styles[loc.lineIndex]) { - return this.styles[loc.lineIndex][loc.charIndex] || { }; - } - return { }; - }, - - /** - * Sets style of a current selection - * @param {Object} [styles] Styles object - * @return {fabric.IText} thisArg - * @chainable - */ - setSelectionStyles: function(styles) { - if (this.selectionStart === this.selectionEnd) { - this._extendStyles(this.selectionStart, styles); - } - else { - for (var i = this.selectionStart; i < this.selectionEnd; i++) { - this._extendStyles(i, styles); - } - } - return this; - }, - - /** - * @private - */ - _extendStyles: function(index, styles) { - var loc = this.get2DCursorLocation(index); - - if (!this.styles[loc.lineIndex]) { - this.styles[loc.lineIndex] = { }; - } - if (!this.styles[loc.lineIndex][loc.charIndex]) { - this.styles[loc.lineIndex][loc.charIndex] = { }; - } - - fabric.util.object.extend(this.styles[loc.lineIndex][loc.charIndex], styles); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _render: function(ctx) { - this.callSuper('_render', ctx); - this.isEditing && this.renderCursorOrSelection(ctx); - this.ctx = ctx; - }, - - /** - * Renders cursor or selection (depending on what exists) - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - renderCursorOrSelection: function(ctx) { - if (!this.active) return; - - var chars = this.text.split(''), - boundaries; - - if (this.selectionStart === this.selectionEnd) { - boundaries = this.getCursorBoundaries(ctx, chars, 'cursor'); - this.renderCursor(ctx, boundaries); - } - else { - boundaries = this.getCursorBoundaries(ctx, chars, 'selection'); - this.renderSelection(ctx, chars, boundaries); - } - }, - - /** - * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start) - * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used. - */ - get2DCursorLocation: function(selectionStart) { - if (typeof selectionStart === 'undefined') { - selectionStart = this.selectionStart; - } - var textBeforeCursor = this.text.slice(0, selectionStart); - var linesBeforeCursor = textBeforeCursor.split(this._reNewline); - - return { - lineIndex: linesBeforeCursor.length - 1, - charIndex: linesBeforeCursor[linesBeforeCursor.length - 1].length - }; - }, - - /** - * Returns fontSize of char at the current cursor - * @param {Number} lineIndex Line index - * @param {Number} charIndex Char index - * @return {Number} Character font size - */ - getCurrentCharFontSize: function(lineIndex, charIndex) { - return ( - this.styles[lineIndex] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fontSize) || this.fontSize; - }, - - /** - * Returns color (fill) of char at the current cursor - * @param {Number} lineIndex Line index - * @param {Number} charIndex Char index - * @return {String} Character color (fill) - */ - getCurrentCharColor: function(lineIndex, charIndex) { - return ( - this.styles[lineIndex] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)] && - this.styles[lineIndex][charIndex === 0 ? 0 : (charIndex - 1)].fill) || this.cursorColor; - }, - - /** - * Returns cursor boundaries (left, top, leftOffset, topOffset) - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} chars Array of characters - */ - getCursorBoundaries: function(ctx, chars, typeOfBoundaries) { - - var cursorLocation = this.get2DCursorLocation(); - var lineIndex = cursorLocation.lineIndex; - var charIndex = cursorLocation.charIndex; - - var textLines = this.text.split(this._reNewline); - - var widthOfLine; - var lineLeftOffset; - - // left/top are left/top of entire text box - // leftOffset/topOffset are offset from that left/top point of a text box - var left = Math.round(this._getLeftOffset()); - var top = -this.height / 2; - - var leftOffset = 0; - var topOffset = typeOfBoundaries === 'cursor' - // selection starts at the very top of the line, - // whereas cursor starts at the padding created by line height - ? (this._getHeightOfLine(ctx, 0) - this.getCurrentCharFontSize(lineIndex, charIndex)) - : 0; - - lineIndex = 0; - charIndex = 0; - - for (var i = 0; i < this.selectionStart; i++) { - if (chars[i] === '\n') { - leftOffset = 0; - topOffset += this._getHeightOfLine(ctx, lineIndex + (typeOfBoundaries === 'cursor' ? 1 : 0)); - - lineIndex++; - charIndex = 0; - } - else { - leftOffset += this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); - charIndex++; - } - - widthOfLine = this._getWidthOfLine(ctx, lineIndex, textLines); - lineLeftOffset = this._getLineLeftOffset(widthOfLine); - } - - return { - left: left, - top: top, - leftOffset: leftOffset + (lineLeftOffset || 0), - topOffset: topOffset - }; - }, - - /** - * Renders cursor - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - renderCursor: function(ctx, boundaries) { - ctx.save(); - - var cursorLocation = this.get2DCursorLocation(); - var lineIndex = cursorLocation.lineIndex; - var charIndex = cursorLocation.charIndex; - - ctx.fillStyle = this.getCurrentCharColor(lineIndex, charIndex); - ctx.globalAlpha = this._currentCursorOpacity; - - var charHeight = this.getCurrentCharFontSize(lineIndex, charIndex); - - ctx.fillRect( - boundaries.left + boundaries.leftOffset, - boundaries.top + boundaries.topOffset, - this.cursorWidth, - charHeight); - - ctx.restore(); - }, - - /** - * Renders text selection - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} chars Array of characters - * @param {Object} boundaries Object with left/top/leftOffset/topOffset - */ - renderSelection: function(ctx, chars, boundaries) { - ctx.save(); - - ctx.fillStyle = this.selectionColor; - - var cursorLocation = this.get2DCursorLocation(); - var lineIndex = cursorLocation.lineIndex; - var charIndex = cursorLocation.charIndex; - var textLines = this.text.split(this._reNewline); - var origLineIndex = lineIndex; - - for (var i = this.selectionStart; i < this.selectionEnd; i++) { - - if (chars[i] === '\n') { - boundaries.leftOffset = 0; - boundaries.topOffset += this._getHeightOfLine(ctx, lineIndex); - lineIndex++; - charIndex = 0; - } - else if (i !== this.text.length) { - - var charWidth = this._getWidthOfChar(ctx, chars[i], lineIndex, charIndex); - var lineOffset = this._getLineLeftOffset(this._getWidthOfLine(ctx, lineIndex, textLines)) || 0; - - if (lineIndex === origLineIndex) { - // only offset the line if we're rendering selection of 2nd, 3rd, etc. line - lineOffset = 0; - } - - ctx.fillRect( - boundaries.left + boundaries.leftOffset + lineOffset, - boundaries.top + boundaries.topOffset, - charWidth, - this._getHeightOfLine(ctx, lineIndex)); - - boundaries.leftOffset += charWidth; - charIndex++; - } - } - ctx.restore(); - }, - - /** - * @private - * @param {String} method - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderChars: function(method, ctx, line, left, top, lineIndex) { - - if (this.isEmptyStyles()) { - return this._renderCharsFast(method, ctx, line, left, top); - } - - this.skipTextAlign = true; - - // set proper box offset - left -= this.textAlign === 'center' - ? (this.width / 2) - : (this.textAlign === 'right') - ? this.width - : 0; - - // set proper line offset - var textLines = this.text.split(this._reNewline); - var lineWidth = this._getWidthOfLine(ctx, lineIndex, textLines); - var lineHeight = this._getHeightOfLine(ctx, lineIndex, textLines); - var lineLeftOffset = this._getLineLeftOffset(lineWidth); - var chars = line.split(''); - - left += lineLeftOffset || 0; - - ctx.save(); - for (var i = 0, len = chars.length; i < len; i++) { - this._renderChar(method, ctx, lineIndex, i, chars[i], left, top, lineHeight); - } - ctx.restore(); - }, - - /** - * @private - * @param {String} method - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} line - */ - _renderCharsFast: function(method, ctx, line, left, top) { - this.skipTextAlign = false; - - if (method === 'fillText' && this.fill) { - this.callSuper('_renderChars', method, ctx, line, left, top); - } - if (method === 'strokeText' && this.stroke) { - this.callSuper('_renderChars', method, ctx, line, left, top); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderChar: function(method, ctx, lineIndex, i, _char, left, top, lineHeight) { - var decl, charWidth; - - if (this.styles && this.styles[lineIndex] && (decl = this.styles[lineIndex][i])) { - - var shouldStroke = decl.stroke || this.stroke; - var shouldFill = decl.fill || this.fill; - - ctx.save(); - charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i, decl); - - if (shouldFill) { - ctx.fillText(_char, left, top); - } - if (shouldStroke) { - ctx.strokeText(_char, left, top); - } - - this._renderCharDecoration(ctx, decl, left, top, charWidth, lineHeight); - ctx.restore(); - - ctx.translate(charWidth, 0); - } - else { - if (method === 'strokeText' && this.stroke) { - ctx[method](_char, left, top); - } - if (method === 'fillText' && this.fill) { - ctx[method](_char, left, top); - } - charWidth = this._applyCharStylesGetWidth(ctx, _char, lineIndex, i); - this._renderCharDecoration(ctx, null, left, top, charWidth, lineHeight); - - ctx.translate(ctx.measureText(_char).width, 0); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderCharDecoration: function(ctx, styleDeclaration, left, top, charWidth, lineHeight) { - var textDecoration = styleDeclaration - ? (styleDeclaration.textDecoration || this.textDecoration) - : this.textDecoration; - - if (!textDecoration) return; - - if (textDecoration.indexOf('underline') > -1) { - - this._renderCharDecorationAtOffset( - ctx, - left, - top + (this.fontSize / this._fontSizeFraction), - charWidth, - 0 - ); - } - if (textDecoration.indexOf('line-through') > -1) { - this._renderCharDecorationAtOffset( - ctx, - left, - top + (this.fontSize / this._fontSizeFraction), - charWidth, - (lineHeight / this._fontSizeFraction) - ); - } - if (textDecoration.indexOf('overline') > -1) { - this._renderCharDecorationAtOffset( - ctx, - left, - top, - charWidth, - lineHeight - (this.fontSize / this._fontSizeFraction) - ); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _renderCharDecorationAtOffset: function(ctx, left, top, charWidth, offset) { - ctx.fillRect(left, top - offset, charWidth, 1); - }, - - /** - * @private - * @param {String} method - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} line - */ - _renderTextLine: function(method, ctx, line, left, top, lineIndex) { - // to "cancel" this.fontSize subtraction in fabric.Text#_renderTextLine - top += this.fontSize / 4; - this.callSuper('_renderTextLine', method, ctx, line, left, top, lineIndex); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines - */ - _renderTextDecoration: function(ctx, textLines) { - if (this.isEmptyStyles()) { - return this.callSuper('_renderTextDecoration', ctx, textLines); - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {Array} textLines Array of all text lines - */ - _renderTextLinesBackground: function(ctx, textLines) { - if (!this.textBackgroundColor && !this.styles) return; - - ctx.save(); - - if (this.textBackgroundColor) { - ctx.fillStyle = this.textBackgroundColor; - } - - var lineHeights = 0; - var fractionOfFontSize = this.fontSize / this._fontSizeFraction; - - for (var i = 0, len = textLines.length; i < len; i++) { - - var heightOfLine = this._getHeightOfLine(ctx, i, textLines); - if (textLines[i] === '') { - lineHeights += heightOfLine; - continue; - } - - var lineWidth = this._getWidthOfLine(ctx, i, textLines); - var lineLeftOffset = this._getLineLeftOffset(lineWidth); - - if (this.textBackgroundColor) { - ctx.fillStyle = this.textBackgroundColor; - - ctx.fillRect( - this._getLeftOffset() + lineLeftOffset, - this._getTopOffset() + lineHeights + fractionOfFontSize, - lineWidth, - heightOfLine - ); - } - if (this.styles[i]) { - for (var j = 0, jlen = textLines[i].length; j < jlen; j++) { - if (this.styles[i] && this.styles[i][j] && this.styles[i][j].textBackgroundColor) { - - var _char = textLines[i][j]; - - ctx.fillStyle = this.styles[i][j].textBackgroundColor; - - ctx.fillRect( - this._getLeftOffset() + lineLeftOffset + this._getWidthOfCharsAt(ctx, i, j, textLines), - this._getTopOffset() + lineHeights + fractionOfFontSize, - this._getWidthOfChar(ctx, _char, i, j, textLines) + 1, - heightOfLine - ); - } - } - } - lineHeights += heightOfLine; - } - ctx.restore(); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - * @param {String} _char - * @param {Number} lineIndex - * @param {Number} charIndex - * @param {Object} [decl] - */ - _applyCharStylesGetWidth: function(ctx, _char, lineIndex, charIndex, decl) { - var styleDeclaration = decl || (this.styles[lineIndex] && this.styles[lineIndex][charIndex]); - - if (styleDeclaration) { - // cloning so that original style object is not polluted with following font declarations - styleDeclaration = clone(styleDeclaration); - } - else { - styleDeclaration = { }; - } - - var fill = styleDeclaration.fill || this.fill; - ctx.fillStyle = fill.toLive - ? fill.toLive(ctx) - : fill; - - if (styleDeclaration.stroke) { - ctx.strokeStyle = (styleDeclaration.stroke && styleDeclaration.stroke.toLive) - ? styleDeclaration.stroke.toLive(ctx) - : styleDeclaration.stroke; - } - - ctx.lineWidth = styleDeclaration.strokeWidth || this.strokeWidth; - - this._applyFontStyles(styleDeclaration); - - if (typeof styleDeclaration.shadow === 'string') { - styleDeclaration.shadow = new fabric.Shadow(styleDeclaration.shadow); - } - - this._setShadow.call(styleDeclaration, ctx); - - ctx.font = this._getFontDeclaration.call(styleDeclaration); - - return ctx.measureText(_char).width; - }, - - /** - * @private - * @param {Object} styleDeclaration - */ - _applyFontStyles: function(styleDeclaration) { - if (!styleDeclaration.fontFamily) { - styleDeclaration.fontFamily = this.fontFamily; - } - if (!styleDeclaration.fontSize) { - styleDeclaration.fontSize = this.fontSize; - } - if (!styleDeclaration.fontWeight) { - styleDeclaration.fontWeight = this.fontWeight; - } - if (!styleDeclaration.fontStyle) { - styleDeclaration.fontStyle = this.fontStyle; - } - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfChar: function(ctx, _char, lineIndex, charIndex) { - ctx.save(); - var width = this._applyCharStylesGetWidth(ctx, _char, lineIndex, charIndex); - ctx.restore(); - return width; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getHeightOfChar: function(ctx, _char, lineIndex, charIndex) { - if (this.styles[lineIndex] && this.styles[lineIndex][charIndex]) { - return this.styles[lineIndex][charIndex].fontSize || this.fontSize; - } - return this.fontSize; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfCharAt: function(ctx, lineIndex, charIndex, lines) { - lines = lines || this.text.split(this._reNewline); - var _char = lines[lineIndex].split('')[charIndex]; - return this._getWidthOfChar(ctx, _char, lineIndex, charIndex); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getHeightOfCharAt: function(ctx, lineIndex, charIndex, lines) { - lines = lines || this.text.split(this._reNewline); - var _char = lines[lineIndex].split('')[charIndex]; - return this._getHeightOfChar(ctx, _char, lineIndex, charIndex); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfCharsAt: function(ctx, lineIndex, charIndex, lines) { - var width = 0; - for (var i = 0; i < charIndex; i++) { - width += this._getWidthOfCharAt(ctx, lineIndex, i, lines); - } - return width; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getWidthOfLine: function(ctx, lineIndex, textLines) { - // if (!this.styles[lineIndex]) { - // return this.callSuper('_getLineWidth', ctx, textLines[lineIndex]); - // } - return this._getWidthOfCharsAt(ctx, lineIndex, textLines[lineIndex].length, textLines); - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getTextWidth: function(ctx, textLines) { - - if (this.isEmptyStyles()) { - return this.callSuper('_getTextWidth', ctx, textLines); - } - - var maxWidth = this._getWidthOfLine(ctx, 0, textLines); - - for (var i = 1, len = textLines.length; i < len; i++) { - var currentLineWidth = this._getWidthOfLine(ctx, i, textLines); - if (currentLineWidth > maxWidth) { - maxWidth = currentLineWidth; - } - } - return maxWidth; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getHeightOfLine: function(ctx, lineIndex, textLines) { - - textLines = textLines || this.text.split(this._reNewline); - - var maxHeight = this._getHeightOfChar(ctx, textLines[lineIndex][0], lineIndex, 0); - - var line = textLines[lineIndex]; - var chars = line.split(''); - - for (var i = 1, len = chars.length; i < len; i++) { - var currentCharHeight = this._getHeightOfChar(ctx, chars[i], lineIndex, i); - if (currentCharHeight > maxHeight) { - maxHeight = currentCharHeight; - } - } - - return maxHeight * this.lineHeight; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getTextHeight: function(ctx, textLines) { - var height = 0; - for (var i = 0, len = textLines.length; i < len; i++) { - height += this._getHeightOfLine(ctx, i, textLines); - } - return height; - }, - - /** - * @private - * @param {CanvasRenderingContext2D} ctx Context to render on - */ - _getTopOffset: function() { - var topOffset = fabric.Text.prototype._getTopOffset.call(this); - return topOffset - (this.fontSize / this._fontSizeFraction); - }, - - /** - * Returns object representation of an instance - * @methd toObject - * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output - * @return {Object} object representation of an instance - */ - toObject: function(propertiesToInclude) { - return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { - styles: clone(this.styles) - }); - }, - - /* _TO_SVG_START_ */ - /** - * Returns SVG representation of an instance - * @return {String} svg representation of an instance - */ - toSVG: function(reviver) { - if (this.isEmptyStyles()) { - return this.callSuper('toSVG', reviver); - } - // TODO: add support for styled text SVG output - } - /* _TO_SVG_END_ */ }); - /** - * Returns fabric.IText instance from an object representation - * @static - * @memberOf fabric.IText - * @param {Object} object Object to create an instance from - * @return {fabric.IText} instance of fabric.IText - */ - fabric.IText.fromObject = function(object) { - return new fabric.IText(object.text, clone(object)); - }; - })(); + (function() { if (typeof document !== 'undefined' && typeof window !== 'undefined') { diff --git a/src/mixins/itext_behavior.mixin.js b/src/mixins/itext_behavior.mixin.js index 7afe959f..fef76bb8 100644 --- a/src/mixins/itext_behavior.mixin.js +++ b/src/mixins/itext_behavior.mixin.js @@ -2,7 +2,7 @@ var clone = fabric.util.object.clone; - fabric.ITextBehavior = { /** @lends fabric.IText.prototype */ + fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ { /** * Initializes all the interactive behavior of IText @@ -1162,5 +1162,7 @@ insertNewline: function() { this.insertChar('\n'); } - }; + }); + })(); + diff --git a/src/shapes/itext.class.js b/src/shapes/itext.class.js index fd5fd941..3a0a9e66 100644 --- a/src/shapes/itext.class.js +++ b/src/shapes/itext.class.js @@ -3,16 +3,16 @@ var clone = fabric.util.object.clone; /** - * IText class + * IText class (introduced in v1.4) * @class fabric.IText * @extends fabric.Text * @mixes fabric.Observable - * @fires #text:changed + * @fires text:changed * @return {fabric.IText} thisArg * @see {@link fabric.IText#initialize} for constructor definition * - * Supported key combinations: - * + *

Supported key combinations:

+ *
     *   Move cursor:                    left, right, up, down
     *   Select character:               shift + left, shift + right
     *   Select text vertically:         shift + up, shift + down
@@ -26,8 +26,9 @@
     *   Delete word:                    alt + backspace
     *   Delete line:                    cmd + backspace
     *   Forward delete:                 delete
+    * 
*/ - fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, fabric.ITextBehavior, { + fabric.IText = fabric.util.createClass(fabric.Text, fabric.Observable, /** @lends fabric.IText.prototype */ { /** * Type of an object