mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-03 19:45:44 -05:00
Large refactoring of mouse-handling code on View and CanvasView.
Added support for: - Better event bubbling - mouseenter / mouseleave events on view - Better handling of mousedrag / mousemove events on item and view - Support for #removeOn() call in item / view handlers Closes #845
This commit is contained in:
parent
ab68c5b272
commit
db2beba831
7 changed files with 245 additions and 234 deletions
|
@ -24,15 +24,15 @@ var Event = Base.extend(/** @lends Event# */{
|
|||
this.event = event;
|
||||
},
|
||||
|
||||
isPrevented: false,
|
||||
isStopped: false,
|
||||
prevented: false,
|
||||
stopped: false,
|
||||
|
||||
/**
|
||||
* Cancels the event if it is cancelable, without stopping further
|
||||
* propagation of the event.
|
||||
*/
|
||||
preventDefault: function() {
|
||||
this.isPrevented = true;
|
||||
this.prevented = true;
|
||||
this.event.preventDefault();
|
||||
},
|
||||
|
||||
|
@ -40,7 +40,7 @@ var Event = Base.extend(/** @lends Event# */{
|
|||
* Prevents further propagation of the current event.
|
||||
*/
|
||||
stopPropagation: function() {
|
||||
this.isStopped = true;
|
||||
this.stopped = true;
|
||||
this.event.stopPropagation();
|
||||
},
|
||||
|
||||
|
|
|
@ -2659,7 +2659,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
|
|||
isDescendant: function(item) {
|
||||
var parent = this;
|
||||
while (parent = parent._parent) {
|
||||
if (parent == item)
|
||||
if (parent === item)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -351,7 +351,7 @@ var Segment = Base.extend(/** @lends Segment# */{
|
|||
},
|
||||
|
||||
/**
|
||||
* The curve location that describes this segment's position ont the path.
|
||||
* The curve location that describes this segment's position on the path.
|
||||
*
|
||||
* @bean
|
||||
* @type CurveLocation
|
||||
|
|
|
@ -709,6 +709,30 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
|||
* SVG content
|
||||
*/
|
||||
|
||||
removeOn: function(type) {
|
||||
var sets = this._removeSets;
|
||||
if (sets) {
|
||||
// Always clear the drag set on mouseup
|
||||
if (type === 'mouseup')
|
||||
sets.mousedrag = null;
|
||||
var set = sets[type];
|
||||
if (set) {
|
||||
for (var id in set) {
|
||||
var item = set[id];
|
||||
// If we remove this item, we also need to erase it from all
|
||||
// other sets.
|
||||
for (var key in sets) {
|
||||
var other = sets[key];
|
||||
if (other && other != set)
|
||||
delete other[item._id];
|
||||
}
|
||||
item.remove();
|
||||
}
|
||||
sets[type] = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
draw: function(ctx, matrix, pixelRatio) {
|
||||
// Increase the _updateVersion before the draw-loop. After that, items
|
||||
// that are visible will have their _updateVersion set to the new value.
|
||||
|
|
|
@ -322,33 +322,11 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
|
|||
},
|
||||
|
||||
_fireEvent: function(type, event) {
|
||||
// Handle items marked in removeOn*() calls first,.
|
||||
var sets = paper.project._removeSets;
|
||||
if (sets) {
|
||||
// Always clear the drag set on mouseup
|
||||
if (type === 'mouseup')
|
||||
sets.mousedrag = null;
|
||||
var set = sets[type];
|
||||
if (set) {
|
||||
for (var id in set) {
|
||||
var item = set[id];
|
||||
// If we remove this item, we also need to erase it from all
|
||||
// other sets.
|
||||
for (var key in sets) {
|
||||
var other = sets[key];
|
||||
if (other && other != set)
|
||||
delete other[item._id];
|
||||
}
|
||||
item.remove();
|
||||
}
|
||||
sets[type] = null;
|
||||
}
|
||||
}
|
||||
return this.responds(type)
|
||||
&& this.emit(type, new ToolEvent(this, type, event));
|
||||
},
|
||||
|
||||
_handleEvent: function(type, point, event) {
|
||||
_handleEvent: function(type, event, point) {
|
||||
// Update global reference to this scope.
|
||||
paper = this._scope;
|
||||
// Now handle event callbacks
|
||||
|
|
|
@ -31,7 +31,7 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
|
|||
* @name CanvasView#initialize
|
||||
* @param {Size} size the size of the canvas to be created
|
||||
*/
|
||||
initialize: function CanvasView(project, canvas) {
|
||||
initialize: function(project, canvas) {
|
||||
// Handle canvas argument
|
||||
if (!(canvas instanceof HTMLCanvasElement)) {
|
||||
// See if the arguments describe the view size:
|
||||
|
@ -43,8 +43,6 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
|
|||
canvas = CanvasProvider.getCanvas(size);
|
||||
}
|
||||
this._context = canvas.getContext('2d');
|
||||
// Have Item count installed mouse events.
|
||||
this._eventCounters = {};
|
||||
this._pixelRatio = 1;
|
||||
/*#*/ if (__options.environment == 'browser') {
|
||||
if (!/^off|false$/.test(PaperScope.getAttribute(canvas, 'hidpi'))) {
|
||||
|
@ -144,150 +142,6 @@ var CanvasView = View.extend(/** @lends CanvasView# */{
|
|||
project._needsUpdate = false;
|
||||
return true;
|
||||
}
|
||||
},
|
||||
new function() { // Item based mouse handling:
|
||||
var downPoint,
|
||||
lastPoint,
|
||||
overPoint,
|
||||
downItem,
|
||||
lastItem,
|
||||
overItem,
|
||||
dragItem,
|
||||
dblClick,
|
||||
clickTime;
|
||||
|
||||
// Returns true if event was stopped, false otherwise, whether handler was
|
||||
// called or not!
|
||||
function callEvent(view, type, event, point, target, lastPoint) {
|
||||
var item = target,
|
||||
mouseEvent;
|
||||
|
||||
function call(obj, type) {
|
||||
if (obj.responds(type)) {
|
||||
// Only produce the event object if we really need it, and then
|
||||
// reuse it if we're bubbling.
|
||||
if (!mouseEvent) {
|
||||
mouseEvent = new MouseEvent(type, event, point, target,
|
||||
// Calculate delta if lastPoint was passed
|
||||
lastPoint ? point.subtract(lastPoint) : null);
|
||||
}
|
||||
if (obj.emit(type, mouseEvent) && mouseEvent.isStopped) {
|
||||
// Call preventDefault() on native event if mouse event was
|
||||
// handled here.
|
||||
event.preventDefault();
|
||||
return true;
|
||||
}
|
||||
} else if (type === 'doubleclick') {
|
||||
// If obj doesn't respond to doubleclick, fall back to click:
|
||||
return call(obj, 'click');
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble up the parents and call this event until we're told to stop.
|
||||
while (item) {
|
||||
if (call(item, type))
|
||||
return true;
|
||||
item = item.getParent();
|
||||
}
|
||||
// Also call event handler on view, if installed.
|
||||
if (call(view, type))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return /** @lends CanvasView# */{
|
||||
/**
|
||||
* Returns true if event was stopped, false otherwise, whether handler
|
||||
* was called or not!
|
||||
*/
|
||||
_handleEvent: function(type, point, event) {
|
||||
// Drop out if we don't have any event handlers for this type
|
||||
if (!this._eventCounters[type])
|
||||
return;
|
||||
// Run the hit-test first
|
||||
var project = this._project,
|
||||
hit = project.hitTest(point, {
|
||||
tolerance: 0,
|
||||
fill: true,
|
||||
stroke: true
|
||||
}),
|
||||
item = hit && hit.item,
|
||||
stopped = false;
|
||||
// Now handle the mouse events
|
||||
switch (type) {
|
||||
case 'mousedown':
|
||||
stopped = callEvent(this, type, event, point, item);
|
||||
// See if we're clicking again on the same item, within the
|
||||
// double-click time. Firefox uses 300ms as the max time
|
||||
// difference:
|
||||
dblClick = lastItem == item && (Date.now() - clickTime < 300);
|
||||
downItem = lastItem = item;
|
||||
downPoint = lastPoint = overPoint = point;
|
||||
// Only start dragging if none of the mosedown events have
|
||||
// stopped propagation.
|
||||
dragItem = !stopped && item;
|
||||
// Find the first item pu the chain that responds to drag.
|
||||
// NOTE: Drag event don't bubble
|
||||
while (dragItem && !dragItem.responds('mousedrag'))
|
||||
dragItem = dragItem._parent;
|
||||
break;
|
||||
case 'mouseup':
|
||||
// stopping mousup events does not prevent mousedrag / mousemove
|
||||
// hanlding here, but it does click / doubleclick
|
||||
stopped = callEvent(this, type, event, point, item, downPoint);
|
||||
if (dragItem) {
|
||||
// If the point has changed since the last mousedrag event,
|
||||
// send another one
|
||||
if (lastPoint && !lastPoint.equals(point))
|
||||
callEvent(this, 'mousedrag', event, point, dragItem,
|
||||
lastPoint);
|
||||
// If we end up over another item, send it a mousemove event
|
||||
// now. Use point as overPoint, so delta is (0, 0) since
|
||||
// this will be the first mousemove event for this item.
|
||||
if (item !== dragItem) {
|
||||
overPoint = point;
|
||||
callEvent(this, 'mousemove', event, point, item,
|
||||
overPoint);
|
||||
}
|
||||
}
|
||||
if (!stopped && item && item === downItem) {
|
||||
clickTime = Date.now();
|
||||
callEvent(this, dblClick ? 'doubleclick' : 'click', event,
|
||||
downPoint, item);
|
||||
dblClick = false;
|
||||
}
|
||||
downItem = dragItem = null;
|
||||
break;
|
||||
case 'mousemove':
|
||||
// Allow both mousedrag and mousemove events to stop mousemove
|
||||
// events from reaching tools.
|
||||
if (dragItem)
|
||||
stopped = callEvent(this, 'mousedrag', event, point,
|
||||
dragItem, lastPoint);
|
||||
// TODO: Consider implementing this again? "If we have a
|
||||
// mousedrag event, do not send mousemove events to any
|
||||
// item while we're dragging."
|
||||
// For now, we let other items receive mousemove events even
|
||||
// during a drag event.
|
||||
// If we change the overItem, reset overPoint to point so
|
||||
// delta is (0, 0)
|
||||
if (!stopped) {
|
||||
if (item !== overItem)
|
||||
overPoint = point;
|
||||
stopped = callEvent(this, type, event, point, item,
|
||||
overPoint);
|
||||
}
|
||||
lastPoint = overPoint = point;
|
||||
if (item !== overItem) {
|
||||
callEvent(this, 'mouseleave', event, point, overItem);
|
||||
overItem = item;
|
||||
callEvent(this, 'mouseenter', event, point, item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return stopped;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/*#*/ if (__options.environment == 'node') {
|
||||
|
|
271
src/view/View.js
271
src/view/View.js
|
@ -119,6 +119,8 @@ var View = Base.extend(Emitter, /** @lends View# */{
|
|||
// Items that need the onFrame handler called on them
|
||||
this._frameItems = {};
|
||||
this._frameItemCount = 0;
|
||||
// Count the installed events, see _installEvent() / _uninstallEvent().
|
||||
this._eventCounters = {};
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -150,7 +152,8 @@ var View = Base.extend(Emitter, /** @lends View# */{
|
|||
return true;
|
||||
},
|
||||
|
||||
_events: Base.each(['onResize', 'onMouseDown', 'onMouseUp', 'onMouseMove'],
|
||||
_events: Base.each(['onResize', 'onMouseDown', 'onMouseUp', 'onMouseMove',
|
||||
'onMouseDrag', 'onMouseEnter', 'onMouseLeave'],
|
||||
function(name) {
|
||||
this[name] = {
|
||||
install: function(type) {
|
||||
|
@ -681,16 +684,23 @@ var View = Base.extend(Emitter, /** @lends View# */{
|
|||
},
|
||||
new function() { // Injection scope for mouse events on the browser
|
||||
/*#*/ if (__options.environment == 'browser') {
|
||||
|
||||
/**
|
||||
* Native event handling, coordinate conversion, focus handling and
|
||||
* delegation to view and tool objects.
|
||||
*/
|
||||
|
||||
var tool,
|
||||
prevFocus,
|
||||
tempFocus,
|
||||
dragging = false;
|
||||
mouseDown = false;
|
||||
|
||||
function getView(event) {
|
||||
// Get the view from the current event target.
|
||||
var target = DomEvent.getTarget(event);
|
||||
// Some node do not have the getAttribute method, e.g. SVG nodes.
|
||||
return target.getAttribute && View._viewsById[target.getAttribute('id')];
|
||||
return target.getAttribute && View._viewsById[
|
||||
target.getAttribute('id')];
|
||||
}
|
||||
|
||||
function viewToProject(view, event) {
|
||||
|
@ -710,16 +720,33 @@ new function() { // Injection scope for mouse events on the browser
|
|||
}
|
||||
}
|
||||
|
||||
function handleMouseMove(view, point, event) {
|
||||
view._handleEvent('mousemove', point, event);
|
||||
var tool = view._scope.tool;
|
||||
if (tool) {
|
||||
// If there's no onMouseDrag, fire onMouseMove while dragging.
|
||||
tool._handleEvent(dragging && tool.responds('mousedrag')
|
||||
? 'mousedrag' : 'mousemove', point, event);
|
||||
function handleEvent(type, view, event, point) {
|
||||
var eventType = type === 'mousemove' && mouseDown ? 'mousedrag' : type,
|
||||
project = paper.project,
|
||||
tool = view._scope.tool;
|
||||
|
||||
function handle(obj) {
|
||||
obj._handleEvent(eventType, event, point);
|
||||
}
|
||||
|
||||
if (!point)
|
||||
point = viewToProject(view, event);
|
||||
if (project)
|
||||
project.removeOn(eventType);
|
||||
// Always first call the view's mouse handlers, as required by
|
||||
// CanvasView, and then handle the active tool, if any.
|
||||
// No need to call view if it doesn't have event handlers for this type.
|
||||
if (view._eventCounters[type])
|
||||
handle(view);
|
||||
if (tool)
|
||||
handle(tool);
|
||||
// In the end we always call update(), which only updates the view if
|
||||
// anything has changed in the above calls.
|
||||
view.update();
|
||||
return tool;
|
||||
}
|
||||
|
||||
function handleMouseMove(view, event, point) {
|
||||
handleEvent('mousemove', view, event, point);
|
||||
}
|
||||
|
||||
// Touch handling inspired by Hammer.js
|
||||
|
@ -747,9 +774,9 @@ new function() { // Injection scope for mouse events on the browser
|
|||
|
||||
var viewEvents = {
|
||||
'selectstart dragstart': function(event) {
|
||||
// Only stop this even if we're dragging already, since otherwise no
|
||||
// text whatsoever can be selected on the page.
|
||||
if (dragging)
|
||||
// Only stop this even if we're mouseDown already, since otherwise
|
||||
// no text whatsoever can be selected on the page.
|
||||
if (mouseDown)
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
@ -766,10 +793,12 @@ new function() { // Injection scope for mouse events on the browser
|
|||
// TODO: Remove again after Dec 2016 once it is fixed in Chrome.
|
||||
var offset = DomEvent.getOffset(event, view._element),
|
||||
x = offset.x,
|
||||
abs = Math.abs(x),
|
||||
max = 1 << 25;
|
||||
offset.x = abs - max < abs ? (abs - max) * (x < 0 ? -1 : 1) : x;
|
||||
handleMouseMove(view, view.viewToProject(offset), event);
|
||||
abs = Math.abs,
|
||||
ax = abs(x),
|
||||
max = 1 << 25,
|
||||
diff = ax - max;
|
||||
offset.x = abs(diff) < ax ? diff * (x < 0 ? -1 : 1) : x;
|
||||
handleMouseMove(view, event, view.viewToProject(offset));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -783,22 +812,14 @@ new function() { // Injection scope for mouse events on the browser
|
|||
viewEvents[mousedown] = function(event) {
|
||||
// Get the view from the event, and store a reference to the view that
|
||||
// should receive keyboard input.
|
||||
var view = View._focused = getView(event),
|
||||
point = viewToProject(view, event);
|
||||
dragging = true;
|
||||
// Always first call the view's mouse handlers, as required by
|
||||
// CanvasView, and then handle the active tool, if any.
|
||||
view._handleEvent('mousedown', point, event);
|
||||
if (tool = view._scope.tool)
|
||||
tool._handleEvent('mousedown', point, event);
|
||||
// In the end we always call update(), which only updates the view if
|
||||
// anything has changed in the above calls.
|
||||
view.update();
|
||||
var view = View._focused = getView(event);
|
||||
mouseDown = true;
|
||||
handleEvent('mousedown', view, event);
|
||||
};
|
||||
|
||||
docEvents[mousemove] = function(event) {
|
||||
var view = View._focused;
|
||||
if (!dragging) {
|
||||
if (!mouseDown) {
|
||||
// See if we can get the view from the current event target, and
|
||||
// handle the mouse move over it.
|
||||
var target = getView(event);
|
||||
|
@ -808,7 +829,7 @@ new function() { // Injection scope for mouse events on the browser
|
|||
// If we switch view, fire one last mousemove in the old view,
|
||||
// to give items the change to receive a mouseleave, etc.
|
||||
if (view !== target)
|
||||
handleMouseMove(view, viewToProject(view, event), event);
|
||||
handleMouseMove(view, event);
|
||||
prevFocus = view;
|
||||
view = View._focused = tempFocus = target;
|
||||
} else if (tempFocus && tempFocus === view) {
|
||||
|
@ -817,23 +838,15 @@ new function() { // Injection scope for mouse events on the browser
|
|||
updateFocus();
|
||||
}
|
||||
}
|
||||
if (view) {
|
||||
var point = viewToProject(view, event);
|
||||
if (dragging || view.getBounds().contains(point))
|
||||
tool = handleMouseMove(view, point, event);
|
||||
}
|
||||
if (view)
|
||||
handleMouseMove(view, event);
|
||||
};
|
||||
|
||||
docEvents[mouseup] = function(event) {
|
||||
var view = View._focused;
|
||||
if (!view || !dragging)
|
||||
return;
|
||||
var point = viewToProject(view, event);
|
||||
dragging = false;
|
||||
view._handleEvent('mouseup', point, event);
|
||||
if (tool)
|
||||
tool._handleEvent('mouseup', point, event);
|
||||
view.update();
|
||||
if (view && mouseDown)
|
||||
handleEvent('mouseup', view, event);
|
||||
mouseDown = false;
|
||||
};
|
||||
|
||||
DomEvent.add(document, docEvents);
|
||||
|
@ -842,9 +855,84 @@ new function() { // Injection scope for mouse events on the browser
|
|||
load: updateFocus
|
||||
});
|
||||
|
||||
// Flags defining which native events are required by which Paper events
|
||||
// as required for counting amount of necessary natives events.
|
||||
// The mapping is native -> virtual
|
||||
/**
|
||||
* Higher level event handling, hit-testing, and emitting of normal mouse
|
||||
* events along with "virtual" events such as mouseenter, mouseleave,
|
||||
* mousedrag, click, doubleclick, on both the hit-test item and the view,
|
||||
* with support for bubbling (event-propagation).
|
||||
*/
|
||||
|
||||
var downPoint,
|
||||
lastPoint,
|
||||
downItem,
|
||||
overItem,
|
||||
dragItem,
|
||||
clickItem,
|
||||
clickTime,
|
||||
dblClick,
|
||||
overView,
|
||||
// Event fallbacks for "virutal" events, e.g. if an item doesn't respond
|
||||
// to doubleclick, fall back to click:
|
||||
fallbacks = {
|
||||
doubleclick: 'click',
|
||||
mousedrag: 'mousemove'
|
||||
};
|
||||
|
||||
// Returns true if event was stopped, false otherwise, whether handler was
|
||||
// called or not!
|
||||
function emitEvent(obj, type, event, point, prevPoint, stopItem) {
|
||||
var target = obj,
|
||||
mouseEvent;
|
||||
|
||||
function emit(obj, type) {
|
||||
if (obj.responds(type)) {
|
||||
// Only produce the event object if we really need it, and then
|
||||
// reuse it if we're bubbling.
|
||||
if (!mouseEvent) {
|
||||
mouseEvent = new MouseEvent(
|
||||
type, event, point, target, mouseDown,
|
||||
// Calculate delta if prevPoint was passed
|
||||
prevPoint ? point.subtract(prevPoint) : null);
|
||||
}
|
||||
// Bail out if propagation is stopped
|
||||
if (obj.emit(type, mouseEvent) && mouseEvent.stopped)
|
||||
return true;
|
||||
} else {
|
||||
var fallback = fallbacks[type];
|
||||
if (fallback)
|
||||
return emit(obj, fallback);
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble up the parents and emit this event until we're told to stop.
|
||||
while (obj && obj !== stopItem) {
|
||||
if (emit(obj, type))
|
||||
return true;
|
||||
obj = obj._parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function emitEvents(view, item, type, event, point, prevPoint) {
|
||||
// First handle the drag-item and its parents, through bubbling.
|
||||
return (dragItem && emitEvent(dragItem, type, event, point,
|
||||
prevPoint)
|
||||
// Next handle the hit-test item, if it's different from the drag
|
||||
// item and not a descendant of it (in which case it would already
|
||||
// have received an event in the call above). Use fallbacks to
|
||||
// translate mousedrag to mousemove, since drag is handled above.
|
||||
|| item && item !== dragItem && !item.isDescendant(dragItem)
|
||||
&& emitEvent(item, fallbacks[type] || type, event, point,
|
||||
prevPoint, dragItem)
|
||||
// Lastly handle the move / drag on the view, if we're still here.
|
||||
|| emitEvent(view, type, event, point, prevPoint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags defining which native events are required by which Paper events
|
||||
* as required for counting amount of necessary natives events.
|
||||
* The mapping is native -> virtual
|
||||
*/
|
||||
var mouseFlags = {
|
||||
mousedown: {
|
||||
mousedown: 1,
|
||||
|
@ -869,18 +957,87 @@ new function() { // Injection scope for mouse events on the browser
|
|||
return {
|
||||
_viewEvents: viewEvents,
|
||||
|
||||
// To be defined in subclasses
|
||||
_handleEvent: function(/* type, point, event */) {},
|
||||
/**
|
||||
* Returns true if event was stopped, false otherwise, whether handler
|
||||
* was called or not!
|
||||
*/
|
||||
_handleEvent: function(type, event, point) {
|
||||
// Run the hit-test first
|
||||
var hit = this._project.hitTest(point, {
|
||||
tolerance: 0,
|
||||
fill: true,
|
||||
stroke: true
|
||||
}),
|
||||
item = hit && hit.item,
|
||||
inView = this.getBounds().contains(point),
|
||||
stopped = false,
|
||||
mouse = {};
|
||||
// Create a simple lookup object to quickly check for different
|
||||
// mouse event types.
|
||||
mouse[type.substr(5)] = true;
|
||||
// Handle mousemove first, even if this is not actually a mousemove
|
||||
// event but the mouse has moved since the last event, but do not
|
||||
// allow it to stop the other events in that case.
|
||||
var nativeMove = mouse.move || mouse.drag;
|
||||
moveType = nativeMove && type
|
||||
|| lastPoint && !lastPoint.equals(point) && 'mousemove';
|
||||
if (moveType) {
|
||||
// Handle mouseenter / leave between items, as well as views.
|
||||
if (item !== overItem) {
|
||||
if (overItem)
|
||||
emitEvent(overItem, 'mouseleave', event, point);
|
||||
if (item)
|
||||
emitEvent(item, 'mouseenter', event, point);
|
||||
}
|
||||
overItem = item;
|
||||
if (overView && !overView.getBounds().contains(point)) {
|
||||
emitEvent(overView, 'mouseleave', event, point);
|
||||
overView = null;
|
||||
}
|
||||
if (this !== overView && inView) {
|
||||
emitEvent(this, 'mouseenter', event, point);
|
||||
overView = this;
|
||||
}
|
||||
if (inView || mouse.drag)
|
||||
stopped = emitEvents(this, item, moveType, event, point,
|
||||
lastPoint);
|
||||
}
|
||||
if (!nativeMove) {
|
||||
// Now handle mousedown / mouseup
|
||||
stopped = emitEvents(this, item, type, event, point, downPoint);
|
||||
if (mouse.down) {
|
||||
// See if we're clicking again on the same item, within the
|
||||
// double-click time. Firefox uses 300ms as the max time
|
||||
// difference:
|
||||
dblClick = clickItem === item
|
||||
&& (Date.now() - clickTime < 300);
|
||||
downItem = clickItem = item;
|
||||
downPoint = lastPoint = point;
|
||||
// Only start dragging if the mousedown event has not
|
||||
// stopped propagation.
|
||||
dragItem = !stopped && item;
|
||||
} else if (mouse.up) {
|
||||
// Emulate click / doubleclick, but only on item, not view
|
||||
if (!stopped && item && item === downItem) {
|
||||
clickTime = Date.now();
|
||||
emitEvent(item, dblClick ? 'doubleclick' : 'click',
|
||||
event, point, downPoint);
|
||||
dblClick = false;
|
||||
}
|
||||
downItem = dragItem = null;
|
||||
}
|
||||
}
|
||||
lastPoint = point;
|
||||
return stopped;
|
||||
},
|
||||
|
||||
_installEvent: function(type) {
|
||||
// If the view requires counting of installed mouse events,
|
||||
// increase the counters now according to mouseFlags
|
||||
var counters = this._eventCounters;
|
||||
if (counters) {
|
||||
for (var key in mouseFlags) {
|
||||
counters[key] = (counters[key] || 0)
|
||||
+ (mouseFlags[key][type] || 0);
|
||||
}
|
||||
for (var key in mouseFlags) {
|
||||
counters[key] = (counters[key] || 0)
|
||||
+ (mouseFlags[key][type] || 0);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -888,10 +1045,8 @@ new function() { // Injection scope for mouse events on the browser
|
|||
// If the view requires counting of installed mouse events,
|
||||
// decrease the counters now according to mouseFlags
|
||||
var counters = this._eventCounters;
|
||||
if (counters) {
|
||||
for (var key in mouseFlags)
|
||||
counters[key] -= mouseFlags[key][type] || 0;
|
||||
}
|
||||
for (var key in mouseFlags)
|
||||
counters[key] -= mouseFlags[key][type] || 0;
|
||||
},
|
||||
|
||||
statics: {
|
||||
|
|
Loading…
Reference in a new issue