ares-ide
Version:
A browser-based code editor and UI designer for Enyo 2 projects
284 lines (279 loc) • 8.12 kB
JavaScript
/**
Enyo supports a cross-platform set of drag events. These events allow users
to write a single set of event handlers for applications that run on both
mobile and desktop platforms.
The following events are provided:
* "dragstart"
* "dragfinish"
* "drag"
* "drop"
* "dragover"
* "dragout"
* "hold"
* "release"
* "holdpulse"
* "flick"
For more information on these events, see the documentation on [User
Input](building-apps/user-input.html) in the Enyo Developer Guide.
*/
//* @protected
enyo.dispatcher.features.push(
function(e) {
// NOTE: beware of properties in enyo.gesture inadvertently mapped to event types
if (enyo.gesture.drag[e.type]) {
return enyo.gesture.drag[e.type](e);
}
}
);
//* @public
enyo.gesture.drag = {
//* @protected
hysteresisSquared: 16,
holdPulseDelay: 200,
trackCount: 5,
minFlick: 0.1,
minTrack: 8,
down: function(e) {
// tracking if the mouse is down
//enyo.log("tracking ON");
// Note: 'tracking' flag indicates interest in mousemove, it's turned off
// on mouseup
// make sure to stop dragging in case the up event was not received.
this.stopDragging(e);
this.cancelHold();
this.target = e.target;
this.startTracking(e);
this.beginHold(e);
},
move: function(e) {
if (this.tracking) {
this.track(e);
// If the mouse is not down and we're tracking a drag, abort.
// this error condition can occur on IE/Webkit after interaction with a scrollbar.
if (!e.which) {
this.stopDragging(e);
this.cancelHold();
this.tracking = false;
//enyo.log("enyo.gesture.drag: mouse must be down to drag.");
return;
}
if (this.dragEvent) {
this.sendDrag(e);
} else if (this.dy*this.dy + this.dx*this.dx >= this.hysteresisSquared) {
this.sendDragStart(e);
this.cancelHold();
}
}
},
up: function(e) {
this.endTracking(e);
this.stopDragging(e);
this.cancelHold();
},
leave: function(e) {
if (this.dragEvent) {
this.sendDragOut(e);
}
},
stopDragging: function(e) {
if (this.dragEvent) {
this.sendDrop(e);
var handled = this.sendDragFinish(e);
this.dragEvent = null;
return handled;
}
},
makeDragEvent: function(inType, inTarget, inEvent, inInfo) {
var adx = Math.abs(this.dx), ady = Math.abs(this.dy);
var h = adx > ady;
// suggest locking if off-axis < 22.5 degrees
var l = (h ? ady/adx : adx/ady) < 0.414;
var e = {};
// var e = {
e.type = inType;
e.dx = this.dx;
e.dy = this.dy;
e.ddx = this.dx - this.lastDx;
e.ddy = this.dy - this.lastDy;
e.xDirection = this.xDirection;
e.yDirection = this.yDirection;
e.pageX = inEvent.pageX;
e.pageY = inEvent.pageY;
e.clientX = inEvent.clientX;
e.clientY = inEvent.clientY;
e.horizontal = h;
e.vertical = !h;
e.lockable = l;
e.target = inTarget;
e.dragInfo = inInfo;
e.ctrlKey = inEvent.ctrlKey;
e.altKey = inEvent.altKey;
e.metaKey = inEvent.metaKey;
e.shiftKey = inEvent.shiftKey;
e.srcEvent = inEvent.srcEvent;
// };
//Fix for IE8, which doesn't include pageX and pageY properties
if(enyo.platform.ie==8 && e.target) {
e.pageX = e.clientX + e.target.scrollLeft;
e.pageY = e.clientY + e.target.scrollTop;
}
e.preventDefault = enyo.gesture.preventDefault;
e.disablePrevention = enyo.gesture.disablePrevention;
return e;
},
sendDragStart: function(e) {
//enyo.log("dragstart");
this.dragEvent = this.makeDragEvent("dragstart", this.target, e);
enyo.dispatch(this.dragEvent);
},
sendDrag: function(e) {
//enyo.log("sendDrag to " + this.dragEvent.target.id + ", over to " + e.target.id);
// send dragOver event to the standard event target
var synth = this.makeDragEvent("dragover", e.target, e, this.dragEvent.dragInfo);
enyo.dispatch(synth);
// send drag event to the drag source
synth.type = "drag";
synth.target = this.dragEvent.target;
enyo.dispatch(synth);
},
sendDragFinish: function(e) {
//enyo.log("dragfinish");
var synth = this.makeDragEvent("dragfinish", this.dragEvent.target, e, this.dragEvent.dragInfo);
synth.preventTap = function() {
if (e.preventTap) {
e.preventTap();
}
};
enyo.dispatch(synth);
},
sendDragOut: function(e) {
var synth = this.makeDragEvent("dragout", e.target, e, this.dragEvent.dragInfo);
enyo.dispatch(synth);
},
sendDrop: function(e) {
var synth = this.makeDragEvent("drop", e.target, e, this.dragEvent.dragInfo);
synth.preventTap = function() {
if (e.preventTap) {
e.preventTap();
}
};
enyo.dispatch(synth);
},
startTracking: function(e) {
this.tracking = true;
// note: use clientX/Y to be compatible with ie8
this.px0 = e.clientX;
this.py0 = e.clientY;
// this.flickInfo = {startEvent: e, moves: []};
this.flickInfo = {};
this.flickInfo.startEvent = e;
// FIXME: so we're trying to reuse objects where possible, should
// do the same in scenarios like this for arrays
this.flickInfo.moves = [];
this.track(e);
},
track: function(e) {
this.lastDx = this.dx;
this.lastDy = this.dy;
this.dx = e.clientX - this.px0;
this.dy = e.clientY - this.py0;
this.xDirection = this.calcDirection(this.dx - this.lastDx, 0);
this.yDirection = this.calcDirection(this.dy - this.lastDy, 0);
//
var ti = this.flickInfo;
ti.moves.push({
x: e.clientX,
y: e.clientY,
// t: enyo.now()
t: enyo.bench()
});
// track specified # of points
if (ti.moves.length > this.trackCount) {
ti.moves.shift();
}
},
endTracking: function() {
this.tracking = false;
var ti = this.flickInfo;
var moves = ti && ti.moves;
if (moves && moves.length > 1) {
// note: important to use up time to reduce flick
// velocity based on time between move and up.
var l = moves[moves.length-1];
// var n = enyo.now();
var n = enyo.bench();
// take the greatest of flick between each tracked move and last move
for (var i=moves.length-2, dt=0, x1=0, y1=0, x=0, y=0, sx=0, sy=0, m; (m=moves[i]); i--) {
// this flick (this move - last move) / (this time - last time)
dt = n - m.t;
x1 = (l.x - m.x) / dt;
y1 = (l.y - m.y) / dt;
// establish flick direction
sx = sx || (x1 < 0 ? -1 : (x1 > 0 ? 1 : 0));
sy = sy || (y1 < 0 ? -1 : (y1 > 0 ? 1 : 0));
// if either axis is a greater flick than previously recorded use this one
if ((x1 * sx > x * sx) || (y1 * sy > y * sy)) {
x = x1;
y = y1;
}
}
var v = Math.sqrt(x*x + y*y);
if (v > this.minFlick) {
// generate the flick using the start event so it has those coordinates
this.sendFlick(ti.startEvent, x, y, v);
}
}
this.flickInfo = null;
},
calcDirection: function(inNum, inDefault) {
return inNum > 0 ? 1 : (inNum < 0 ? -1 : inDefault);
},
beginHold: function(e) {
// this.holdStart = enyo.now();
this.holdStart = enyo.bench();
// clone the event to ensure it stays alive on IE upon returning to event loop
var $ce = enyo.clone(e);
$ce.srcEvent = enyo.clone(e.srcEvent);
this._holdJobFunction = enyo.bind(this, "sendHoldPulse", $ce);
this._holdJobFunction.ce = $ce;
this.holdJob = setInterval(this._holdJobFunction, this.holdPulseDelay);
},
cancelHold: function() {
clearInterval(this.holdJob);
this.holdJob = null;
if (this._holdJobFunction) {
this._holdJobFunction.ce = null;
this._holdJobFunction = null;
}
if (this.sentHold) {
this.sentHold = false;
this.sendRelease(this.holdEvent);
}
},
sendHoldPulse: function(inEvent) {
if (!this.sentHold) {
this.sentHold = true;
this.sendHold(inEvent);
}
var e = enyo.gesture.makeEvent("holdpulse", inEvent);
// e.holdTime = enyo.now() - this.holdStart;
e.holdTime = enyo.bench() - this.holdStart;
enyo.dispatch(e);
},
sendHold: function(inEvent) {
this.holdEvent = inEvent;
var e = enyo.gesture.makeEvent("hold", inEvent);
enyo.dispatch(e);
},
sendRelease: function(inEvent) {
var e = enyo.gesture.makeEvent("release", inEvent);
enyo.dispatch(e);
},
sendFlick: function(inEvent, inX, inY, inV) {
var e = enyo.gesture.makeEvent("flick", inEvent);
e.xVelocity = inX;
e.yVelocity = inY;
e.velocity = inV;
enyo.dispatch(e);
}
};