i){r=!0;var d=a-f,g=a,p=Math.abs(d-i),v=Math.abs(g-i);h=v=this.text.length&&this.selectionEnd>=this.text.length||this._moveCursorUpOrDown("Down",t)},moveCursorDownWithoutShift:function(t){return this._selectionDirection="right",this.selectionEnd=this.selectionEnd+t,this.selectionStart=this.selectionEnd,0!==t},swapSelectionPoints:function(){var t=this.selectionEnd;this.selectionEnd=this.selectionStart,this.selectionStart=t},moveCursorDownWithShift:function(t){return this.selectionEnd===this.selectionStart&&(this._selectionDirection="right"),"right"===this._selectionDirection?this.selectionEnd+=t:this.selectionStart+=t,this.selectionEndthis.text.length&&(this.selectionEnd=this.text.length),0!==t},getUpCursorOffset:function(t,e){var i=e?this.selectionEnd:this.selectionStart,r=this.get2DCursorLocation(i),n=r.lineIndex;if(0===n||t.metaKey||33===t.keyCode)return i;for(var s,o=this._textLines[n].slice(0,r.charIndex),a=this._textLines[n-1]||"",h=this._getLineWidth(this.ctx,r.lineIndex),c=this._getLineLeftOffset(h),l=c,u=0,f=o.length;ui){r=!0;var d=a-f,g=a,p=Math.abs(d-i),v=Math.abs(g-i);h=v=this.text.length&&this.selectionEnd>=this.text.length||this._moveCursorLeftOrRight("Right",t)},_moveCursorLeftOrRight:function(t,e){var i="moveCursor"+t+"With";this._currentCursorOpacity=1,i+=e.shiftKey?"Shift":"outShift",this[i](e)&&(console.log("will fire"),this.abortCursorAnimation(),this.initDelayedCursor(),this._fireSelectionChanged(),this._updateTextarea())},moveCursorRightWithShift:function(t){return"left"===this._selectionDirection&&this.selectionStart!==this.selectionEnd?this._moveRight(t,"selectionStart"):this.selectionEnd!==this.text.length?(this._selectionDirection="right",this._moveRight(t,"selectionEnd")):void 0},moveCursorRightWithoutShift:function(t){var e=!1;return this._selectionDirection="right",this.selectionStart===this.selectionEnd?(e=this._moveRight(t,"selectionStart"),this.selectionEnd=this.selectionStart,e):(this.selectionStart=this.selectionEnd,!0)},removeChars:function(t){this.selectionStart===this.selectionEnd?this._removeCharsNearCursor(t):this._removeCharsFromTo(this.selectionStart,this.selectionEnd),this.setSelectionEnd(this.selectionStart),this._removeExtraneousStyles(),this.canvas&&this.canvas.renderAll(),this.setCoords(),this.fire("changed"),this.canvas&&this.canvas.fire("text:changed",{target:this})},_removeCharsNearCursor:function(t){if(0!==this.selectionStart)if(t.metaKey){var e=this.findLineBoundaryLeft(this.selectionStart);this._removeCharsFromTo(e,this.selectionStart),this.setSelectionStart(e)}else if(t.altKey){var i=this.findWordBoundaryLeft(this.selectionStart);this._removeCharsFromTo(i,this.selectionStart),this.setSelectionStart(i)}else this._removeSingleCharAndStyle(this.selectionStart),this.setSelectionStart(this.selectionStart-1)}}),function(){var t=fabric.util.toFixed,e=fabric.Object.NUM_FRACTION_DIGITS;fabric.util.object.extend(fabric.IText.prototype,{_setSVGTextLineText:function(t,e,i,r,n,s){this._getLineStyle(t)?this._setSVGTextLineChars(t,e,i,r,s):fabric.Text.prototype._setSVGTextLineText.call(this,t,e,i,r,n)},_setSVGTextLineChars:function(t,e,i,r,n){for(var s=this._textLines[t],o=0,a=this._getLineLeftOffset(this._getLineWidth(this.ctx,t))-this.width/2,h=this._getSVGLineTopOffset(t),c=this._getHeightOfLine(this.ctx,t),l=0,u=s.length;l\n'].join("")},_createTextCharSpan:function(i,r,n,s,o){var a=this.getSvgStyles.call(fabric.util.object.extend({visible:!0,fill:this.fill,stroke:this.stroke,type:"text",getSvgFilter:fabric.Object.prototype.getSvgFilter},r));return['\t\t\t',fabric.util.string.escapeXml(i),"\n"].join("")}})}(),function(t){"use strict";var e=t.fabric||(t.fabric={}),i=e.util.object.clone;e.Textbox=e.util.createClass(e.IText,e.Observable,{type:"textbox",minWidth:20,dynamicMinWidth:0,__cachedLines:null,lockScalingY:!0,lockScalingFlip:!0,initialize:function(t,i){this.ctx=e.util.createCanvasElement().getContext("2d"),this.callSuper("initialize",t,i),this.setControlsVisibility(e.Textbox.getTextboxControlVisibility()),this._dimensionAffectingProps.width=!0},_initDimensions:function(t){this.__skipDimension||(t||(t=e.util.createCanvasElement().getContext("2d"),this._setTextStyles(t)),this.dynamicMinWidth=0,this._textLines=this._splitTextIntoLines(),this.dynamicMinWidth>this.width&&this._set("width",this.dynamicMinWidth),this._clearCache(),this.height=this._getTextHeight(t))},_generateStyleMap:function(){for(var t=0,e=0,i=0,r={},n=0;n=this.width&&!d&&(n.push(s),s="",r=l,d=!0),d||(s+=c),s+=a,u=this._measureText(t,c,i,h),h++,d=!1,l>f&&(f=l);return g&&n.push(s),f>this.dynamicMinWidth&&(this.dynamicMinWidth=f),n},_splitTextIntoLines:function(){var t=this.textAlign;this.ctx.save(),this._setTextStyles(this.ctx),this.textAlign="left";var e=this._wrapText(this.ctx,this.text);return this.textAlign=t,this.ctx.restore(),this._textLines=e,this._styleMap=this._generateStyleMap(),e},setOnGroup:function(t,e){"scaleX"===t&&(this.set("scaleX",Math.abs(1/e)),this.set("width",this.get("width")*e/("undefined"==typeof this.__oldScaleX?1:this.__oldScaleX)),this.__oldScaleX=e)},get2DCursorLocation:function(t){"undefined"==typeof t&&(t=this.selectionStart);for(var e=this._textLines.length,i=0,r=0;r=h.getMinWidth()?(h.set("width",c),!0):void 0},fabric.Group.prototype._refreshControlsVisibility=function(){if("undefined"!=typeof fabric.Textbox)for(var t=this._objects.length;t--;)if(this._objects[t]instanceof fabric.Textbox)return void this.setControlsVisibility(fabric.Textbox.getTextboxControlVisibility())};var e=fabric.util.object.clone;fabric.util.object.extend(fabric.Textbox.prototype,{_removeExtraneousStyles:function(){for(var t in this._styleMap)this._textLines[t]||delete this.styles[this._styleMap[t].line]},insertCharStyleObject:function(t,e,i){var r=this._styleMap[t];t=r.line,e=r.offset+e,fabric.IText.prototype.insertCharStyleObject.apply(this,[t,e,i])},insertNewlineStyleObject:function(t,e,i){var r=this._styleMap[t];t=r.line,e=r.offset+e,fabric.IText.prototype.insertNewlineStyleObject.apply(this,[t,e,i])},shiftLineStyles:function(t,i){var r=e(this.styles),n=this._styleMap[t];t=n.line;for(var s in this.styles){var o=parseInt(s,10);o>t&&(this.styles[o+i]=r[o],r[o-i]||delete this.styles[o])}},_getTextOnPreviousLine:function(t){for(var e=this._textLines[t-1];this._styleMap[t-2]&&this._styleMap[t-2].line===this._styleMap[t-1].line;)e=this._textLines[t-2]+e,t--;return e},removeStyleObject:function(t,e){var i=this.get2DCursorLocation(e),r=this._styleMap[i.lineIndex],n=r.line,s=r.offset+i.charIndex;this._removeStyleObject(t,i,n,s)}})}(),function(){var t=fabric.IText.prototype._getNewSelectionStartFromOffset;fabric.IText.prototype._getNewSelectionStartFromOffset=function(e,i,r,n,s){n=t.call(this,e,i,r,n,s);for(var o=0,a=0,h=0;h=n));h++)"\n"!==this.text[o+a]&&" "!==this.text[o+a]||a++;return n-h+a}}(),function(){function request(t,e,i){var r=URL.parse(t);r.port||(r.port=0===r.protocol.indexOf("https:")?443:80);var n=0===r.protocol.indexOf("https:")?HTTPS:HTTP,s=n.request({hostname:r.hostname,port:r.port,path:r.path,method:"GET"},function(t){var r="";e&&t.setEncoding(e),t.on("end",function(){i(r)}),t.on("data",function(e){200===t.statusCode&&(r+=e)})});s.on("error",function(t){t.errno===process.ECONNREFUSED?fabric.log("ECONNREFUSED: connection refused to "+r.hostname+":"+r.port):fabric.log(t.message),i(null)}),s.end()}function requestFs(t,e){var i=require("fs");i.readFile(t,function(t,i){if(t)throw fabric.log(t),t;e(i)})}if("undefined"==typeof document||"undefined"==typeof window){var DOMParser=require("xmldom").DOMParser,URL=require("url"),HTTP=require("http"),HTTPS=require("https"),Canvas=require("canvas"),Image=require("canvas").Image;fabric.util.loadImage=function(t,e,i){function r(r){r?(n.src=new Buffer(r,"binary"),n._src=t,e&&e.call(i,n)):(n=null,e&&e.call(i,null,!0))}var n=new Image;t&&(t instanceof Buffer||0===t.indexOf("data"))?(n.src=n._src=t,e&&e.call(i,n)):t&&0!==t.indexOf("http")?requestFs(t,r):t?request(t,"binary",r):e&&e.call(i,t)},fabric.loadSVGFromURL=function(t,e,i){t=t.replace(/^\n\s*/,"").replace(/\?.*$/,"").trim(),0!==t.indexOf("http")?requestFs(t,function(t){fabric.loadSVGFromString(t.toString(),e,i)}):request(t,"",function(t){fabric.loadSVGFromString(t,e,i)})},fabric.loadSVGFromString=function(t,e,i){var r=(new DOMParser).parseFromString(t);fabric.parseSVGDocument(r.documentElement,function(t,i){e&&e(t,i)},i)},fabric.util.getScript=function(url,callback){request(url,"",function(body){eval(body),callback&&callback()})},fabric.Image.fromObject=function(t,e){fabric.util.loadImage(t.src,function(i){var r=new fabric.Image(i);r._initConfig(t),r._initFilters(t.filters,function(i){r.filters=i||[],r._initFilters(t.resizeFilters,function(t){r.resizeFilters=t||[],e&&e(r)})})})},fabric.createCanvasForNode=function(t,e,i,r){r=r||i;var n=fabric.document.createElement("canvas"),s=new Canvas(t||600,e||600,r),o=new Canvas(t||600,e||600,r);n.style={},n.width=s.width,n.height=s.height;var a=fabric.Canvas||fabric.StaticCanvas,h=new a(n,i);return h.contextContainer=s.getContext("2d"),h.nodeCanvas=s,h.contextCache=o.getContext("2d"),h.nodeCacheCanvas=o,h.Font=Canvas.Font,h},fabric.StaticCanvas.prototype.createPNGStream=function(){return this.nodeCanvas.createPNGStream()},fabric.StaticCanvas.prototype.createJPEGStream=function(t){return this.nodeCanvas.createJPEGStream(t)};var origSetWidth=fabric.StaticCanvas.prototype.setWidth;fabric.StaticCanvas.prototype.setWidth=function(t,e){return origSetWidth.call(this,t,e),this.nodeCanvas.width=t,this},fabric.Canvas&&(fabric.Canvas.prototype.setWidth=fabric.StaticCanvas.prototype.setWidth);var origSetHeight=fabric.StaticCanvas.prototype.setHeight;fabric.StaticCanvas.prototype.setHeight=function(t,e){return origSetHeight.call(this,t,e),this.nodeCanvas.height=t,this},fabric.Canvas&&(fabric.Canvas.prototype.setHeight=fabric.StaticCanvas.prototype.setHeight)}}();
\ No newline at end of file
diff --git a/dist/fabric.min.js.gz b/dist/fabric.min.js.gz
index dc6bebd9..caa9aa32 100644
Binary files a/dist/fabric.min.js.gz and b/dist/fabric.min.js.gz differ
diff --git a/dist/fabric.require.js b/dist/fabric.require.js
index 00c9c8b2..f6153389 100644
--- a/dist/fabric.require.js
+++ b/dist/fabric.require.js
@@ -10181,26 +10181,25 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
},
setSelectionStart: function(index) {
index = Math.max(index, 0);
- if (this.selectionStart !== index) {
- this.fire("selection:changed");
- this.canvas && this.canvas.fire("text:selection:changed", {
- target: this
- });
- this.selectionStart = index;
- }
- this._updateTextarea();
+ this._updateAndFire("selectionStart", index);
},
setSelectionEnd: function(index) {
index = Math.min(index, this.text.length);
- if (this.selectionEnd !== index) {
- this.fire("selection:changed");
- this.canvas && this.canvas.fire("text:selection:changed", {
- target: this
- });
- this.selectionEnd = index;
+ this._updateAndFire("selectionEnd", index);
+ },
+ _updateAndFire: function(property, index) {
+ if (this[property] !== index) {
+ this._fireSelectionChanged();
+ this[property] = index;
}
this._updateTextarea();
},
+ _fireSelectionChanged: function() {
+ this.fire("selection:changed");
+ this.canvas && this.canvas.fire("text:selection:changed", {
+ target: this
+ });
+ },
getSelectionStyles: function(startIndex, endIndex) {
if (arguments.length === 2) {
var styles = [];
@@ -10761,16 +10760,21 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
}, delay);
},
abortCursorAnimation: function() {
+ var shouldClear = this._currentTickState || this._currentTickCompleteState;
this._currentTickState && this._currentTickState.abort();
this._currentTickCompleteState && this._currentTickCompleteState.abort();
clearTimeout(this._cursorTimeout1);
clearTimeout(this._cursorTimeout2);
this._currentCursorOpacity = 0;
- this.canvas && this.canvas.clearContext(this.canvas.contextTop || this.ctx);
+ if (shouldClear) {
+ this.canvas && this.canvas.clearContext(this.canvas.contextTop || this.ctx);
+ }
},
selectAll: function() {
- this.setSelectionStart(0);
- this.setSelectionEnd(this.text.length);
+ this.selectionStart = 0;
+ this.selectionEnd = this.text.length;
+ this._fireSelectionChanged();
+ this._updateTextarea();
},
getSelectedText: function() {
return this.text.slice(this.selectionStart, this.selectionEnd);
@@ -10840,14 +10844,21 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
return index;
},
selectWord: function(selectionStart) {
+ selectionStart = selectionStart || this.selectionStart;
var newSelectionStart = this.searchWordBoundary(selectionStart, -1), newSelectionEnd = this.searchWordBoundary(selectionStart, 1);
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(newSelectionEnd);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = newSelectionEnd;
+ this._fireSelectionChanged();
+ this._updateTextarea();
+ this.renderCursorOrSelection();
},
selectLine: function(selectionStart) {
+ selectionStart = selectionStart || this.selectionStart;
var newSelectionStart = this.findLineBoundaryLeft(selectionStart), newSelectionEnd = this.findLineBoundaryRight(selectionStart);
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(newSelectionEnd);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = newSelectionEnd;
+ this._fireSelectionChanged();
+ this._updateTextarea();
},
enterEditing: function(e) {
if (this.isEditing || !this.editable) {
@@ -10868,10 +10879,10 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
if (!this.canvas) {
return this;
}
- this.canvas.renderAll();
this.canvas.fire("text:editing:entered", {
target: this
});
+ this.canvas.renderAll();
this.initMouseMoveHandler();
return this;
},
@@ -10897,12 +10908,14 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
return;
}
if (newSelectionStart > this.__selectionStartOnMouseDown) {
- this.setSelectionStart(this.__selectionStartOnMouseDown);
- this.setSelectionEnd(newSelectionStart);
+ this.selectionStart = this.__selectionStartOnMouseDown;
+ this.selectionEnd = newSelectionStart;
} else {
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(this.__selectionStartOnMouseDown);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = this.__selectionStartOnMouseDown;
}
+ this._fireSelectionChanged();
+ this._updateTextarea();
this.renderCursorOrSelection();
},
_setEditingProps: function() {
@@ -11020,7 +11033,8 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
this._removeSingleCharAndStyle(start + 1);
end--;
}
- this.setSelectionStart(start);
+ this.selectionStart = start;
+ this.selectionEnd = start;
},
_removeSingleCharAndStyle: function(index) {
var isBeginningOfLine = this.text[index - 1] === "\n", indexStyle = isBeginningOfLine ? index : index - 1;
@@ -11032,7 +11046,6 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
var style;
if (this.selectionEnd - this.selectionStart > 1) {
this._removeCharsFromTo(this.selectionStart, this.selectionEnd);
- this.setSelectionEnd(this.selectionStart);
}
if (!useCopiedStyle && this.isEmptyStyles()) {
this.insertChar(_chars, false);
@@ -11056,12 +11069,13 @@ fabric.Image.filters.BaseFilter = fabric.util.createClass({
return;
}
this._updateTextarea();
- this.canvas && this.canvas.renderAll();
this.setCoords();
+ this._fireSelectionChanged();
this.fire("changed");
this.canvas && this.canvas.fire("text:changed", {
target: this
});
+ this.canvas && this.canvas.renderAll();
},
insertNewlineStyleObject: function(lineIndex, charIndex, isEndOfLine) {
this.shiftLineStyles(lineIndex, +1);
@@ -11228,7 +11242,10 @@ fabric.util.object.extend(fabric.IText.prototype, {
}
if (this.isEditing) {
this.__selectionStartOnMouseDown = this.selectionStart;
- this.initDelayedCursor(true);
+ if (this.selectionStart === this.selectionEnd) {
+ this.abortCursorAnimation();
+ }
+ this.renderCursorOrSelection();
}
});
},
@@ -11244,7 +11261,11 @@ fabric.util.object.extend(fabric.IText.prototype, {
}
if (this.__lastSelected && !this.__corner) {
this.enterEditing(options.e);
- this.initDelayedCursor(true);
+ if (this.selectionStart === this.selectionEnd) {
+ this.initDelayedCursor(true);
+ } else {
+ this.renderCursorOrSelection();
+ }
}
this.selected = true;
});
@@ -11253,15 +11274,17 @@ fabric.util.object.extend(fabric.IText.prototype, {
var newSelectionStart = this.getSelectionStartFromPointer(e);
if (e.shiftKey) {
if (newSelectionStart < this.selectionStart) {
- this.setSelectionEnd(this.selectionStart);
- this.setSelectionStart(newSelectionStart);
+ this.selectionEnd = this.selectionStart;
+ this.selectionStart = newSelectionStart;
} else {
- this.setSelectionEnd(newSelectionStart);
+ this.selectionEnd = newSelectionStart;
}
} else {
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(newSelectionStart);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = newSelectionStart;
}
+ this._fireSelectionChanged();
+ this._updateTextarea();
},
getSelectionStartFromPointer: function(e) {
var mouseOffset = this.getLocalPointer(e), prevWidth = 0, width = 0, height = 0, charIndex = 0, newSelectionStart, line;
@@ -11491,42 +11514,39 @@ fabric.util.object.extend(fabric.IText.prototype, {
return indexOnNextLine;
},
moveCursorDown: function(e) {
- this.abortCursorAnimation();
- this._currentCursorOpacity = 1;
- var offset = this.getDownCursorOffset(e, this._selectionDirection === "right");
- if (e.shiftKey) {
- this.moveCursorDownWithShift(offset);
- } else {
- this.moveCursorDownWithoutShift(offset);
+ if (this.selectionStart >= this.text.length && this.selectionEnd >= this.text.length) {
+ return;
}
- this.initDelayedCursor();
+ this._moveCursorUpOrDown("Down", e);
},
moveCursorDownWithoutShift: function(offset) {
this._selectionDirection = "right";
- this.setSelectionStart(this.selectionStart + offset);
- this.setSelectionEnd(this.selectionStart);
+ this.selectionEnd = this.selectionEnd + offset;
+ this.selectionStart = this.selectionEnd;
+ return offset !== 0;
},
swapSelectionPoints: function() {
var swapSel = this.selectionEnd;
- this.setSelectionEnd(this.selectionStart);
- this.setSelectionStart(swapSel);
+ this.selectionEnd = this.selectionStart;
+ this.selectionStart = swapSel;
},
moveCursorDownWithShift: function(offset) {
if (this.selectionEnd === this.selectionStart) {
this._selectionDirection = "right";
}
if (this._selectionDirection === "right") {
- this.setSelectionEnd(this.selectionEnd + offset);
+ this.selectionEnd += offset;
} else {
- this.setSelectionStart(this.selectionStart + offset);
+ this.selectionStart += offset;
}
if (this.selectionEnd < this.selectionStart && this._selectionDirection === "left") {
this.swapSelectionPoints();
this._selectionDirection = "right";
}
if (this.selectionEnd > this.text.length) {
- this.setSelectionEnd(this.text.length);
+ this.selectionEnd = this.text.length;
}
+ return offset !== 0;
},
getUpCursorOffset: function(e, isRight) {
var selectionProp = isRight ? this.selectionEnd : this.selectionStart, cursorLocation = this.get2DCursorLocation(selectionProp), lineIndex = cursorLocation.lineIndex;
@@ -11559,110 +11579,131 @@ fabric.util.object.extend(fabric.IText.prototype, {
return indexOnPrevLine;
},
moveCursorUp: function(e) {
- this.abortCursorAnimation();
- this._currentCursorOpacity = 1;
- var offset = this.getUpCursorOffset(e, this._selectionDirection === "right");
- if (e.shiftKey) {
- this.moveCursorUpWithShift(offset);
- } else {
- this.moveCursorUpWithoutShift(offset);
+ if (this.selectionStart === 0 && this.selectionEnd === 0) {
+ return;
+ }
+ this._moveCursorUpOrDown("Up", e);
+ },
+ _moveCursorUpOrDown: function(direction, e) {
+ var action = "get" + direction + "CursorOffset", moveAction = "moveCursor" + direction, offset = this[action](e, this._selectionDirection === "right");
+ if (e.shiftKey) {
+ moveAction += "WithShift";
+ } else {
+ moveAction += "WithoutShift";
+ }
+ if (this[moveAction](offset)) {
+ this.abortCursorAnimation();
+ this._currentCursorOpacity = 1;
+ this.initDelayedCursor();
+ this._fireSelectionChanged();
+ this._updateTextarea();
}
- this.initDelayedCursor();
},
moveCursorUpWithShift: function(offset) {
if (this.selectionEnd === this.selectionStart) {
this._selectionDirection = "left";
}
if (this._selectionDirection === "right") {
- this.setSelectionEnd(this.selectionEnd - offset);
+ this.selectionEnd -= offset;
} else {
- this.setSelectionStart(this.selectionStart - offset);
+ this.selectionStart -= offset;
}
if (this.selectionEnd < this.selectionStart && this._selectionDirection === "right") {
this.swapSelectionPoints();
this._selectionDirection = "left";
}
+ return offset !== 0;
},
moveCursorUpWithoutShift: function(offset) {
- if (this.selectionStart === this.selectionEnd) {
- this.setSelectionStart(this.selectionStart - offset);
- }
- this.setSelectionEnd(this.selectionStart);
this._selectionDirection = "left";
+ this.selectionStart -= offset;
+ this.selectionEnd = this.selectionStart;
+ return offset !== 0;
},
moveCursorLeft: function(e) {
if (this.selectionStart === 0 && this.selectionEnd === 0) {
return;
}
- this.abortCursorAnimation();
- this._currentCursorOpacity = 1;
- if (e.shiftKey) {
- this.moveCursorLeftWithShift(e);
- } else {
- this.moveCursorLeftWithoutShift(e);
- }
- this.initDelayedCursor();
+ this._moveCursorLeftOrRight("Left", e);
},
_move: function(e, prop, direction) {
- var propMethod = prop === "selectionStart" ? "setSelectionStart" : "setSelectionEnd";
+ var newValue;
if (e.altKey) {
- this[propMethod](this["findWordBoundary" + direction](this[prop]));
+ newValue = this["findWordBoundary" + direction](this[prop]);
} else if (e.metaKey || e.keyCode === 35 || e.keyCode === 36) {
- this[propMethod](this["findLineBoundary" + direction](this[prop]));
+ newValue = this["findLineBoundary" + direction](this[prop]);
} else {
- this[propMethod](this[prop] + (direction === "Left" ? -1 : 1));
+ this[prop] += direction === "Left" ? -1 : 1;
+ return true;
+ }
+ if (typeof newValue !== undefined && this[prop] !== newValue) {
+ this[prop] = newValue;
+ return true;
}
},
_moveLeft: function(e, prop) {
- this._move(e, prop, "Left");
+ return this._move(e, prop, "Left");
},
_moveRight: function(e, prop) {
- this._move(e, prop, "Right");
+ return this._move(e, prop, "Right");
},
moveCursorLeftWithoutShift: function(e) {
+ var change = false;
this._selectionDirection = "left";
- if (this.selectionEnd === this.selectionStart) {
- this._moveLeft(e, "selectionStart");
+ if (this.selectionEnd === this.selectionStart && this.selectionStart !== 0) {
+ change = this._moveLeft(e, "selectionStart");
}
- this.setSelectionEnd(this.selectionStart);
+ this.selectionEnd = this.selectionStart;
+ return change;
},
moveCursorLeftWithShift: function(e) {
if (this._selectionDirection === "right" && this.selectionStart !== this.selectionEnd) {
- this._moveLeft(e, "selectionEnd");
- } else {
+ return this._moveLeft(e, "selectionEnd");
+ } else if (this.selectionStart !== 0) {
this._selectionDirection = "left";
- this._moveLeft(e, "selectionStart");
+ return this._moveLeft(e, "selectionStart");
}
},
moveCursorRight: function(e) {
if (this.selectionStart >= this.text.length && this.selectionEnd >= this.text.length) {
return;
}
- this.abortCursorAnimation();
+ this._moveCursorLeftOrRight("Right", e);
+ },
+ _moveCursorLeftOrRight: function(direction, e) {
+ var actionName = "moveCursor" + direction + "With";
this._currentCursorOpacity = 1;
if (e.shiftKey) {
- this.moveCursorRightWithShift(e);
+ actionName += "Shift";
} else {
- this.moveCursorRightWithoutShift(e);
+ actionName += "outShift";
+ }
+ if (this[actionName](e)) {
+ console.log("will fire");
+ this.abortCursorAnimation();
+ this.initDelayedCursor();
+ this._fireSelectionChanged();
+ this._updateTextarea();
}
- this.initDelayedCursor();
},
moveCursorRightWithShift: function(e) {
if (this._selectionDirection === "left" && this.selectionStart !== this.selectionEnd) {
- this._moveRight(e, "selectionStart");
- } else {
+ return this._moveRight(e, "selectionStart");
+ } else if (this.selectionEnd !== this.text.length) {
this._selectionDirection = "right";
- this._moveRight(e, "selectionEnd");
+ return this._moveRight(e, "selectionEnd");
}
},
moveCursorRightWithoutShift: function(e) {
+ var changed = false;
this._selectionDirection = "right";
if (this.selectionStart === this.selectionEnd) {
- this._moveRight(e, "selectionStart");
- this.setSelectionEnd(this.selectionStart);
+ changed = this._moveRight(e, "selectionStart");
+ this.selectionEnd = this.selectionStart;
+ return changed;
} else {
- this.setSelectionEnd(this.selectionEnd + this.getNumNewLinesInSelectedText());
- this.setSelectionStart(this.selectionEnd);
+ this.selectionStart = this.selectionEnd;
+ return true;
}
},
removeChars: function(e) {
diff --git a/src/mixins/itext_behavior.mixin.js b/src/mixins/itext_behavior.mixin.js
index 9c9f38d5..d6fd2334 100644
--- a/src/mixins/itext_behavior.mixin.js
+++ b/src/mixins/itext_behavior.mixin.js
@@ -157,6 +157,7 @@
* Aborts cursor animation and clears all timeouts
*/
abortCursorAnimation: function() {
+ var shouldClear = this._currentTickState || this._currentTickCompleteState;
this._currentTickState && this._currentTickState.abort();
this._currentTickCompleteState && this._currentTickCompleteState.abort();
@@ -166,15 +167,20 @@
this._currentCursorOpacity = 0;
// to clear just itext area we need to transform the context
// it may not be worth it
- this.canvas && this.canvas.clearContext(this.canvas.contextTop || this.ctx);
+ if (shouldClear) {
+ this.canvas && this.canvas.clearContext(this.canvas.contextTop || this.ctx);
+ }
+
},
/**
* Selects entire text
*/
selectAll: function() {
- this.setSelectionStart(0);
- this.setSelectionEnd(this.text.length);
+ this.selectionStart = 0;
+ this.selectionEnd = this.text.length;
+ this._fireSelectionChanged();
+ this._updateTextarea();
},
/**
@@ -305,12 +311,15 @@
* @param {Number} selectionStart Index of a character
*/
selectWord: function(selectionStart) {
+ selectionStart = selectionStart || this.selectionStart;
var newSelectionStart = this.searchWordBoundary(selectionStart, -1), /* search backwards */
- newSelectionEnd = this.searchWordBoundary(selectionStart, 1);
- /* search forward */
+ newSelectionEnd = this.searchWordBoundary(selectionStart, 1); /* search forward */
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(newSelectionEnd);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = newSelectionEnd;
+ this._fireSelectionChanged();
+ this._updateTextarea();
+ this.renderCursorOrSelection();
},
/**
@@ -318,11 +327,14 @@
* @param {Number} selectionStart Index of a character
*/
selectLine: function(selectionStart) {
+ selectionStart = selectionStart || this.selectionStart;
var newSelectionStart = this.findLineBoundaryLeft(selectionStart),
- newSelectionEnd = this.findLineBoundaryRight(selectionStart);
+ newSelectionEnd = this.findLineBoundaryRight(selectionStart);
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(newSelectionEnd);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = newSelectionEnd;
+ this._fireSelectionChanged();
+ this._updateTextarea();
},
/**
@@ -354,9 +366,8 @@
if (!this.canvas) {
return this;
}
-
- this.canvas.renderAll();
this.canvas.fire('text:editing:entered', { target: this });
+ this.canvas.renderAll();
this.initMouseMoveHandler();
return this;
},
@@ -392,13 +403,15 @@
return;
}
if (newSelectionStart > this.__selectionStartOnMouseDown) {
- this.setSelectionStart(this.__selectionStartOnMouseDown);
- this.setSelectionEnd(newSelectionStart);
+ this.selectionStart = this.__selectionStartOnMouseDown;
+ this.selectionEnd = newSelectionStart;
}
else {
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(this.__selectionStartOnMouseDown);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = this.__selectionStartOnMouseDown;
}
+ this._fireSelectionChanged();
+ this._updateTextarea();
this.renderCursorOrSelection();
},
@@ -565,7 +578,8 @@
this._removeSingleCharAndStyle(start + 1);
end--;
}
- this.setSelectionStart(start);
+ this.selectionStart = start;
+ this.selectionEnd = start;
},
_removeSingleCharAndStyle: function(index) {
@@ -588,7 +602,6 @@
if (this.selectionEnd - this.selectionStart > 1) {
this._removeCharsFromTo(this.selectionStart, this.selectionEnd);
- this.setSelectionEnd(this.selectionStart);
}
//short circuit for block paste
if (!useCopiedStyle && this.isEmptyStyles()) {
@@ -621,10 +634,11 @@
return;
}
this._updateTextarea();
- this.canvas && this.canvas.renderAll();
this.setCoords();
+ this._fireSelectionChanged();
this.fire('changed');
this.canvas && this.canvas.fire('text:changed', { target: this });
+ this.canvas && this.canvas.renderAll();
},
/**
diff --git a/src/mixins/itext_click_behavior.mixin.js b/src/mixins/itext_click_behavior.mixin.js
index 31d34a93..cf7a227f 100644
--- a/src/mixins/itext_click_behavior.mixin.js
+++ b/src/mixins/itext_click_behavior.mixin.js
@@ -103,7 +103,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
if (this.isEditing) {
this.__selectionStartOnMouseDown = this.selectionStart;
- this.initDelayedCursor(true);
+ if (this.selectionStart === this.selectionEnd) {
+ this.abortCursorAnimation();
+ }
+ this.renderCursorOrSelection();
}
});
},
@@ -130,7 +133,12 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
if (this.__lastSelected && !this.__corner) {
this.enterEditing(options.e);
- this.initDelayedCursor(true);
+ if (this.selectionStart === this.selectionEnd) {
+ this.initDelayedCursor(true);
+ }
+ else {
+ this.renderCursorOrSelection();
+ }
}
this.selected = true;
});
@@ -145,17 +153,19 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
if (e.shiftKey) {
if (newSelectionStart < this.selectionStart) {
- this.setSelectionEnd(this.selectionStart);
- this.setSelectionStart(newSelectionStart);
+ this.selectionEnd = this.selectionStart;
+ this.selectionStart = newSelectionStart;
}
else {
- this.setSelectionEnd(newSelectionStart);
+ this.selectionEnd = newSelectionStart;
}
}
else {
- this.setSelectionStart(newSelectionStart);
- this.setSelectionEnd(newSelectionStart);
+ this.selectionStart = newSelectionStart;
+ this.selectionEnd = newSelectionStart;
}
+ this._fireSelectionChanged();
+ this._updateTextarea();
},
/**
diff --git a/src/mixins/itext_key_behavior.mixin.js b/src/mixins/itext_key_behavior.mixin.js
index b5b82bd0..ffd6fc38 100644
--- a/src/mixins/itext_key_behavior.mixin.js
+++ b/src/mixins/itext_key_behavior.mixin.js
@@ -343,19 +343,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e Event object
*/
moveCursorDown: function(e) {
- this.abortCursorAnimation();
- this._currentCursorOpacity = 1;
-
- var offset = this.getDownCursorOffset(e, this._selectionDirection === 'right');
-
- if (e.shiftKey) {
- this.moveCursorDownWithShift(offset);
+ if (this.selectionStart >= this.text.length && this.selectionEnd >= this.text.length) {
+ return;
}
- else {
- this.moveCursorDownWithoutShift(offset);
- }
-
- this.initDelayedCursor();
+ this._moveCursorUpOrDown('Down', e);
},
/**
@@ -364,8 +355,9 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
moveCursorDownWithoutShift: function(offset) {
this._selectionDirection = 'right';
- this.setSelectionStart(this.selectionStart + offset);
- this.setSelectionEnd(this.selectionStart);
+ this.selectionEnd = this.selectionEnd + offset;
+ this.selectionStart = this.selectionEnd;
+ return offset !== 0;
},
/**
@@ -373,8 +365,8 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
swapSelectionPoints: function() {
var swapSel = this.selectionEnd;
- this.setSelectionEnd(this.selectionStart);
- this.setSelectionStart(swapSel);
+ this.selectionEnd = this.selectionStart;
+ this.selectionStart = swapSel;
},
/**
@@ -386,18 +378,19 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this._selectionDirection = 'right';
}
if (this._selectionDirection === 'right') {
- this.setSelectionEnd(this.selectionEnd + offset);
+ this.selectionEnd += offset;
}
else {
- this.setSelectionStart(this.selectionStart + offset);
+ this.selectionStart += offset;
}
if (this.selectionEnd < this.selectionStart && this._selectionDirection === 'left') {
this.swapSelectionPoints();
this._selectionDirection = 'right';
}
if (this.selectionEnd > this.text.length) {
- this.setSelectionEnd(this.text.length);
+ this.selectionEnd = this.text.length;
}
+ return offset !== 0;
},
/**
@@ -479,19 +472,34 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e Event object
*/
moveCursorUp: function(e) {
+ if (this.selectionStart === 0 && this.selectionEnd === 0) {
+ return;
+ }
+ this._moveCursorUpOrDown('Up', e);
+ },
- this.abortCursorAnimation();
- this._currentCursorOpacity = 1;
-
- var offset = this.getUpCursorOffset(e, this._selectionDirection === 'right');
+ /**
+ * Moves cursor up or down, fires the events
+ * @param {String} direction 'Up' or 'Down'
+ * @param {Event} e Event object
+ */
+ _moveCursorUpOrDown: function(direction, e) {
+ var action = 'get' + direction + 'CursorOffset',
+ moveAction = 'moveCursor' + direction,
+ offset = this[action](e, this._selectionDirection === 'right');
if (e.shiftKey) {
- this.moveCursorUpWithShift(offset);
+ moveAction += 'WithShift';
}
else {
- this.moveCursorUpWithoutShift(offset);
+ moveAction += 'WithoutShift';
+ }
+ if (this[moveAction](offset)) {
+ this.abortCursorAnimation();
+ this._currentCursorOpacity = 1;
+ this.initDelayedCursor();
+ this._fireSelectionChanged();
+ this._updateTextarea();
}
-
- this.initDelayedCursor();
},
/**
@@ -503,15 +511,16 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
this._selectionDirection = 'left';
}
if (this._selectionDirection === 'right') {
- this.setSelectionEnd(this.selectionEnd - offset);
+ this.selectionEnd -= offset;
}
else {
- this.setSelectionStart(this.selectionStart - offset);
+ this.selectionStart -= offset;
}
if (this.selectionEnd < this.selectionStart && this._selectionDirection === 'right') {
this.swapSelectionPoints();
this._selectionDirection = 'left';
}
+ return offset !== 0;
},
/**
@@ -519,12 +528,10 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Number} offset
*/
moveCursorUpWithoutShift: function(offset) {
- if (this.selectionStart === this.selectionEnd) {
- this.setSelectionStart(this.selectionStart - offset);
- }
- this.setSelectionEnd(this.selectionStart);
-
this._selectionDirection = 'left';
+ this.selectionStart -= offset;
+ this.selectionEnd = this.selectionStart;
+ return offset !== 0;
},
/**
@@ -535,33 +542,28 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
if (this.selectionStart === 0 && this.selectionEnd === 0) {
return;
}
-
- this.abortCursorAnimation();
- this._currentCursorOpacity = 1;
-
- if (e.shiftKey) {
- this.moveCursorLeftWithShift(e);
- }
- else {
- this.moveCursorLeftWithoutShift(e);
- }
-
- this.initDelayedCursor();
+ this._moveCursorLeftOrRight('Left', e);
},
/**
* @private
+ * @return {Boolean} true if a change happened
*/
_move: function(e, prop, direction) {
- var propMethod = (prop === 'selectionStart' ? 'setSelectionStart' : 'setSelectionEnd');
+ var newValue;
if (e.altKey) {
- this[propMethod](this['findWordBoundary' + direction](this[prop]));
+ newValue = this['findWordBoundary' + direction](this[prop]);
}
else if (e.metaKey || e.keyCode === 35 || e.keyCode === 36 ) {
- this[propMethod](this['findLineBoundary' + direction](this[prop]));
+ newValue = this['findLineBoundary' + direction](this[prop]);
}
else {
- this[propMethod](this[prop] + (direction === 'Left' ? -1 : 1));
+ this[prop] += direction === 'Left' ? -1 : 1;
+ return true;
+ }
+ if (typeof newValue !== undefined && this[prop] !== newValue) {
+ this[prop] = newValue;
+ return true;
}
},
@@ -569,14 +571,14 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @private
*/
_moveLeft: function(e, prop) {
- this._move(e, prop, 'Left');
+ return this._move(e, prop, 'Left');
},
/**
* @private
*/
_moveRight: function(e, prop) {
- this._move(e, prop, 'Right');
+ return this._move(e, prop, 'Right');
},
/**
@@ -584,14 +586,16 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e
*/
moveCursorLeftWithoutShift: function(e) {
+ var change = false;
this._selectionDirection = 'left';
// only move cursor when there is no selection,
// otherwise we discard it, and leave cursor on same place
- if (this.selectionEnd === this.selectionStart) {
- this._moveLeft(e, 'selectionStart');
+ if (this.selectionEnd === this.selectionStart && this.selectionStart !== 0) {
+ change = this._moveLeft(e, 'selectionStart');
}
- this.setSelectionEnd(this.selectionStart);
+ this.selectionEnd = this.selectionStart;
+ return change;
},
/**
@@ -600,11 +604,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
moveCursorLeftWithShift: function(e) {
if (this._selectionDirection === 'right' && this.selectionStart !== this.selectionEnd) {
- this._moveLeft(e, 'selectionEnd');
+ return this._moveLeft(e, 'selectionEnd');
}
- else {
+ else if (this.selectionStart !== 0){
this._selectionDirection = 'left';
- this._moveLeft(e, 'selectionStart');
+ return this._moveLeft(e, 'selectionStart');
}
},
@@ -616,18 +620,30 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
if (this.selectionStart >= this.text.length && this.selectionEnd >= this.text.length) {
return;
}
+ this._moveCursorLeftOrRight('Right', e);
+ },
- this.abortCursorAnimation();
+ /**
+ * Moves cursor right or Left, fires event
+ * @param {String} direction 'Left', 'Right'
+ * @param {Event} e Event object
+ */
+ _moveCursorLeftOrRight: function(direction, e) {
+ var actionName = 'moveCursor' + direction + 'With';
this._currentCursorOpacity = 1;
if (e.shiftKey) {
- this.moveCursorRightWithShift(e);
+ actionName += 'Shift';
}
else {
- this.moveCursorRightWithoutShift(e);
+ actionName += 'outShift';
+ }
+ if (this[actionName](e)) {
+ this.abortCursorAnimation();
+ this.initDelayedCursor();
+ this._fireSelectionChanged();
+ this._updateTextarea();
}
-
- this.initDelayedCursor();
},
/**
@@ -636,11 +652,11 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
*/
moveCursorRightWithShift: function(e) {
if (this._selectionDirection === 'left' && this.selectionStart !== this.selectionEnd) {
- this._moveRight(e, 'selectionStart');
+ return this._moveRight(e, 'selectionStart');
}
- else {
+ else if (this.selectionEnd !== this.text.length) {
this._selectionDirection = 'right';
- this._moveRight(e, 'selectionEnd');
+ return this._moveRight(e, 'selectionEnd');
}
},
@@ -649,15 +665,17 @@ fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.protot
* @param {Event} e Event object
*/
moveCursorRightWithoutShift: function(e) {
+ var changed = false;
this._selectionDirection = 'right';
if (this.selectionStart === this.selectionEnd) {
- this._moveRight(e, 'selectionStart');
- this.setSelectionEnd(this.selectionStart);
+ changed = this._moveRight(e, 'selectionStart');
+ this.selectionEnd = this.selectionStart;
+ return changed;
}
else {
- this.setSelectionEnd(this.selectionEnd + this.getNumNewLinesInSelectedText());
- this.setSelectionStart(this.selectionEnd);
+ this.selectionStart = this.selectionEnd;
+ return true;
}
},
diff --git a/src/shapes/itext.class.js b/src/shapes/itext.class.js
index 7bf4f41c..472ed44e 100644
--- a/src/shapes/itext.class.js
+++ b/src/shapes/itext.class.js
@@ -213,12 +213,7 @@
*/
setSelectionStart: function(index) {
index = Math.max(index, 0);
- if (this.selectionStart !== index) {
- this.fire('selection:changed');
- this.canvas && this.canvas.fire('text:selection:changed', { target: this });
- this.selectionStart = index;
- }
- this._updateTextarea();
+ this._updateAndFire('selectionStart', index);
},
/**
@@ -227,14 +222,31 @@
*/
setSelectionEnd: function(index) {
index = Math.min(index, this.text.length);
- if (this.selectionEnd !== index) {
- this.fire('selection:changed');
- this.canvas && this.canvas.fire('text:selection:changed', { target: this });
- this.selectionEnd = index;
+ this._updateAndFire('selectionEnd', index);
+ },
+
+ /**
+ * @private
+ * @param {String} property 'selectionStart' or 'selectionEnd'
+ * @param {Number} index new position of property
+ */
+ _updateAndFire: function(property, index) {
+ if (this[property] !== index) {
+ this._fireSelectionChanged();
+ this[property] = index;
}
this._updateTextarea();
},
+ /**
+ * Fires the even of selection changed
+ * @private
+ */
+ _fireSelectionChanged: function() {
+ this.fire('selection:changed');
+ this.canvas && this.canvas.fire('text:selection:changed', { target: this });
+ },
+
/**
* Gets style of a current selection/cursor (at the start position)
* @param {Number} [startIndex] Start index to get styles at
@@ -313,7 +325,6 @@
if (!this.active || !this.isEditing) {
return;
}
-
var chars = this.text.split(''),
boundaries, ctx;
diff --git a/test.js b/test.js
index 700d04e8..ea3f4660 100644
--- a/test.js
+++ b/test.js
@@ -36,7 +36,8 @@ testrunner.run({
'./test/unit/object_interactivity.js',
'./test/unit/object_geometry.js',
'./test/unit/object_origin.js',
- './test/unit/itext.js'
+ './test/unit/itext.js',
+ './test/unit/itext_key_behaviour.js'
]
}, function(err, report) {
if (err) {
diff --git a/test/unit/itext_key_behaviour.js b/test/unit/itext_key_behaviour.js
new file mode 100644
index 00000000..53ee7b05
--- /dev/null
+++ b/test/unit/itext_key_behaviour.js
@@ -0,0 +1,228 @@
+(function(){
+ test('event selection:changed firing', function() {
+ var iText = new fabric.IText('test need some word\nsecond line'),
+ selection = 0;
+
+ function countSelectionChange() {
+ selection++;
+ }
+
+ iText.on('selection:changed', countSelectionChange);
+
+ iText.enterEditing();
+ equal(selection, 0, 'should not fire on enter edit');
+
+ iText.selectAll();
+ equal(selection, 1, 'should fire once on selectAll');
+ equal(iText.selectionStart, 0, 'should start from 0');
+ equal(iText.selectionEnd, 31, 'should end at end of text');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.selectWord();
+ equal(selection, 1, 'should fire once on selectWord');
+ equal(iText.selectionStart, 0, 'should start at word start');
+ equal(iText.selectionEnd, 4, 'should end at word end');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.selectLine();
+ equal(selection, 1, 'should fire once on selectLine');
+ equal(iText.selectionStart, 0, 'should start at line start');
+ equal(iText.selectionEnd, 19, 'should end at line end');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.moveCursorLeft({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorLeft');
+ equal(iText.selectionStart, 1, 'should be 1 less than 2');
+ equal(iText.selectionEnd, 1, 'should be 1 less than 2');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.moveCursorRight({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorRight');
+ equal(iText.selectionStart, 3, 'should be 1 more than 2');
+ equal(iText.selectionEnd, 3, 'should be 1 more than 2');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.moveCursorDown({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorDown');
+ equal(iText.selectionStart, 22, 'should be on second line');
+ equal(iText.selectionEnd, 22, 'should be on second line');
+ iText.moveCursorDown({ shiftKey: false});
+ equal(selection, 2, 'should fire once on moveCursorDown');
+ equal(iText.selectionStart, 31, 'should be at end of text');
+ equal(iText.selectionEnd, 31, 'should be at end of text');
+ selection = 0;
+
+ iText.selectionStart = 22;
+ iText.selectionEnd = 22;
+ iText.moveCursorUp({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorUp');
+ equal(iText.selectionStart, 2, 'should be back to first line');
+ equal(iText.selectionEnd, 2, 'should be back on first line');
+ iText.moveCursorUp({ shiftKey: false});
+ equal(selection, 2, 'should fire once on moveCursorUp');
+ equal(iText.selectionStart, 0, 'should be back to first line');
+ equal(iText.selectionEnd, 0, 'should be back on first line');
+ selection = 0;
+
+ iText.selectionStart = 0;
+ iText.selectionEnd = 0;
+ iText.moveCursorLeft({ shiftKey: false});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 0, 'should not move');
+ equal(iText.selectionEnd, 0, 'should not move');
+ iText.moveCursorUp({ shiftKey: false});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 0, 'should not move');
+ equal(iText.selectionEnd, 0, 'should not move');
+ selection = 0;
+
+ iText.selectionStart = 31;
+ iText.selectionEnd = 31;
+ iText.moveCursorRight({ shiftKey: false});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 31, 'should not move');
+ equal(iText.selectionEnd, 31, 'should not move');
+ iText.moveCursorDown({ shiftKey: false});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 31, 'should not move');
+ equal(iText.selectionEnd, 31, 'should not move');
+ selection = 0;
+
+ iText.selectionStart = 28;
+ iText.selectionEnd = 31;
+ iText.moveCursorUp({ shiftKey: false});
+ equal(selection, 1, 'should fire');
+ equal(iText.selectionStart, 9, 'should move to upper line');
+ equal(iText.selectionEnd, 9, 'should move to upper line');
+ selection = 0;
+
+ iText.selectionStart = 1;
+ iText.selectionEnd = 4;
+ iText.moveCursorDown({ shiftKey: false});
+ equal(selection, 1, 'should fire');
+ equal(iText.selectionStart, 24, 'should move to down line');
+ equal(iText.selectionEnd, 24, 'should move to down line');
+ selection = 0;
+
+ iText.selectionStart = 0;
+ iText.selectionEnd = 0;
+ iText.insertChars('hello');
+ equal(selection, 1, 'should fire once on insert multiple chars');
+ equal(iText.selectionStart, 5, 'should be at end of text inserted');
+ equal(iText.selectionEnd, 5, 'should be at end of text inserted');
+ });
+
+ test('moving cursor with shift', function() {
+ var iText = new fabric.IText('test need some word\nsecond line'),
+ selection = 0;
+
+ function countSelectionChange() {
+ selection++;
+ }
+
+ iText.on('selection:changed', countSelectionChange);
+
+ iText.enterEditing();
+ equal(selection, 0, 'should not fire on enter edit');
+
+ iText.selectAll();
+ equal(selection, 1, 'should fire once on selectAll');
+ equal(iText.selectionStart, 0, 'should start from 0');
+ equal(iText.selectionEnd, 31, 'should end at end of text');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.selectWord();
+ equal(selection, 1, 'should fire once on selectWord');
+ equal(iText.selectionStart, 0, 'should start at word start');
+ equal(iText.selectionEnd, 4, 'should end at word end');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.selectLine();
+ equal(selection, 1, 'should fire once on selectLine');
+ equal(iText.selectionStart, 0, 'should start at line start');
+ equal(iText.selectionEnd, 19, 'should end at line end');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.moveCursorLeft({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorLeft');
+ equal(iText.selectionStart, 1, 'should be 1 less than 2');
+ equal(iText.selectionEnd, 1, 'should be 1 less than 2');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.moveCursorRight({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorRight');
+ equal(iText.selectionStart, 3, 'should be 1 more than 2');
+ equal(iText.selectionEnd, 3, 'should be 1 more than 2');
+ selection = 0;
+
+ iText.selectionStart = 2;
+ iText.selectionEnd = 2;
+ iText.moveCursorDown({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorDown');
+ equal(iText.selectionStart, 22, 'should be on second line');
+ equal(iText.selectionEnd, 22, 'should be on second line');
+ iText.moveCursorDown({ shiftKey: false});
+ equal(selection, 2, 'should fire once on moveCursorDown');
+ equal(iText.selectionStart, 31, 'should be at end of text');
+ equal(iText.selectionEnd, 31, 'should be at end of text');
+ selection = 0;
+
+ iText.selectionStart = 22;
+ iText.selectionEnd = 22;
+ iText.moveCursorUp({ shiftKey: false});
+ equal(selection, 1, 'should fire once on moveCursorUp');
+ equal(iText.selectionStart, 2, 'should be back to first line');
+ equal(iText.selectionEnd, 2, 'should be back on first line');
+ iText.moveCursorUp({ shiftKey: false});
+ equal(selection, 2, 'should fire once on moveCursorUp');
+ equal(iText.selectionStart, 0, 'should be back to first line');
+ equal(iText.selectionEnd, 0, 'should be back on first line');
+ selection = 0;
+
+ iText.selectionStart = 0;
+ iText.selectionEnd = 1;
+ iText._selectionDirection = 'left';
+ iText.moveCursorLeft({ shiftKey: true});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 0, 'should not move');
+ equal(iText.selectionEnd, 1, 'should not move');
+ iText.moveCursorUp({ shiftKey: true});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 0, 'should not move');
+ equal(iText.selectionEnd, 1, 'should not move');
+ selection = 0;
+
+
+ iText.selectionStart = 30;
+ iText.selectionEnd = 31;
+ iText._selectionDirection = 'right';
+ iText.moveCursorRight({ shiftKey: true});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 30, 'should not move');
+ equal(iText.selectionEnd, 31, 'should not move');
+ iText.moveCursorDown({ shiftKey: true});
+ equal(selection, 0, 'should not fire with no change');
+ equal(iText.selectionStart, 30, 'should not move');
+ equal(iText.selectionEnd, 31, 'should not move');
+ selection = 0;
+ });
+
+})();
diff --git a/test/unit/object.js b/test/unit/object.js
index 8f09e4a5..8696acde 100644
--- a/test/unit/object.js
+++ b/test/unit/object.js
@@ -1440,7 +1440,6 @@ test('toDataURL & reference to canvas', function() {
group.scaleY = 2;
object.group = group;
var objectScale = object.getObjectScaling();
- console.log(objectScale);
deepEqual(objectScale, {
scaleX: object.scaleX * group.scaleX,
scaleY: object.scaleY * group.scaleY