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
998 lines (914 loc) • 33 kB
JavaScript
(function() {
var addListener = fabric.util.addListener,
removeListener = fabric.util.removeListener,
RIGHT_CLICK = 3, MIDDLE_CLICK = 2, LEFT_CLICK = 1,
addEventOptions = { passive: false };
function checkClick(e, value) {
return e.button && (e.button === value - 1);
}
fabric.util.object.extend(fabric.Canvas.prototype, /** @lends fabric.Canvas.prototype */ {
/**
* Contains the id of the touch event that owns the fabric transform
* @type Number
* @private
*/
mainTouchId: null,
/**
* Adds mouse listeners to canvas
* @private
*/
_initEventListeners: function () {
// in case we initialized the class twice. This should not happen normally
// but in some kind of applications where the canvas element may be changed
// this is a workaround to having double listeners.
this.removeListeners();
this._bindEvents();
this.addOrRemove(addListener, 'add');
},
/**
* return an event prefix pointer or mouse.
* @private
*/
_getEventPrefix: function () {
return this.enablePointerEvents ? 'pointer' : 'mouse';
},
addOrRemove: function(functor, eventjsFunctor) {
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
functor(fabric.window, 'resize', this._onResize);
functor(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
functor(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
functor(canvasElement, eventTypePrefix + 'out', this._onMouseOut);
functor(canvasElement, eventTypePrefix + 'enter', this._onMouseEnter);
functor(canvasElement, 'wheel', this._onMouseWheel);
functor(canvasElement, 'contextmenu', this._onContextMenu);
functor(canvasElement, 'dblclick', this._onDoubleClick);
functor(canvasElement, 'dragover', this._onDragOver);
functor(canvasElement, 'dragenter', this._onDragEnter);
functor(canvasElement, 'dragleave', this._onDragLeave);
functor(canvasElement, 'drop', this._onDrop);
if (!this.enablePointerEvents) {
functor(canvasElement, 'touchstart', this._onTouchStart, addEventOptions);
}
if (typeof eventjs !== 'undefined' && eventjsFunctor in eventjs) {
eventjs[eventjsFunctor](canvasElement, 'gesture', this._onGesture);
eventjs[eventjsFunctor](canvasElement, 'drag', this._onDrag);
eventjs[eventjsFunctor](canvasElement, 'orientation', this._onOrientationChange);
eventjs[eventjsFunctor](canvasElement, 'shake', this._onShake);
eventjs[eventjsFunctor](canvasElement, 'longpress', this._onLongPress);
}
},
/**
* Removes all event listeners
*/
removeListeners: function() {
this.addOrRemove(removeListener, 'remove');
// if you dispose on a mouseDown, before mouse up, you need to clean document to...
var eventTypePrefix = this._getEventPrefix();
removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
},
/**
* @private
*/
_bindEvents: function() {
if (this.eventsBound) {
// for any reason we pass here twice we do not want to bind events twice.
return;
}
this._onMouseDown = this._onMouseDown.bind(this);
this._onTouchStart = this._onTouchStart.bind(this);
this._onMouseMove = this._onMouseMove.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
this._onTouchEnd = this._onTouchEnd.bind(this);
this._onResize = this._onResize.bind(this);
this._onGesture = this._onGesture.bind(this);
this._onDrag = this._onDrag.bind(this);
this._onShake = this._onShake.bind(this);
this._onLongPress = this._onLongPress.bind(this);
this._onOrientationChange = this._onOrientationChange.bind(this);
this._onMouseWheel = this._onMouseWheel.bind(this);
this._onMouseOut = this._onMouseOut.bind(this);
this._onMouseEnter = this._onMouseEnter.bind(this);
this._onContextMenu = this._onContextMenu.bind(this);
this._onDoubleClick = this._onDoubleClick.bind(this);
this._onDragOver = this._onDragOver.bind(this);
this._onDragEnter = this._simpleEventHandler.bind(this, 'dragenter');
this._onDragLeave = this._simpleEventHandler.bind(this, 'dragleave');
this._onDrop = this._simpleEventHandler.bind(this, 'drop');
this.eventsBound = true;
},
/**
* @private
* @param {Event} [e] Event object fired on Event.js gesture
* @param {Event} [self] Inner Event object
*/
_onGesture: function(e, self) {
this.__onTransformGesture && this.__onTransformGesture(e, self);
},
/**
* @private
* @param {Event} [e] Event object fired on Event.js drag
* @param {Event} [self] Inner Event object
*/
_onDrag: function(e, self) {
this.__onDrag && this.__onDrag(e, self);
},
/**
* @private
* @param {Event} [e] Event object fired on wheel event
*/
_onMouseWheel: function(e) {
this.__onMouseWheel(e);
},
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onMouseOut: function(e) {
var target = this._hoveredTarget;
this.fire('mouse:out', { target: target, e: e });
this._hoveredTarget = null;
target && target.fire('mouseout', { e: e });
var _this = this;
this._hoveredTargets.forEach(function(_target){
_this.fire('mouse:out', { target: target, e: e });
_target && target.fire('mouseout', { e: e });
});
this._hoveredTargets = [];
if (this._iTextInstances) {
this._iTextInstances.forEach(function(obj) {
if (obj.isEditing) {
obj.hiddenTextarea.focus();
}
});
}
},
/**
* @private
* @param {Event} e Event object fired on mouseenter
*/
_onMouseEnter: function(e) {
// This find target and consequent 'mouse:over' is used to
// clear old instances on hovered target.
// calling findTarget has the side effect of killing target.__corner.
// as a short term fix we are not firing this if we are currently transforming.
// as a long term fix we need to separate the action of finding a target with the
// side effects we added to it.
if (!this.currentTransform && !this.findTarget(e)) {
this.fire('mouse:over', { target: null, e: e });
this._hoveredTarget = null;
this._hoveredTargets = [];
}
},
/**
* @private
* @param {Event} [e] Event object fired on Event.js orientation change
* @param {Event} [self] Inner Event object
*/
_onOrientationChange: function(e, self) {
this.__onOrientationChange && this.__onOrientationChange(e, self);
},
/**
* @private
* @param {Event} [e] Event object fired on Event.js shake
* @param {Event} [self] Inner Event object
*/
_onShake: function(e, self) {
this.__onShake && this.__onShake(e, self);
},
/**
* @private
* @param {Event} [e] Event object fired on Event.js shake
* @param {Event} [self] Inner Event object
*/
_onLongPress: function(e, self) {
this.__onLongPress && this.__onLongPress(e, self);
},
/**
* prevent default to allow drop event to be fired
* @private
* @param {Event} [e] Event object fired on Event.js shake
*/
_onDragOver: function(e) {
e.preventDefault();
var target = this._simpleEventHandler('dragover', e);
this._fireEnterLeaveEvents(target, e);
},
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onContextMenu: function (e) {
if (this.stopContextMenu) {
e.stopPropagation();
e.preventDefault();
}
return false;
},
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onDoubleClick: function (e) {
this._cacheTransformEventData(e);
this._handleEvent(e, 'dblclick');
this._resetTransformEventData(e);
},
/**
* Return a the id of an event.
* returns either the pointerId or the identifier or 0 for the mouse event
* @private
* @param {Event} evt Event object
*/
getPointerId: function(evt) {
var changedTouches = evt.changedTouches;
if (changedTouches) {
return changedTouches[0] && changedTouches[0].identifier;
}
if (this.enablePointerEvents) {
return evt.pointerId;
}
return -1;
},
/**
* Determines if an event has the id of the event that is considered main
* @private
* @param {evt} event Event object
*/
_isMainEvent: function(evt) {
if (evt.isPrimary === true) {
return true;
}
if (evt.isPrimary === false) {
return false;
}
if (evt.type === 'touchend' && evt.touches.length === 0) {
return true;
}
if (evt.changedTouches) {
return evt.changedTouches[0].identifier === this.mainTouchId;
}
return true;
},
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onTouchStart: function(e) {
e.preventDefault();
if (this.mainTouchId === null) {
this.mainTouchId = this.getPointerId(e);
}
this.__onMouseDown(e);
this._resetTransformEventData();
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
addListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
addListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
// Unbind mousedown to prevent double triggers from touch devices
removeListener(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
},
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onMouseDown: function (e) {
this.__onMouseDown(e);
this._resetTransformEventData();
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
removeListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
addListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
addListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
},
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onTouchEnd: function(e) {
if (e.touches.length > 0) {
// if there are still touches stop here
return;
}
this.__onMouseUp(e);
this._resetTransformEventData();
this.mainTouchId = null;
var eventTypePrefix = this._getEventPrefix();
removeListener(fabric.document, 'touchend', this._onTouchEnd, addEventOptions);
removeListener(fabric.document, 'touchmove', this._onMouseMove, addEventOptions);
var _this = this;
if (this._willAddMouseDown) {
clearTimeout(this._willAddMouseDown);
}
this._willAddMouseDown = setTimeout(function() {
// Wait 400ms before rebinding mousedown to prevent double triggers
// from touch devices
addListener(_this.upperCanvasEl, eventTypePrefix + 'down', _this._onMouseDown);
_this._willAddMouseDown = 0;
}, 400);
},
/**
* @private
* @param {Event} e Event object fired on mouseup
*/
_onMouseUp: function (e) {
this.__onMouseUp(e);
this._resetTransformEventData();
var canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
if (this._isMainEvent(e)) {
removeListener(fabric.document, eventTypePrefix + 'up', this._onMouseUp);
removeListener(fabric.document, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
addListener(canvasElement, eventTypePrefix + 'move', this._onMouseMove, addEventOptions);
}
},
/**
* @private
* @param {Event} e Event object fired on mousemove
*/
_onMouseMove: function (e) {
!this.allowTouchScrolling && e.preventDefault && e.preventDefault();
this.__onMouseMove(e);
},
/**
* @private
*/
_onResize: function () {
this.calcOffset();
},
/**
* Decides whether the canvas should be redrawn in mouseup and mousedown events.
* @private
* @param {Object} target
*/
_shouldRender: function(target) {
var activeObject = this._activeObject;
if (
!!activeObject !== !!target ||
(activeObject && target && (activeObject !== target))
) {
// this covers: switch of target, from target to no target, selection of target
// multiSelection with key and mouse
return true;
}
else if (activeObject && activeObject.isEditing) {
// if we mouse up/down over a editing textbox a cursor change,
// there is no need to re render
return false;
}
return false;
},
/**
* Method that defines the actions when mouse is released on canvas.
* The method resets the currentTransform parameters, store the image corner
* position in the image object and render the canvas on top.
* @private
* @param {Event} e Event object fired on mouseup
*/
__onMouseUp: function (e) {
var target, transform = this._currentTransform,
groupSelector = this._groupSelector, shouldRender = false,
isClick = (!groupSelector || (groupSelector.left === 0 && groupSelector.top === 0));
this._cacheTransformEventData(e);
target = this._target;
this._handleEvent(e, 'up:before');
// if right/middle click just fire events and return
// target undefined will make the _handleEvent search the target
if (checkClick(e, RIGHT_CLICK)) {
if (this.fireRightClick) {
this._handleEvent(e, 'up', RIGHT_CLICK, isClick);
}
return;
}
if (checkClick(e, MIDDLE_CLICK)) {
if (this.fireMiddleClick) {
this._handleEvent(e, 'up', MIDDLE_CLICK, isClick);
}
this._resetTransformEventData();
return;
}
if (this.isDrawingMode && this._isCurrentlyDrawing) {
this._onMouseUpInDrawingMode(e);
return;
}
if (!this._isMainEvent(e)) {
return;
}
if (transform) {
this._finalizeCurrentTransform(e);
shouldRender = transform.actionPerformed;
}
if (!isClick) {
this._maybeGroupObjects(e);
shouldRender || (shouldRender = this._shouldRender(target));
}
if (target) {
var corner = target._findTargetCorner(
this.getPointer(e, true),
fabric.util.isTouchEvent(e)
);
var control = target.controls[corner],
mouseUpHandler = control && control.getMouseUpHandler(e, target, control);
if (mouseUpHandler) {
mouseUpHandler(e, target, control);
}
target.isMoving = false;
}
this._setCursorFromEvent(e, target);
this._handleEvent(e, 'up', LEFT_CLICK, isClick);
this._groupSelector = null;
this._currentTransform = null;
// reset the target information about which corner is selected
target && (target.__corner = 0);
if (shouldRender) {
this.requestRenderAll();
}
else if (!isClick) {
this.renderTop();
}
},
/**
* @private
* Handle event firing for target and subtargets
* @param {Event} e event from mouse
* @param {String} eventType event to fire (up, down or move)
* @return {Fabric.Object} target return the the target found, for internal reasons.
*/
_simpleEventHandler: function(eventType, e) {
var target = this.findTarget(e),
targets = this.targets,
options = {
e: e,
target: target,
subTargets: targets,
};
this.fire(eventType, options);
target && target.fire(eventType, options);
if (!targets) {
return target;
}
for (var i = 0; i < targets.length; i++) {
targets[i].fire(eventType, options);
}
return target;
},
/**
* @private
* Handle event firing for target and subtargets
* @param {Event} e event from mouse
* @param {String} eventType event to fire (up, down or move)
* @param {fabric.Object} targetObj receiving event
* @param {Number} [button] button used in the event 1 = left, 2 = middle, 3 = right
* @param {Boolean} isClick for left button only, indicates that the mouse up happened without move.
*/
_handleEvent: function(e, eventType, button, isClick) {
var target = this._target,
targets = this.targets || [],
options = {
e: e,
target: target,
subTargets: targets,
button: button || LEFT_CLICK,
isClick: isClick || false,
pointer: this._pointer,
absolutePointer: this._absolutePointer,
transform: this._currentTransform
};
this.fire('mouse:' + eventType, options);
target && target.fire('mouse' + eventType, options);
for (var i = 0; i < targets.length; i++) {
targets[i].fire('mouse' + eventType, options);
}
},
/**
* @private
* @param {Event} e send the mouse event that generate the finalize down, so it can be used in the event
*/
_finalizeCurrentTransform: function(e) {
var transform = this._currentTransform,
target = transform.target,
eventName,
options = {
e: e,
target: target,
transform: transform,
};
if (target._scaling) {
target._scaling = false;
}
target.setCoords();
if (transform.actionPerformed || (this.stateful && target.hasStateChanged())) {
if (transform.actionPerformed) {
eventName = this._addEventOptions(options, transform);
this._fire(eventName, options);
}
this._fire('modified', options);
}
},
/**
* Mutate option object in order to add by property and give back the event name.
* @private
* @param {Object} options to mutate
* @param {Object} transform to inspect action from
*/
_addEventOptions: function(options, transform) {
// we can probably add more details at low cost
// scale change, rotation changes, translation changes
var eventName, by;
switch (transform.action) {
case 'scaleX':
eventName = 'scaled';
by = 'x';
break;
case 'scaleY':
eventName = 'scaled';
by = 'y';
break;
case 'skewX':
eventName = 'skewed';
by = 'x';
break;
case 'skewY':
eventName = 'skewed';
by = 'y';
break;
case 'scale':
eventName = 'scaled';
by = 'equally';
break;
case 'rotate':
eventName = 'rotated';
break;
case 'drag':
eventName = 'moved';
break;
}
options.by = by;
return eventName;
},
/**
* @private
* @param {Event} e Event object fired on mousedown
*/
_onMouseDownInDrawingMode: function(e) {
this._isCurrentlyDrawing = true;
if (this.getActiveObject()) {
this.discardActiveObject(e).requestRenderAll();
}
var pointer = this.getPointer(e);
this.freeDrawingBrush.onMouseDown(pointer, { e: e, pointer: pointer });
this._handleEvent(e, 'down');
},
/**
* @private
* @param {Event} e Event object fired on mousemove
*/
_onMouseMoveInDrawingMode: function(e) {
if (this._isCurrentlyDrawing) {
var pointer = this.getPointer(e);
this.freeDrawingBrush.onMouseMove(pointer, { e: e, pointer: pointer });
}
this.setCursor(this.freeDrawingCursor);
this._handleEvent(e, 'move');
},
/**
* @private
* @param {Event} e Event object fired on mouseup
*/
_onMouseUpInDrawingMode: function(e) {
var pointer = this.getPointer(e);
this._isCurrentlyDrawing = this.freeDrawingBrush.onMouseUp({ e: e, pointer: pointer });
this._handleEvent(e, 'up');
},
/**
* Method that defines the actions when mouse is clicked on canvas.
* The method inits the currentTransform parameters and renders all the
* canvas so the current image can be placed on the top canvas and the rest
* in on the container one.
* @private
* @param {Event} e Event object fired on mousedown
*/
__onMouseDown: function (e) {
this._cacheTransformEventData(e);
this._handleEvent(e, 'down:before');
var target = this._target;
// if right click just fire events
if (checkClick(e, RIGHT_CLICK)) {
if (this.fireRightClick) {
this._handleEvent(e, 'down', RIGHT_CLICK);
}
return;
}
if (checkClick(e, MIDDLE_CLICK)) {
if (this.fireMiddleClick) {
this._handleEvent(e, 'down', MIDDLE_CLICK);
}
return;
}
if (this.isDrawingMode) {
this._onMouseDownInDrawingMode(e);
return;
}
if (!this._isMainEvent(e)) {
return;
}
// ignore if some object is being transformed at this moment
if (this._currentTransform) {
return;
}
var pointer = this._pointer;
// save pointer for check in __onMouseUp event
this._previousPointer = pointer;
var shouldRender = this._shouldRender(target),
shouldGroup = this._shouldGroup(e, target);
if (this._shouldClearSelection(e, target)) {
this.discardActiveObject(e);
}
else if (shouldGroup) {
this._handleGrouping(e, target);
target = this._activeObject;
}
if (this.selection && (!target ||
(!target.selectable && !target.isEditing && target !== this._activeObject))) {
this._groupSelector = {
ex: pointer.x,
ey: pointer.y,
top: 0,
left: 0
};
}
if (target) {
var alreadySelected = target === this._activeObject;
if (target.selectable) {
this.setActiveObject(target, e);
}
var corner = target._findTargetCorner(
this.getPointer(e, true),
fabric.util.isTouchEvent(e)
);
target.__corner = corner;
if (target === this._activeObject && (corner || !shouldGroup)) {
var control = target.controls[corner],
mouseDownHandler = control && control.getMouseDownHandler(e, target, control);
if (mouseDownHandler) {
mouseDownHandler(e, target, control);
}
this._setupCurrentTransform(e, target, alreadySelected);
}
}
this._handleEvent(e, 'down');
// we must renderAll so that we update the visuals
(shouldRender || shouldGroup) && this.requestRenderAll();
},
/**
* reset cache form common information needed during event processing
* @private
*/
_resetTransformEventData: function() {
this._target = null;
this._pointer = null;
this._absolutePointer = null;
},
/**
* Cache common information needed during event processing
* @private
* @param {Event} e Event object fired on event
*/
_cacheTransformEventData: function(e) {
// reset in order to avoid stale caching
this._resetTransformEventData();
this._pointer = this.getPointer(e, true);
this._absolutePointer = this.restorePointerVpt(this._pointer);
this._target = this._currentTransform ? this._currentTransform.target : this.findTarget(e) || null;
},
/**
* @private
*/
_beforeTransform: function(e) {
var t = this._currentTransform;
this.stateful && t.target.saveState();
this.fire('before:transform', {
e: e,
transform: t,
});
},
/**
* Method that defines the actions when mouse is hovering the canvas.
* The currentTransform parameter will define whether the user is rotating/scaling/translating
* an image or neither of them (only hovering). A group selection is also possible and would cancel
* all any other type of action.
* In case of an image transformation only the top canvas will be rendered.
* @private
* @param {Event} e Event object fired on mousemove
*/
__onMouseMove: function (e) {
this._handleEvent(e, 'move:before');
this._cacheTransformEventData(e);
var target, pointer;
if (this.isDrawingMode) {
this._onMouseMoveInDrawingMode(e);
return;
}
if (!this._isMainEvent(e)) {
return;
}
var groupSelector = this._groupSelector;
// We initially clicked in an empty area, so we draw a box for multiple selection
if (groupSelector) {
pointer = this._pointer;
groupSelector.left = pointer.x - groupSelector.ex;
groupSelector.top = pointer.y - groupSelector.ey;
this.renderTop();
}
else if (!this._currentTransform) {
target = this.findTarget(e) || null;
this._setCursorFromEvent(e, target);
this._fireOverOutEvents(target, e);
}
else {
this._transformObject(e);
}
this._handleEvent(e, 'move');
this._resetTransformEventData();
},
/**
* Manage the mouseout, mouseover events for the fabric object on the canvas
* @param {Fabric.Object} target the target where the target from the mousemove event
* @param {Event} e Event object fired on mousemove
* @private
*/
_fireOverOutEvents: function(target, e) {
var _hoveredTarget = this._hoveredTarget,
_hoveredTargets = this._hoveredTargets, targets = this.targets,
length = Math.max(_hoveredTargets.length, targets.length);
this.fireSyntheticInOutEvents(target, e, {
oldTarget: _hoveredTarget,
evtOut: 'mouseout',
canvasEvtOut: 'mouse:out',
evtIn: 'mouseover',
canvasEvtIn: 'mouse:over',
});
for (var i = 0; i < length; i++){
this.fireSyntheticInOutEvents(targets[i], e, {
oldTarget: _hoveredTargets[i],
evtOut: 'mouseout',
evtIn: 'mouseover',
});
}
this._hoveredTarget = target;
this._hoveredTargets = this.targets.concat();
},
/**
* Manage the dragEnter, dragLeave events for the fabric objects on the canvas
* @param {Fabric.Object} target the target where the target from the onDrag event
* @param {Event} e Event object fired on ondrag
* @private
*/
_fireEnterLeaveEvents: function(target, e) {
var _draggedoverTarget = this._draggedoverTarget,
_hoveredTargets = this._hoveredTargets, targets = this.targets,
length = Math.max(_hoveredTargets.length, targets.length);
this.fireSyntheticInOutEvents(target, e, {
oldTarget: _draggedoverTarget,
evtOut: 'dragleave',
evtIn: 'dragenter',
});
for (var i = 0; i < length; i++) {
this.fireSyntheticInOutEvents(targets[i], e, {
oldTarget: _hoveredTargets[i],
evtOut: 'dragleave',
evtIn: 'dragenter',
});
}
this._draggedoverTarget = target;
},
/**
* Manage the synthetic in/out events for the fabric objects on the canvas
* @param {Fabric.Object} target the target where the target from the supported events
* @param {Event} e Event object fired
* @param {Object} config configuration for the function to work
* @param {String} config.targetName property on the canvas where the old target is stored
* @param {String} [config.canvasEvtOut] name of the event to fire at canvas level for out
* @param {String} config.evtOut name of the event to fire for out
* @param {String} [config.canvasEvtIn] name of the event to fire at canvas level for in
* @param {String} config.evtIn name of the event to fire for in
* @private
*/
fireSyntheticInOutEvents: function(target, e, config) {
var inOpt, outOpt, oldTarget = config.oldTarget, outFires, inFires,
targetChanged = oldTarget !== target, canvasEvtIn = config.canvasEvtIn, canvasEvtOut = config.canvasEvtOut;
if (targetChanged) {
inOpt = { e: e, target: target, previousTarget: oldTarget };
outOpt = { e: e, target: oldTarget, nextTarget: target };
}
inFires = target && targetChanged;
outFires = oldTarget && targetChanged;
if (outFires) {
canvasEvtOut && this.fire(canvasEvtOut, outOpt);
oldTarget.fire(config.evtOut, outOpt);
}
if (inFires) {
canvasEvtIn && this.fire(canvasEvtIn, inOpt);
target.fire(config.evtIn, inOpt);
}
},
/**
* Method that defines actions when an Event Mouse Wheel
* @param {Event} e Event object fired on mouseup
*/
__onMouseWheel: function(e) {
this._cacheTransformEventData(e);
this._handleEvent(e, 'wheel');
this._resetTransformEventData();
},
/**
* @private
* @param {Event} e Event fired on mousemove
*/
_transformObject: function(e) {
var pointer = this.getPointer(e),
transform = this._currentTransform;
transform.reset = false;
transform.target.isMoving = true;
transform.shiftKey = e.shiftKey;
transform.altKey = e[this.centeredKey];
this._performTransformAction(e, transform, pointer);
transform.actionPerformed && this.requestRenderAll();
},
/**
* @private
*/
_performTransformAction: function(e, transform, pointer) {
var x = pointer.x,
y = pointer.y,
action = transform.action,
actionPerformed = false,
// this object could be created from the function in the control handlers
options = {
target: transform.target,
e: e,
transform: transform,
pointer: pointer
};
if (action === 'drag') {
actionPerformed = this._translateObject(x, y);
if (actionPerformed) {
this._fire('moving', options);
this.setCursor(options.target.moveCursor || this.moveCursor);
}
}
else {
(actionPerformed = transform.actionHandler(e, transform, x, y)) && this._fire(action, options);
}
transform.actionPerformed = transform.actionPerformed || actionPerformed;
},
/**
* @private
*/
_fire: fabric.controlHandlers.fireEvent,
/**
* Sets the cursor depending on where the canvas is being hovered.
* Note: very buggy in Opera
* @param {Event} e Event object
* @param {Object} target Object that the mouse is hovering, if so.
*/
_setCursorFromEvent: function (e, target) {
if (!target) {
this.setCursor(this.defaultCursor);
return false;
}
var hoverCursor = target.hoverCursor || this.hoverCursor,
activeSelection = this._activeObject && this._activeObject.type === 'activeSelection' ?
this._activeObject : null,
// only show proper corner when group selection is not active
corner = (!activeSelection || !activeSelection.contains(target))
// here we call findTargetCorner always with undefined for the touch parameter.
// we assume that if you are using a cursor you do not need to interact with
// the bigger touch area.
&& target._findTargetCorner(this.getPointer(e, true));
if (!corner) {
if (target.subTargetCheck){
// hoverCursor should come from top-most subTarget,
// so we walk the array backwards
this.targets.concat().reverse().map(function(_target){
hoverCursor = _target.hoverCursor || hoverCursor;
});
}
this.setCursor(hoverCursor);
}
else {
this.setCursor(this.getCornerCursor(corner, target, e));
}
},
/**
* @private
*/
getCornerCursor: function(corner, target, e) {
var control = target.controls[corner];
return control.cursorStyleHandler(e, control, target);
}
});
})();