fabric-pure-browser
Version:
Fabric.js package with no node-specific dependencies (node-canvas, jsdom). The project is published once a day (in case if a new version appears) from 'master' branch of https://github.com/fabricjs/fabric.js repository. You can keep original imports in
270 lines (242 loc) • 7.82 kB
JavaScript
fabric.util.object.extend(fabric.IText.prototype, /** @lends fabric.IText.prototype */ {
/**
* Initializes "dbclick" event handler
*/
initDoubleClickSimulation: function() {
// for double click
this.__lastClickTime = +new Date();
// for triple click
this.__lastLastClickTime = +new Date();
this.__lastPointer = { };
this.on('mousedown', this.onMouseDown);
},
/**
* Default event handler to simulate triple click
* @private
*/
onMouseDown: function(options) {
if (!this.canvas) {
return;
}
this.__newClickTime = +new Date();
var newPointer = options.pointer;
if (this.isTripleClick(newPointer)) {
this.fire('tripleclick', options);
this._stopEvent(options.e);
}
this.__lastLastClickTime = this.__lastClickTime;
this.__lastClickTime = this.__newClickTime;
this.__lastPointer = newPointer;
this.__lastIsEditing = this.isEditing;
this.__lastSelected = this.selected;
},
isTripleClick: function(newPointer) {
return this.__newClickTime - this.__lastClickTime < 500 &&
this.__lastClickTime - this.__lastLastClickTime < 500 &&
this.__lastPointer.x === newPointer.x &&
this.__lastPointer.y === newPointer.y;
},
/**
* @private
*/
_stopEvent: function(e) {
e.preventDefault && e.preventDefault();
e.stopPropagation && e.stopPropagation();
},
/**
* Initializes event handlers related to cursor or selection
*/
initCursorSelectionHandlers: function() {
this.initMousedownHandler();
this.initMouseupHandler();
this.initClicks();
},
/**
* Default handler for double click, select a word
*/
doubleClickHandler: function(options) {
if (!this.isEditing) {
return;
}
this.selectWord(this.getSelectionStartFromPointer(options.e));
},
/**
* Default handler for triple click, select a line
*/
tripleClickHandler: function(options) {
if (!this.isEditing) {
return;
}
this.selectLine(this.getSelectionStartFromPointer(options.e));
},
/**
* Initializes double and triple click event handlers
*/
initClicks: function() {
this.on('mousedblclick', this.doubleClickHandler);
this.on('tripleclick', this.tripleClickHandler);
},
/**
* Default event handler for the basic functionalities needed on _mouseDown
* can be overridden to do something different.
* Scope of this implementation is: find the click position, set selectionStart
* find selectionEnd, initialize the drawing of either cursor or selection area
*/
_mouseDownHandler: function(options) {
if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) {
return;
}
this.__isMousedown = true;
if (this.selected) {
this.setCursorByClick(options.e);
}
if (this.isEditing) {
this.__selectionStartOnMouseDown = this.selectionStart;
if (this.selectionStart === this.selectionEnd) {
this.abortCursorAnimation();
}
this.renderCursorOrSelection();
}
},
/**
* Default event handler for the basic functionalities needed on mousedown:before
* can be overridden to do something different.
* Scope of this implementation is: verify the object is already selected when mousing down
*/
_mouseDownHandlerBefore: function(options) {
if (!this.canvas || !this.editable || (options.e.button && options.e.button !== 1)) {
return;
}
// we want to avoid that an object that was selected and then becomes unselectable,
// may trigger editing mode in some way.
this.selected = this === this.canvas._activeObject;
},
/**
* Initializes "mousedown" event handler
*/
initMousedownHandler: function() {
this.on('mousedown', this._mouseDownHandler);
this.on('mousedown:before', this._mouseDownHandlerBefore);
},
/**
* Initializes "mouseup" event handler
*/
initMouseupHandler: function() {
this.on('mouseup', this.mouseUpHandler);
},
/**
* standard hander for mouse up, overridable
* @private
*/
mouseUpHandler: function(options) {
this.__isMousedown = false;
if (!this.editable || this.group ||
(options.transform && options.transform.actionPerformed) ||
(options.e.button && options.e.button !== 1)) {
return;
}
if (this.canvas) {
var currentActive = this.canvas._activeObject;
if (currentActive && currentActive !== this) {
// avoid running this logic when there is an active object
// this because is possible with shift click and fast clicks,
// to rapidly deselect and reselect this object and trigger an enterEdit
return;
}
}
if (this.__lastSelected && !this.__corner) {
this.selected = false;
this.__lastSelected = false;
this.enterEditing(options.e);
if (this.selectionStart === this.selectionEnd) {
this.initDelayedCursor(true);
}
else {
this.renderCursorOrSelection();
}
}
else {
this.selected = true;
}
},
/**
* Changes cursor location in a text depending on passed pointer (x/y) object
* @param {Event} e Event object
*/
setCursorByClick: function(e) {
var newSelection = this.getSelectionStartFromPointer(e),
start = this.selectionStart, end = this.selectionEnd;
if (e.shiftKey) {
this.setSelectionStartEndWithShift(start, end, newSelection);
}
else {
this.selectionStart = newSelection;
this.selectionEnd = newSelection;
}
if (this.isEditing) {
this._fireSelectionChanged();
this._updateTextarea();
}
},
/**
* Returns index of a character corresponding to where an object was clicked
* @param {Event} e Event object
* @return {Number} Index of a character
*/
getSelectionStartFromPointer: function(e) {
var mouseOffset = this.getLocalPointer(e),
prevWidth = 0,
width = 0,
height = 0,
charIndex = 0,
lineIndex = 0,
lineLeftOffset,
line;
for (var i = 0, len = this._textLines.length; i < len; i++) {
if (height <= mouseOffset.y) {
height += this.getHeightOfLine(i) * this.scaleY;
lineIndex = i;
if (i > 0) {
charIndex += this._textLines[i - 1].length + this.missingNewlineOffset(i - 1);
}
}
else {
break;
}
}
lineLeftOffset = this._getLineLeftOffset(lineIndex);
width = lineLeftOffset * this.scaleX;
line = this._textLines[lineIndex];
for (var j = 0, jlen = line.length; j < jlen; j++) {
prevWidth = width;
// i removed something about flipX here, check.
width += this.__charBounds[lineIndex][j].kernedWidth * this.scaleX;
if (width <= mouseOffset.x) {
charIndex++;
}
else {
break;
}
}
return this._getNewSelectionStartFromOffset(mouseOffset, prevWidth, width, charIndex, jlen);
},
/**
* @private
*/
_getNewSelectionStartFromOffset: function(mouseOffset, prevWidth, width, index, jlen) {
// we need Math.abs because when width is after the last char, the offset is given as 1, while is 0
var distanceBtwLastCharAndCursor = mouseOffset.x - prevWidth,
distanceBtwNextCharAndCursor = width - mouseOffset.x,
offset = distanceBtwNextCharAndCursor > distanceBtwLastCharAndCursor ||
distanceBtwNextCharAndCursor < 0 ? 0 : 1,
newSelectionStart = index + offset;
// if object is horizontally flipped, mirror cursor location from the end
if (this.flipX) {
newSelectionStart = jlen - newSelectionStart;
}
if (newSelectionStart > this._text.length) {
newSelectionStart = this._text.length;
}
return newSelectionStart;
}
});