Implement new convention when to call event.preventDefault() after mouse-events:

- If any of the handlers were called, except for mousemove events which need to call `event.preventDefault()` explicitly, or `return false;`.
- If this is a mousedown event, and the view or tools respond to mouseup.
This commit is contained in:
Jürg Lehni 2016-01-14 11:19:54 +01:00
parent 1a6bf972d5
commit b8a1fbcd67
4 changed files with 28 additions and 41 deletions

View file

@ -85,20 +85,14 @@ var Emitter = {
// won't throw us off track here: // won't throw us off track here:
handlers = handlers.slice(); handlers = handlers.slice();
for (var i = 0, l = handlers.length; i < l; i++) { for (var i = 0, l = handlers.length; i < l; i++) {
var res = handlers[i].apply(this, args); if (handlers[i].apply(this, args) === false) {
// Look at the handler's return value to decide how to propagate: // If the handler returns false, prevent the default behavior
if (res === false) { // and stop propagation of the event by calling stop()
// If it returns false, prevent the default behavior and stop
// propagation of the event by calling stop()
if (event && event.stop) if (event && event.stop)
event.stop(); event.stop();
// Stop propagation right now! // Stop propagation right now!
break; break;
} else if (res === true) { }
// If it return true, remember that one handler wants to enforce
// the browser's default behavior. This is handled later.
event._enforced = true;
}
} }
return true; return true;
}, },

View file

@ -26,8 +26,6 @@ var Event = Base.extend(/** @lends Event# */{
prevented: false, prevented: false,
stopped: false, stopped: false,
// Internal flag indicating whether the default shall be enforced.
_enforced: false,
/** /**
* Cancels the event if it is cancelable, without stopping further * Cancels the event if it is cancelable, without stopping further

View file

@ -282,9 +282,7 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
/** /**
* Private method to handle tool-events. * Private method to handle tool-events.
* *
* @return {@true if the default event should be prevented}. This is if at * @return {@true if at least one event handler was called}.
* least one event handler was called and none of the called handlers
* wants to enforce the default.
*/ */
_handleEvent: function(type, event, point, mouse) { _handleEvent: function(type, event, point, mouse) {
// Update global reference to this scope. // Update global reference to this scope.
@ -301,8 +299,7 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
// case it is shorter than maxDistance, as this would produce weird // case it is shorter than maxDistance, as this would produce weird
// results. matchMaxDistance controls this. // results. matchMaxDistance controls this.
matchMaxDistance = false, matchMaxDistance = false,
called = false, // Has at least one handler been called? called = false,
enforced = false, // Does a handler want to enforce the default?
tool = this; tool = this;
function update(start, minDistance, maxDistance) { function update(start, minDistance, maxDistance) {
@ -348,14 +345,9 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
} }
function emit() { function emit() {
if (tool.responds(type)) { called = tool.responds(type)
var toolEvent = new ToolEvent(tool, type, event); && tool.emit(type, new ToolEvent(tool, type, event))
if (tool.emit(type, toolEvent)) { || called;
called = true;
if (toolEvent._enforced)
enforced = true;
}
}
} }
if (mouse.down) { if (mouse.down) {
@ -383,7 +375,7 @@ var Tool = PaperScopeItem.extend(/** @lends Tool# */{
} }
} }
} }
return called && !enforced; return called;
} }
/** /**
* {@grouptitle Event Handling} * {@grouptitle Event Handling}

View file

@ -838,8 +838,7 @@ new function() { // Injection scope for mouse events on the browser
* with support for bubbling (event-propagation). * with support for bubbling (event-propagation).
*/ */
var called = false, // Has at least one handler been called? var called = false,
enforced = false, // Does a handler want to enforce the default?
// Event fallbacks for "virutal" events, e.g. if an item doesn't respond // Event fallbacks for "virutal" events, e.g. if an item doesn't respond
// to doubleclick, fall back to click: // to doubleclick, fall back to click:
fallbacks = { fallbacks = {
@ -847,8 +846,7 @@ new function() { // Injection scope for mouse events on the browser
mousedrag: 'mousemove' mousedrag: 'mousemove'
}; };
// Returns true if event was stopped, false otherwise, whether handler was // Returns true if event was stopped, false otherwise.
// called or not!
function emitEvent(obj, type, event, point, prevPoint, stopItem) { function emitEvent(obj, type, event, point, prevPoint, stopItem) {
var target = obj, var target = obj,
mouseEvent; mouseEvent;
@ -865,8 +863,6 @@ new function() { // Injection scope for mouse events on the browser
} }
if (obj.emit(type, mouseEvent)) { if (obj.emit(type, mouseEvent)) {
called = true; called = true;
if (mouseEvent._enforced)
enforced = true;
// Bail out if propagation is stopped // Bail out if propagation is stopped
if (mouseEvent.stopped) if (mouseEvent.stopped)
return true; return true;
@ -887,13 +883,11 @@ new function() { // Injection scope for mouse events on the browser
return false; return false;
} }
// Returns true if event was stopped, false otherwise, whether handler was // Returns true if event was stopped, false otherwise.
// called or not!
function emitEvents(view, item, type, event, point, prevPoint) { function emitEvents(view, item, type, event, point, prevPoint) {
// Set enforced and called to false, so it will reflect if the following // Set called to false, so it will reflect if the following calls to
// calls to emitEvent() have called a handler, and if at least one of // emitEvent() have called a handler.
// the handlers wants to enforce default. called = false;
called = enforced = false;
// First handle the drag-item and its parents, through bubbling. // First handle the drag-item and its parents, through bubbling.
return (dragItem && emitEvent(dragItem, type, event, point, return (dragItem && emitEvent(dragItem, type, event, point,
prevPoint) prevPoint)
@ -958,7 +952,7 @@ new function() { // Injection scope for mouse events on the browser
var handleItems = this._itemEvents[type], var handleItems = this._itemEvents[type],
project = paper.project, project = paper.project,
tool = this._scope.tool; tool = this._scope.tool;
// If it's a native mousemove event but the mouse is clicke, convert // If it's a native mousemove event but the mouse is down, convert
// it to a mousedrag. // it to a mousedrag.
// NOTE: emitEvent(), as well as Tool#_handleEvent() fall back to // NOTE: emitEvent(), as well as Tool#_handleEvent() fall back to
// mousemove if the objects don't respond to mousedrag. // mousemove if the objects don't respond to mousedrag.
@ -1054,8 +1048,17 @@ new function() { // Injection scope for mouse events on the browser
// to only be fired if we're inside the view or if we just left it. // to only be fired if we're inside the view or if we just left it.
// Prevent default if at least one handler was called, and none of // Prevent default if at least one handler was called, and none of
// them enforces default, to prevent scrolling on touch devices. // them enforces default, to prevent scrolling on touch devices.
if (handle && tool && tool._handleEvent(type, event, point, mouse) if (handle && tool)
|| called && !enforced) called = tool._handleEvent(type, event, point, mouse) || called;
// Call preventDefault()`, but only according to this convention:
// - If any of the handlers were called, except for mousemove events
// which need to call `event.preventDefault()` explicitly, or
// `return false;`.
// - If this is a mousedown event, and the view or tools respond to
// mouseup.
var up = 'mouseup';
if (called && !mouse.move || mouse.down && (this._itemEvents[up]
|| this.responds(up) || tool.responds(up)))
event.preventDefault(); event.preventDefault();
// In the end we always call update(), which only updates the view // In the end we always call update(), which only updates the view
// if anything has changed in the above calls. // if anything has changed in the above calls.