MIDI.js/js/Window/Event.js
2012-11-21 02:25:41 -08:00

1779 lines
54 KiB
JavaScript

/*
----------------------------------------------------
Event.js : 1.1.1 : 2012/11/19 : MIT License
----------------------------------------------------
https://github.com/mudcube/Event.js
----------------------------------------------------
1 : click, dblclick, dbltap
1+ : tap, longpress, drag, swipe
2+ : pinch, rotate
: mousewheel, devicemotion, shake
----------------------------------------------------
TODO
----------------------------------------------------
* switch configuration to 4th argument on addEventListener
* bbox calculation for elements scaled with transform.
----------------------------------------------------
NOTES
----------------------------------------------------
* When using other libraries that may have built in "Event" namespace,
i.e. Typescript, you can use "eventjs" instead of "Event" for all example calls.
----------------------------------------------------
REQUIREMENTS: querySelector, querySelectorAll
----------------------------------------------------
* There are two ways to add/remove events with this library.
----------------------------------------------------
// Retains "this" attribute as target, and overrides native addEventListener.
target.addEventListener(type, listener, useCapture);
target.removeEventListener(type, listener, useCapture);
// Attempts to perform as fast as possible.
Event.add(type, listener, configure);
Event.remove(type, listener, configure);
* You can turn prototyping on/off for individual features.
----------------------------------------------------
Event.modifyEventListener = true; // add custom *EventListener commands to HTMLElements.
Event.modifySelectors = true; // add bulk *EventListener commands on NodeLists from querySelectorAll and others.
* Example of setting up a single listener with a custom configuration.
----------------------------------------------------
// optional configuration.
var configure = {
fingers: 2, // listen for specifically two fingers.
snap: 90 // snap to 90 degree intervals.
};
// adding with addEventListener()
target.addEventListener("swipe", function(event) {
// additional variables can be found on the event object.
console.log(event.velocity, event.angle, event.fingers);
}, configure);
// adding with Event.add()
Event.add("swipe", function(event, self) {
// additional variables can be found on the self object.
console.log(self.velocity, self.angle, self.fingers);
}, configure);
* Multiple listeners glued together.
----------------------------------------------------
// adding with addEventListener()
target.addEventListener("click swipe", function(event) { });
// adding with Event.add()
Event.add(target, "click swipe", function(event, self) { });
* Use query selectors to create an event (querySelectorAll)
----------------------------------------------------
// adding events to NodeList from querySelectorAll()
document.querySelectorAll("#element a.link").addEventListener("click", callback);
// adding with Event.add()
Event.add("#element a.link", "click", callback);
* Listen for selector to become available (querySelector)
----------------------------------------------------
Event.add("body", "ready", callback);
// or...
Event.add({
target: "body",
type: "ready",
timeout: 10000, // set a timeout to stop checking.
interval: 30, // set how often to check for element.
listener: callback
});
* Multiple listeners bound to one callback w/ single configuration.
----------------------------------------------------
var bindings = Event.add({
target: target,
type: "click swipe",
snap: 90, // snap to 90 degree intervals.
minFingers: 2, // minimum required fingers to start event.
maxFingers: 4, // maximum fingers in one event.
listener: function(event, self) {
console.log(self.gesture); // will be click or swipe.
console.log(self.x);
console.log(self.y);
console.log(self.identifier);
console.log(self.start);
console.log(self.fingers); // somewhere between "2" and "4".
self.pause(); // disable event.
self.resume(); // enable event.
self.remove(); // remove event.
}
});
* Multiple listeners bound to multiple callbacks w/ single configuration.
----------------------------------------------------
var bindings = Event.add({
target: target,
minFingers: 1,
maxFingers: 12,
listeners: {
click: function(event, self) {
self.remove(); // removes this click listener.
},
swipe: function(event, self) {
binding.remove(); // removes both the click + swipe listeners.
}
}
});
* Multiple listeners bound to multiple callbacks w/ multiple configurations.
----------------------------------------------------
var binding = Event.add({
target: target,
listeners: {
longpress: {
fingers: 1,
wait: 500, // milliseconds
listener: function(event, self) {
console.log(self.fingers); // "1" finger.
}
},
drag: {
fingers: 3,
position: "relative", // "relative", "absolute", "difference", "move"
listener: function(event, self) {
console.log(self.fingers); // "3" fingers.
console.log(self.x); // coordinate is relative to edge of target.
}
}
}
});
* Capturing an event and manually forwarding it to a proxy (tiered events).
----------------------------------------------------
Event.add(target, "down", function(event, self) {
var x = event.pageX; // local variables that wont change.
var y = event.pageY;
Event.proxy.drag({
event: event,
target: target,
listener: function(event, self) {
console.log(x - event.pageX); // measure movement.
console.log(y - event.pageY);
}
});
});
----------------------------------------------------
* Event proxies.
* type, fingers, state, start, x, y, position, bbox
* rotation, scale, velocity, angle, delay, timeout
----------------------------------------------------
// "Click" :: fingers, minFingers, maxFingers.
Event.add(window, "click", function(event, self) {
console.log(self.gesture, self.x, self.y);
});
// "Double-Click" :: fingers, minFingers, maxFingers.
Event.add(window, "dblclick", function(event, self) {
console.log(self.gesture, self.x, self.y);
});
// "Drag" :: fingers, maxFingers, position
Event.add(window, "drag", function(event, self) {
console.log(self.gesture, self.fingers, self.state, self.start, self.x, self.y, self.bbox);
});
// "Gesture" :: fingers, minFingers, maxFingers.
Event.add(window, "gesture", function(event, self) {
console.log(self.gesture, self.fingers, self.state, self.rotation, self.scale);
});
// "Swipe" :: fingers, minFingers, maxFingers, snap, threshold.
Event.add(window, "swipe", function(event, self) {
console.log(self.gesture, self.fingers, self.velocity, self.angle);
});
// "Tap" :: fingers, minFingers, maxFingers, timeout.
Event.add(window, "tap", function(event, self) {
console.log(self.gesture, self.fingers);
});
// "Longpress" :: fingers, minFingers, maxFingers, delay.
Event.add(window, "longpress", function(event, self) {
console.log(self.gesture, self.fingers);
});
//
Event.add(window, "shake", function(event, self) {
console.log(self.gesture, self.acceleration, self.accelerationIncludingGravity);
});
//
Event.add(window, "devicemotion", function(event, self) {
console.log(self.gesture, self.acceleration, self.accelerationIncludingGravity);
});
//
Event.add(window, "wheel", function(event, self) {
console.log(self.gesture, self.state, self.wheelDelta);
});
* Stop, prevent and cancel.
----------------------------------------------------
Event.stop(event); // stop bubble.
Event.prevent(event); // prevent default.
Event.cancel(event); // stop and prevent.
* Track for proper command/control-key for Mac/PC.
----------------------------------------------------
Event.add(window, "keyup keydown", Event.proxy.metaTracker);
console.log(Event.proxy.metaKey);
* Test for event features, in this example Drag & Drop file support.
----------------------------------------------------
console.log(Event.supports('dragstart') && Event.supports('drop') && !!window.FileReader);
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(eventjs) === "undefined") var eventjs = Event;
Event = (function(root) { "use strict";
// Add custom *EventListener commands to HTMLElements.
root.modifyEventListener = false;
// Add bulk *EventListener commands on NodeLists from querySelectorAll and others.
root.modifySelectors = false;
// Event maintenance.
root.add = function(target, type, listener, configure) {
return eventManager(target, type, listener, configure, "add");
};
root.remove = function(target, type, listener, configure) {
return eventManager(target, type, listener, configure, "remove");
};
root.stop = function(event) {
if (event.stopPropagation) event.stopPropagation();
event.cancelBubble = true; // <= IE8
event.bubble = 0;
};
root.prevent = function(event) {
if (event.preventDefault) event.preventDefault();
event.returnValue = false; // <= IE8
};
root.cancel = function(event) {
root.stop(event);
root.prevent(event);
};
// Check whether event is natively supported (via @kangax)
root.supports = function (target, type) {
if (typeof(target) === "string") {
type = target;
target = window;
}
type = "on" + type;
if (type in target) return true;
if (!target.setAttribute) target = document.createElement("div");
if (target.setAttribute && target.removeAttribute) {
target.setAttribute(type, "");
var isSupported = typeof target[type] === "function";
if (typeof target[type] !== "undefined") target[type] = null;
target.removeAttribute(type);
return isSupported;
}
};
var clone = function (obj) {
if (!obj || typeof (obj) !== 'object') return obj;
var temp = new obj.constructor();
for (var key in obj) {
if (!obj[key] || typeof (obj[key]) !== 'object') {
temp[key] = obj[key];
} else { // clone sub-object
temp[key] = clone(obj[key]);
}
}
return temp;
};
/// Handle custom *EventListener commands.
var eventManager = function(target, type, listener, configure, trigger, fromOverwrite) {
configure = configure || {};
// Check for element to load on interval (before onload).
if (typeof(target) === "string" && type === "ready") {
var time = (new Date()).getTime();
var timeout = configure.timeout;
var ms = configure.interval || 1000 / 60;
var interval = window.setInterval(function() {
if ((new Date()).getTime() - time > timeout) {
window.clearInterval(interval);
}
if (document.querySelector(target)) {
window.clearInterval(interval);
listener();
}
}, ms);
return;
}
// Get DOM element from Query Selector.
if (typeof(target) === "string") {
target = document.querySelectorAll(target);
if (target.length === 0) return createError("Missing target on listener!"); // No results.
if (target.length === 1) { // Single target.
target = target[0];
}
}
/// Handle multiple targets.
var event;
var events = {};
if (target.length > 0) {
for (var n0 = 0, length0 = target.length; n0 < length0; n0 ++) {
event = eventManager(target[n0], type, listener, clone(configure), trigger);
if (event) events[n0] = event;
}
return createBatchCommands(events);
}
// Check for multiple events in one string.
if (type.indexOf && type.indexOf(" ") !== -1) type = type.split(" ");
if (type.indexOf && type.indexOf(",") !== -1) type = type.split(",");
// Attach or remove multiple events associated with a target.
if (typeof(type) !== "string") { // Has multiple events.
if (typeof(type.length) === "number") { // Handle multiple listeners glued together.
for (var n1 = 0, length1 = type.length; n1 < length1; n1 ++) { // Array [type]
event = eventManager(target, type[n1], listener, clone(configure), trigger);
if (event) events[type[n1]] = event;
}
} else { // Handle multiple listeners.
for (var key in type) { // Object {type}
if (typeof(type[key]) === "function") { // without configuration.
event = eventManager(target, key, type[key], clone(configure), trigger);
} else { // with configuration.
event = eventManager(target, key, type[key].listener, clone(type[key]), trigger);
}
if (event) events[key] = event;
}
}
return createBatchCommands(events);
}
// Ensure listener is a function.
if (typeof(listener) !== "function") return createError("Listener is not a function!");
// Generate a unique wrapper identifier.
var useCapture = configure.useCapture || false;
var id = normalize(type) + getID(target) + "." + getID(listener) + "." + (useCapture ? 1 : 0);
// Handle the event.
if (root.Gesture && root.Gesture._gestureHandlers[type]) { // Fire custom event.
if (trigger === "remove") { // Remove event listener.
if (!wrappers[id]) return; // Already removed.
wrappers[id].remove();
delete wrappers[id];
} else if (trigger === "add") { // Attach event listener.
if (wrappers[id]) return wrappers[id]; // Already attached.
// Retains "this" orientation.
if (configure.useCall && !root.modifyEventListener) {
var tmp = listener;
listener = function(event, self) {
for (var key in self) event[key] = self[key];
return tmp.call(target, event);
};
}
// Create listener proxy.
configure.gesture = type;
configure.target = target;
configure.listener = listener;
configure.fromOverwrite = fromOverwrite;
// Record wrapper.
wrappers[id] = root.proxy[type](configure);
}
} else { // Fire native event.
type = normalize(type);
if (trigger === "remove") { // Remove event listener.
if (!wrappers[id]) return; // Already removed.
target[remove](type, listener, useCapture);
delete wrappers[id];
} else if (trigger === "add") { // Attach event listener.
if (wrappers[id]) return wrappers[id]; // Already attached.
target[add](type, listener, useCapture);
// Record wrapper.
wrappers[id] = {
type: type,
target: target,
listener: listener,
remove: function() {
root.remove(target, type, listener, configure);
}
};
}
}
return wrappers[id];
};
/// Perform batch actions on multiple events.
var createBatchCommands = function(events) {
return {
remove: function() { // Remove multiple events.
for (var key in events) {
events[key].remove();
}
},
add: function() { // Add multiple events.
for (var key in events) {
events[key].add();
}
}
};
};
/// Display error message in console.
var createError = function(message) {
if (typeof(console) === "undefined") return;
if (typeof(console.error) === "undefined") return;
console.error(message);
};
/// Handle naming discrepancies between platforms.
var normalize = (function() {
var translate = {};
return function(type) {
if (!root.pointerType) {
if (window.navigator.msPointerEnabled) {
root.pointerType = "mspointer";
translate = {
"mousedown": "MSPointerDown",
"mousemove": "MSPointerMove",
"mouseup": "MSPointerUp"
};
} else if (root.supports("touchstart")) {
root.pointerType = "touch";
translate = {
"mousedown": "touchstart",
"mouseup": "touchend",
"mousemove": "touchmove"
};
} else {
root.pointerType = "mouse";
}
}
if (translate[type]) type = translate[type];
if (!document.addEventListener) { // IE
return "on" + type;
} else {
return type;
}
};
})();
/// Event wrappers to keep track of all events placed in the window.
var wrappers = {};
var counter = 0;
var getID = function(object) {
if (object === window) return "#window";
if (object === document) return "#document";
if (!object) return createError("Missing target on listener!");
if (!object.uniqueID) object.uniqueID = "id" + counter ++;
return object.uniqueID;
};
/// Detect platforms native *EventListener command.
var add = document.addEventListener ? "addEventListener" : "attachEvent";
var remove = document.removeEventListener ? "removeEventListener" : "detachEvent";
/*
Pointer.js
------------------------
Modified from; https://github.com/borismus/pointer.js
*/
root.createPointerEvent = function (event, self, preventRecord) {
var eventName = self.gesture;
var target = self.target;
var pts = event.changedTouches || root.proxy.getCoords(event);
if (pts.length) {
var pt = pts[0];
self.pointers = preventRecord ? [] : pts;
self.pageX = pt.pageX;
self.pageY = pt.pageY;
self.x = self.pageX;
self.y = self.pageY;
}
///
var newEvent = document.createEvent("Event");
newEvent.initEvent(eventName, true, true);
newEvent.originalEvent = event;
for (var k in self) newEvent[k] = self[k];
target.dispatchEvent(newEvent);
};
/// Allows *EventListener to use custom event proxies.
if (root.modifyEventListener) (function() {
var augmentEventListener = function(proto) {
var recall = function(trigger) { // overwrite native *EventListener's
var handle = trigger + "EventListener";
var handler = proto[handle];
proto[handle] = function (type, listener, useCapture) {
if (root.Gesture && root.Gesture._gestureHandlers[type]) { // capture custom events.
var configure = useCapture;
if (typeof(useCapture) === "object") {
configure.useCall = true;
} else { // convert to configuration object.
configure = {
useCall: true,
useCapture: useCapture
};
}
eventManager(this, type, listener, configure, trigger, true);
handler.call(this, type, listener, useCapture);
} else { // use native function.
handler.call(this, normalize(type), listener, useCapture);
}
};
};
recall("add");
recall("remove");
};
// NOTE: overwriting HTMLElement doesn't do anything in Firefox.
if (navigator.userAgent.match(/Firefox/)) {
// TODO: fix Firefox for the general case.
augmentEventListener(HTMLDivElement.prototype);
augmentEventListener(HTMLCanvasElement.prototype);
} else {
augmentEventListener(HTMLElement.prototype);
}
augmentEventListener(document);
augmentEventListener(window);
})();
/// Allows querySelectorAll and other NodeLists to perform *EventListener commands in bulk.
if (root.modifySelectors) (function() {
var proto = NodeList.prototype;
proto.removeEventListener = function(type, listener, useCapture) {
for (var n = 0, length = this.length; n < length; n ++) {
this[n].removeEventListener(type, listener, useCapture);
}
};
proto.addEventListener = function(type, listener, useCapture) {
for (var n = 0, length = this.length; n < length; n ++) {
this[n].addEventListener(type, listener, useCapture);
}
};
})();
return root;
})(Event);
/*
----------------------------------------------------
Event.proxy : 0.4.2 : 2012/07/29 : MIT License
----------------------------------------------------
https://github.com/mudcube/Event.js
----------------------------------------------------
Pointer Gestures
----------------------------------------------------
1 : click, dblclick, dbltap
1+ : tap, taphold, drag, swipe
2+ : pinch, rotate
----------------------------------------------------
Gyroscope Gestures
----------------------------------------------------
* shake
----------------------------------------------------
Fixes issues with
----------------------------------------------------
* mousewheel-Firefox uses DOMMouseScroll and does not return wheelDelta.
* devicemotion-Fixes issue where event.acceleration is not returned.
----------------------------------------------------
Ideas for the future
----------------------------------------------------
* Keyboard, GamePad, and other input abstractions.
* Event batching - i.e. for every x fingers down a new gesture is created.
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
/*
Create a new pointer gesture instance.
*/
root.pointerSetup = function(conf, self) {
/// Configure.
conf.doc = conf.target.ownerDocument || conf.target; // Associated document.
conf.minFingers = conf.minFingers || conf.fingers || 1; // Minimum required fingers.
conf.maxFingers = conf.maxFingers || conf.fingers || Infinity; // Maximum allowed fingers.
conf.position = conf.position || "relative"; // Determines what coordinate system points are returned.
/// Convenience data.
self = self || {};
self.gesture = conf.gesture;
self.target = conf.target;
self.pointerType = Event.pointerType;
///
if (Event.modifyEventListener && conf.fromOverwrite) conf.listener = Event.createPointerEvent;
/// Convenience commands.
var fingers = 0;
var type = self.gesture.indexOf("pointer") === 0 && Event.modifyEventListener ? "pointer" : "mouse";
self.proxy = function(listener) {
self.defaultListener = conf.listener;
conf.listener = listener;
listener(conf.event, self);
};
self.remove = function() {
if (conf.onPointerDown) Event.remove(conf.target, type + "down", conf.onPointerDown);
if (conf.onPointerMove) Event.remove(conf.doc, type + "move", conf.onPointerMove);
if (conf.onPointerUp) Event.remove(conf.doc, type + "up", conf.onPointerUp);
};
self.resume = function(opt) {
if (conf.onPointerMove && (!opt || opt.move)) Event.add(conf.doc, type + "move", conf.onPointerMove);
if (conf.onPointerUp && (!opt || opt.move)) Event.add(conf.doc, type + "up", conf.onPointerUp);
conf.fingers = fingers;
};
self.pause = function(opt) {
fingers = conf.fingers;
if (conf.onPointerMove && (!opt || opt.move)) Event.remove(conf.doc, type + "move", conf.onPointerMove);
if (conf.onPointerUp && (!opt || opt.up)) Event.remove(conf.doc, type + "up", conf.onPointerUp);
conf.fingers = 0;
};
///
return self;
};
/*
Begin proxied pointer command.
*/
root.pointerStart = function(event, self, conf) {
var addTouchStart = function(touch, sid) {
var bbox = conf.bbox;
var pt = track[sid] = {};
///
switch(conf.position) {
case "absolute": // Absolute from within window.
pt.offsetX = 0;
pt.offsetY = 0;
break;
case "difference": // Relative from origin.
pt.offsetX = touch.pageX;
pt.offsetY = touch.pageY;
break;
case "move": // Move target element.
pt.offsetX = touch.pageX - bbox.x1;
pt.offsetY = touch.pageY - bbox.y1;
break;
default: // Relative from within target.
pt.offsetX = bbox.x1;
pt.offsetY = bbox.y1;
break;
}
///
if (conf.position === "relative") {
var x = (touch.pageX + bbox.scrollLeft - pt.offsetX) * bbox.scaleX;
var y = (touch.pageY + bbox.scrollTop - pt.offsetY) * bbox.scaleY;
} else {
var x = (touch.pageX - pt.offsetX);
var y = (touch.pageY - pt.offsetY);
}
///
pt.rotation = 0;
pt.scale = 1;
pt.startTime = pt.moveTime = (new Date).getTime();
pt.move = { x: x, y: y };
pt.start = { x: x, y: y };
///
conf.fingers ++;
};
///
conf.event = event;
if (self.defaultListener) {
conf.listener = self.defaultListener;
delete self.defaultListener;
}
///
var isTouchStart = !conf.fingers;
var track = conf.tracker;
var touches = event.changedTouches || root.getCoords(event);
var length = touches.length;
// Adding touch events to tracking.
for (var i = 0; i < length; i ++) {
var touch = touches[i];
var sid = touch.identifier || Infinity; // Touch ID.
// Track the current state of the touches.
if (conf.fingers) {
if (conf.fingers >= conf.maxFingers) {
var ids = [];
for (var sid in conf.tracker) ids.push(sid);
self.identifier = ids.join(",");
return isTouchStart;
}
var fingers = 0; // Finger ID.
for (var rid in track) {
// Replace removed finger.
if (track[rid].up) {
delete track[rid];
addTouchStart(touch, sid);
conf.cancel = true;
break;
}
fingers ++;
}
// Add additional finger.
if (track[sid]) continue;
addTouchStart(touch, sid);
} else { // Start tracking fingers.
track = conf.tracker = {};
self.bbox = conf.bbox = root.getBoundingBox(conf.target);
conf.fingers = 0;
conf.cancel = false;
addTouchStart(touch, sid);
}
}
///
var ids = [];
for (var sid in conf.tracker) ids.push(sid);
self.identifier = ids.join(",");
///
return isTouchStart;
};
/*
End proxied pointer command.
*/
root.pointerEnd = function(event, self, conf, onPointerUp) {
// Record changed touches have ended (iOS changedTouches is not reliable).
var touches = event.touches || [];
var length = touches.length;
var exists = {};
for (var i = 0; i < length; i ++) {
var touch = touches[i];
exists[touch.identifier || Infinity] = true;
}
for (var key in conf.tracker) {
var track = conf.tracker[key];
if (!exists[key] && !track.up) {
if (onPointerUp) { // add changedTouches to mouse.
event.changedTouches = [{
pageX: track.pageX,
pageY: track.pageY,
identifier: key === "Infinity" ? Infinity : key
}];
onPointerUp(event, "up");
}
conf.tracker[key].up = true;
conf.fingers --;
}
}
/*
// This should work but fails in Safari on iOS4 so not using it.
var touches = event.changedTouches || root.getCoords(event);
var length = touches.length;
// Record changed touches have ended (this should work).
for (var i = 0; i < length; i ++) {
var touch = touches[i];
var sid = touch.identifier || Infinity;
if (conf.tracker[sid]) {
conf.tracker[sid].up = true;
conf.fingers --;
}
}
*/
// Wait for all fingers to be released.
if (conf.fingers !== 0) return false;
// Record total number of fingers gesture used.
var ids = [];
conf.gestureFingers = 0;
for (var sid in conf.tracker) {
conf.gestureFingers ++;
ids.push(sid);
}
self.identifier = ids.join(",");
// Our pointer gesture has ended.
return true;
};
/*
Returns mouse coords in an array to match event.*Touches
------------------------------------------------------------
var touch = event.changedTouches || root.getCoords(event);
*/
root.getCoords = function(event) {
if (typeof(event.pageX) !== "undefined") { // Desktop browsers.
root.getCoords = function(event) {
return Array({
type: "mouse",
x: event.pageX,
y: event.pageY,
pageX: event.pageX,
pageY: event.pageY,
identifier: Infinity
});
};
} else { // Internet Explorer <= 8.0
root.getCoords = function(event) {
event = event || window.event;
return Array({
type: "mouse",
x: event.clientX + document.documentElement.scrollLeft,
y: event.clientY + document.documentElement.scrollTop,
pageX: event.clientX + document.documentElement.scrollLeft,
pageY: event.clientY + document.documentElement.scrollTop,
identifier: Infinity
});
};
}
return root.getCoords(event);
};
/*
Returns single coords in an object.
------------------------------------------------------------
var mouse = root.getCoord(event);
*/
root.getCoord = function(event) {
if ("ontouchstart" in window) { // Mobile browsers.
var pX = 0;
var pY = 0;
root.getCoord = function(event) {
var touches = event.changedTouches;
if (touches.length) { // ontouchstart + ontouchmove
return {
x: pX = touches[0].pageX,
y: pY = touches[0].pageY
};
} else { // ontouchend
return {
x: pX,
y: pY
};
}
};
} else if(typeof(event.pageX) !== "undefined" && typeof(event.pageY) !== "undefined") { // Desktop browsers.
root.getCoord = function(event) {
return {
x: event.pageX,
y: event.pageY
};
};
} else { // Internet Explorer <=8.0
root.getCoord = function(event) {
event = event || window.event;
return {
x: event.clientX + document.documentElement.scrollLeft,
y: event.clientY + document.documentElement.scrollTop
};
};
}
return root.getCoord(event);
};
/*
Get target scale and position in space.
*/
root.getBoundingBox = function(o) {
if (o === window || o === document) o = document.body;
///
var bbox = {
x1: 0,
y1: 0,
x2: 0,
y2: 0,
scrollLeft: 0,
scrollTop: 0
};
///
if (o === document.body) {
bbox.height = window.innerHeight;
bbox.width = window.innerWidth;
} else {
bbox.height = o.offsetHeight;
bbox.width = o.offsetWidth;
}
/// Get the scale of the element.
bbox.scaleX = o.width / bbox.width || 1;
bbox.scaleY = o.height / bbox.height || 1;
/// Get the offset of element.
var tmp = o;
while (tmp !== null) {
bbox.x1 += tmp.offsetLeft;
bbox.y1 += tmp.offsetTop;
tmp = tmp.offsetParent;
};
/// Get the scroll of container element.
var tmp = o.parentNode;
while (tmp !== null) {
if (tmp === document.body) break;
if (tmp.scrollTop === undefined) break;
bbox.scrollLeft += tmp.scrollLeft;
bbox.scrollTop += tmp.scrollTop;
tmp = tmp.parentNode;
};
/// Record the extent of box.
bbox.x2 = bbox.x1 + bbox.width;
bbox.y2 = bbox.y1 + bbox.height;
///
return bbox;
};
/*
Keep track of metaKey, the proper ctrlKey for users platform.
*/
(function() {
var agent = navigator.userAgent.toLowerCase();
var mac = agent.indexOf("macintosh") !== -1;
if (mac && agent.indexOf("khtml") !== -1) { // chrome, safari.
var watch = { 91: true, 93: true };
} else if (mac && agent.indexOf("firefox") !== -1) { // mac firefox.
var watch = { 224: true };
} else { // windows, linux, or mac opera.
var watch = { 17: true };
}
root.isMetaKey = function(event) {
return !!watch[event.keyCode];
};
root.metaTracker = function(event) {
if (watch[event.keyCode]) {
root.metaKey = event.type === "keydown";
}
};
})();
return root;
})(Event.proxy);
/*
"Click" event proxy.
----------------------------------------------------
Event.add(window, "click", function(event, self) {});
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
root.click = function(conf) {
conf.maxFingers = conf.maxFingers || conf.fingers || 1;
// Setting up local variables.
var EVENT;
// Tracking the events.
conf.onPointerDown = function (event) {
if (root.pointerStart(event, self, conf)) {
Event.add(conf.doc, "mousemove", conf.onPointerMove).listener(event);
Event.add(conf.doc, "mouseup", conf.onPointerUp);
}
};
conf.onPointerMove = function (event) {
EVENT = event;
};
conf.onPointerUp = function(event) {
if (root.pointerEnd(event, self, conf)) {
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
Event.remove(conf.doc, "mouseup", conf.onPointerUp);
if (EVENT.cancelBubble && ++ EVENT.bubble > 1) return;
var pointers = EVENT.changedTouches || root.getCoords(EVENT);
var pointer = pointers[0];
var bbox = conf.bbox;
var newbbox = root.getBoundingBox(conf.target);
if (conf.position === "relative") {
var ax = (pointer.pageX + bbox.scrollLeft - bbox.x1) * bbox.scaleX;
var ay = (pointer.pageY + bbox.scrollTop - bbox.y1) * bbox.scaleY;
} else {
var ax = (pointer.pageX - bbox.x1);
var ay = (pointer.pageY - bbox.y1);
}
if (ax > 0 && ax < bbox.width && // Within target coordinates.
ay > 0 && ay < bbox.height &&
bbox.scrollTop === newbbox.scrollTop) {
conf.listener(EVENT, self);
}
}
};
// Generate maintenance commands, and other configurations.
var self = root.pointerSetup(conf);
self.state = "click";
// Attach events.
Event.add(conf.target, "mousedown", conf.onPointerDown);
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.click = root.click;
return root;
})(Event.proxy);
/*
"Double-Click" aka "Double-Tap" event proxy.
----------------------------------------------------
Event.add(window, "dblclick", function(event, self) {});
----------------------------------------------------
Touch an target twice for <= 700ms, with less than 25 pixel drift.
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
root.dbltap =
root.dblclick = function(conf) {
conf.maxFingers = conf.maxFingers || conf.fingers || 1;
// Setting up local variables.
var delay = 700; // in milliseconds
var time0, time1, timeout;
var pointer0, pointer1;
// Tracking the events.
conf.onPointerDown = function (event) {
var pointers = event.changedTouches || root.getCoords(event);
if (time0 && !time1) { // Click #2
pointer1 = pointers[0];
time1 = (new Date).getTime() - time0;
} else { // Click #1
pointer0 = pointers[0];
time0 = (new Date).getTime();
time1 = 0;
clearTimeout(timeout);
timeout = setTimeout(function() {
time0 = 0;
}, delay);
}
if (root.pointerStart(event, self, conf)) {
Event.add(conf.doc, "mousemove", conf.onPointerMove).listener(event);
Event.add(conf.doc, "mouseup", conf.onPointerUp);
}
};
conf.onPointerMove = function (event) {
if (time0 && !time1) {
var pointers = event.changedTouches || root.getCoords(event);
pointer1 = pointers[0];
}
var bbox = conf.bbox;
if (conf.position === "relative") {
var ax = (pointer1.pageX + bbox.scrollLeft - bbox.x1) * bbox.scaleX;
var ay = (pointer1.pageY + bbox.scrollTop - bbox.y1) * bbox.scaleY;
} else {
var ax = (pointer1.pageX - bbox.x1);
var ay = (pointer1.pageY - bbox.y1);
}
if (!(ax > 0 && ax < bbox.width && // Within target coordinates..
ay > 0 && ay < bbox.height &&
Math.abs(pointer1.pageX - pointer0.pageX) <= 25 && // Within drift deviance.
Math.abs(pointer1.pageY - pointer0.pageY) <= 25)) {
// Cancel out this listener.
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
clearTimeout(timeout);
time0 = time1 = 0;
}
};
conf.onPointerUp = function(event) {
if (root.pointerEnd(event, self, conf)) {
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
Event.remove(conf.doc, "mouseup", conf.onPointerUp);
}
if (time0 && time1) {
if (time1 <= delay && !(event.cancelBubble && ++event.bubble > 1)) {
self.state = conf.gesture;
conf.listener(event, self);
}
clearTimeout(timeout);
time0 = time1 = 0;
}
};
// Generate maintenance commands, and other configurations.
var self = root.pointerSetup(conf);
self.state = "dblclick";
// Attach events.
Event.add(conf.target, "mousedown", conf.onPointerDown);
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.dbltap = root.dbltap;
Event.Gesture._gestureHandlers.dblclick = root.dblclick;
return root;
})(Event.proxy);
/*
"Drag" event proxy (1+ fingers).
----------------------------------------------------
CONFIGURE: maxFingers, position.
----------------------------------------------------
Event.add(window, "drag", function(event, self) {
console.log(self.gesture, self.state, self.start, self.x, self.y, self.bbox);
});
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
root.dragElement = function(that, event) {
root.drag({
event: event,
target: that,
position: "move",
listener: function(event, self) {
that.style.left = self.x + "px";
that.style.top = self.y + "px";
}
});
};
root.drag = function(conf) {
conf.gesture = "drag";
conf.onPointerDown = function (event) {
if (root.pointerStart(event, self, conf)) {
Event.add(conf.doc, "mousemove", conf.onPointerMove);
Event.add(conf.doc, "mouseup", conf.onPointerUp);
}
// Process event listener.
conf.onPointerMove(event, "down");
};
conf.onPointerMove = function (event, state) {
var bbox = conf.bbox;
var touches = event.changedTouches || root.getCoords(event);
var length = touches.length;
for (var i = 0; i < length; i ++) {
var touch = touches[i];
var identifier = touch.identifier || Infinity;
var pt = conf.tracker[identifier];
// Identifier defined outside of listener.
if (!pt) continue;
pt.pageX = touch.pageX;
pt.pageY = touch.pageY;
// Record data.
self.state = state || "move";
self.identifier = identifier;
self.start = pt.start;
self.fingers = 1; // TODO(mud): option to track as single set, or individually.
if (conf.position === "relative") {
self.x = (pt.pageX + bbox.scrollLeft - pt.offsetX) * bbox.scaleX;
self.y = (pt.pageY + bbox.scrollTop - pt.offsetY) * bbox.scaleY;
} else {
self.x = (pt.pageX - pt.offsetX);
self.y = (pt.pageY - pt.offsetY);
}
///
conf.listener(event, self);
}
};
conf.onPointerUp = function(event) {
// Remove tracking for touch.
if (root.pointerEnd(event, self, conf, conf.onPointerMove)) {
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
Event.remove(conf.doc, "mouseup", conf.onPointerUp);
}
};
// Generate maintenance commands, and other configurations.
var self = root.pointerSetup(conf);
// Attach events.
if (conf.event) {
conf.onPointerDown(conf.event);
} else {
Event.add(conf.target, "mousedown", conf.onPointerDown);
}
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.drag = root.drag;
return root;
})(Event.proxy);
/*
"Gesture" event proxy (2+ fingers).
----------------------------------------------------
CONFIGURE: minFingers, maxFingers.
----------------------------------------------------
Event.add(window, "gesture", function(event, self) {
console.log(self.rotation, self.scale, self.fingers, self.state);
});
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
var RAD_DEG = Math.PI / 180;
root.gesture = function(conf) {
conf.minFingers = conf.minFingers || conf.fingers || 2;
// Tracking the events.
conf.onPointerDown = function (event) {
var fingers = conf.fingers;
if (root.pointerStart(event, self, conf)) {
Event.add(conf.doc, "mousemove", conf.onPointerMove);
Event.add(conf.doc, "mouseup", conf.onPointerUp);
}
// Record gesture start.
if (conf.fingers === conf.minFingers && fingers !== conf.fingers) {
self.fingers = conf.minFingers;
self.scale = 1;
self.rotation = 0;
self.state = "start";
var sids = ""; //- FIXME(mud): can generate duplicate IDs.
for (var key in conf.tracker) sids += key;
self.identifier = parseInt(sids);
conf.listener(event, self);
}
};
///
conf.onPointerMove = function (event, state) {
var bbox = conf.bbox;
var points = conf.tracker;
var touches = event.changedTouches || root.getCoords(event);
var length = touches.length;
// Update tracker coordinates.
for (var i = 0; i < length; i ++) {
var touch = touches[i];
var sid = touch.identifier || Infinity;
var pt = points[sid];
// Check whether "pt" is used by another gesture.
if (!pt) continue;
// Find the actual coordinates.
if (conf.position === "relative") {
pt.move.x = (touch.pageX + bbox.scrollLeft - bbox.x1) * bbox.scaleX;
pt.move.y = (touch.pageY + bbox.scrollTop - bbox.y1) * bbox.scaleY;
} else {
pt.move.x = (touch.pageX - bbox.x1);
pt.move.y = (touch.pageY - bbox.y1);
}
}
///
if (conf.fingers < conf.minFingers) return;
///
var touches = [];
var scale = 0;
var rotation = 0;
/// Calculate centroid of gesture.
var centroidx = 0;
var centroidy = 0;
var length = 0;
for (var sid in points) {
var touch = points[sid];
if (touch.up) continue;
centroidx += touch.move.x;
centroidy += touch.move.y;
length ++;
}
centroidx /= length;
centroidy /= length;
///
for (var sid in points) {
var touch = points[sid];
if (touch.up) continue;
var start = touch.start;
if (!start.distance) {
var dx = start.x - centroidx;
var dy = start.y - centroidy;
start.distance = Math.sqrt(dx * dx + dy * dy);
start.angle = Math.atan2(dx, dy) / RAD_DEG;
}
// Calculate scale.
var dx = touch.move.x - centroidx;
var dy = touch.move.y - centroidy;
var distance = Math.sqrt(dx * dx + dy * dy);
scale += distance / start.distance;
// Calculate rotation.
var angle = Math.atan2(dx, dy) / RAD_DEG;
var rotate = (start.angle - angle + 360) % 360 - 180;
touch.DEG2 = touch.DEG1; // Previous degree.
touch.DEG1 = rotate > 0 ? rotate : -rotate; // Current degree.
if (typeof(touch.DEG2) !== "undefined") {
if (rotate > 0) {
touch.rotation += touch.DEG1 - touch.DEG2;
} else {
touch.rotation -= touch.DEG1 - touch.DEG2;
}
rotation += touch.rotation;
}
// Attach current points to self.
touches.push(touch.move);
}
///
self.touches = touches;
self.fingers = conf.fingers;
self.scale = scale / conf.fingers;
self.rotation = rotation / conf.fingers;
self.state = "change";
conf.listener(event, self);
};
conf.onPointerUp = function(event) {
// Remove tracking for touch.
var fingers = conf.fingers;
if (root.pointerEnd(event, self, conf)) {
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
Event.remove(conf.doc, "mouseup", conf.onPointerUp);
}
// Check whether fingers has dropped below minFingers.
if (fingers === conf.minFingers && conf.fingers < conf.minFingers) {
self.fingers = conf.fingers;
self.state = "end";
conf.listener(event, self);
}
};
// Generate maintenance commands, and other configurations.
var self = root.pointerSetup(conf);
// Attach events.
Event.add(conf.target, "mousedown", conf.onPointerDown);
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.gesture = root.gesture;
return root;
})(Event.proxy);
/*
"Pointer" event proxy (1+ fingers).
----------------------------------------------------
CONFIGURE: minFingers, maxFingers.
----------------------------------------------------
Event.add(window, "gesture", function(event, self) {
console.log(self.rotation, self.scale, self.fingers, self.state);
});
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
root.pointerdown =
root.pointermove =
root.pointerup = function(conf) {
if (conf.target.isPointerEmitter) return;
// Tracking the events.
var isDown = true;
conf.onPointerDown = function (event) {
isDown = false;
self.gesture = "pointerdown";
conf.listener(event, self);
};
conf.onPointerMove = function (event) {
self.gesture = "pointermove";
conf.listener(event, self, isDown);
};
conf.onPointerUp = function (event) {
isDown = true;
self.gesture = "pointerup";
conf.listener(event, self, true);
};
// Generate maintenance commands, and other configurations.
var self = root.pointerSetup(conf);
// Attach events.
Event.add(conf.target, "mousedown", conf.onPointerDown);
Event.add(conf.target, "mousemove", conf.onPointerMove);
Event.add(conf.doc, "mouseup", conf.onPointerUp);
// Return this object.
conf.target.isPointerEmitter = true;
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.pointerdown = root.pointerdown;
Event.Gesture._gestureHandlers.pointermove = root.pointermove;
Event.Gesture._gestureHandlers.pointerup = root.pointerup;
return root;
})(Event.proxy);
/*
"Device Motion" and "Shake" event proxy.
----------------------------------------------------
http://developer.android.com/reference/android/hardware/SensorEvent.html#values
----------------------------------------------------
Event.add(window, "shake", function(event, self) {});
Event.add(window, "devicemotion", function(event, self) {
console.log(self.acceleration, self.accelerationIncludingGravity);
});
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
root.shake = function(conf) {
// Externally accessible data.
var self = {
gesture: "devicemotion",
acceleration: {},
accelerationIncludingGravity: {},
target: conf.target,
listener: conf.listener,
remove: function() {
window.removeEventListener('devicemotion', onDeviceMotion, false);
}
};
// Setting up local variables.
var threshold = 4; // Gravitational threshold.
var timeout = 1000; // Timeout between shake events.
var timeframe = 200; // Time between shakes.
var shakes = 3; // Minimum shakes to trigger event.
var lastShake = (new Date).getTime();
var gravity = { x: 0, y: 0, z: 0 };
var delta = {
x: { count: 0, value: 0 },
y: { count: 0, value: 0 },
z: { count: 0, value: 0 }
};
// Tracking the events.
var onDeviceMotion = function(e) {
var alpha = 0.8; // Low pass filter.
var o = e.accelerationIncludingGravity;
gravity.x = alpha * gravity.x + (1 - alpha) * o.x;
gravity.y = alpha * gravity.y + (1 - alpha) * o.y;
gravity.z = alpha * gravity.z + (1 - alpha) * o.z;
self.accelerationIncludingGravity = gravity;
self.acceleration.x = o.x - gravity.x;
self.acceleration.y = o.y - gravity.y;
self.acceleration.z = o.z - gravity.z;
///
if (conf.gesture === "devicemotion") {
conf.listener(e, self);
return;
}
var data = "xyz";
var now = (new Date).getTime();
for (var n = 0, length = data.length; n < length; n ++) {
var letter = data[n];
var ACCELERATION = self.acceleration[letter];
var DELTA = delta[letter];
var abs = Math.abs(ACCELERATION);
/// Check whether another shake event was recently registered.
if (now - lastShake < timeout) continue;
/// Check whether delta surpasses threshold.
if (abs > threshold) {
var idx = now * ACCELERATION / abs;
var span = Math.abs(idx + DELTA.value);
// Check whether last delta was registered within timeframe.
if (DELTA.value && span < timeframe) {
DELTA.value = idx;
DELTA.count ++;
// Check whether delta count has enough shakes.
if (DELTA.count === shakes) {
conf.listener(e, self);
// Reset tracking.
lastShake = now;
DELTA.value = 0;
DELTA.count = 0;
}
} else {
// Track first shake.
DELTA.value = idx;
DELTA.count = 1;
}
}
}
};
// Attach events.
if (!window.addEventListener) return;
window.addEventListener('devicemotion', onDeviceMotion, false);
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.shake = root.shake;
return root;
})(Event.proxy);
/*
"Swipe" event proxy (1+ fingers).
----------------------------------------------------
CONFIGURE: snap, threshold, maxFingers.
----------------------------------------------------
Event.add(window, "swipe", function(event, self) {
console.log(self.velocity, self.angle);
});
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
var RAD_DEG = Math.PI / 180;
root.swipe = function(conf) {
conf.snap = conf.snap || 90; // angle snap.
conf.threshold = conf.threshold || 1; // velocity threshold.
// Tracking the events.
conf.onPointerDown = function (event) {
if (root.pointerStart(event, self, conf)) {
Event.add(conf.doc, "mousemove", conf.onPointerMove).listener(event);
Event.add(conf.doc, "mouseup", conf.onPointerUp);
}
};
conf.onPointerMove = function (event) {
var touches = event.changedTouches || root.getCoords(event);
var length = touches.length;
for (var i = 0; i < length; i ++) {
var touch = touches[i];
var sid = touch.identifier || Infinity;
var o = conf.tracker[sid];
// Identifier defined outside of listener.
if (!o) continue;
o.move.x = touch.pageX;
o.move.y = touch.pageY;
o.moveTime = (new Date).getTime();
}
};
conf.onPointerUp = function(event) {
if (root.pointerEnd(event, self, conf)) {
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
Event.remove(conf.doc, "mouseup", conf.onPointerUp);
///
var velocity1;
var velocity2
var degree1;
var degree2;
/// Calculate centroid of gesture.
var start = { x: 0, y: 0 };
var endx = 0;
var endy = 0;
var length = 0;
///
for (var sid in conf.tracker) {
var touch = conf.tracker[sid];
var xdist = touch.move.x - touch.start.x;
var ydist = touch.move.y - touch.start.y;
endx += touch.move.x;
endy += touch.move.y;
start.x += touch.start.x;
start.y += touch.start.y;
length ++;
var distance = Math.sqrt(xdist * xdist + ydist * ydist);
var ms = touch.moveTime - touch.startTime;
var degree2 = Math.atan2(xdist, ydist) / RAD_DEG + 180;
var velocity2 = ms ? distance / ms : 0;
if (typeof(degree1) === "undefined") {
degree1 = degree2;
velocity1 = velocity2;
} else if (Math.abs(degree2 - degree1) <= 20) {
degree1 = (degree1 + degree2) / 2;
velocity1 = (velocity1 + velocity2) / 2;
} else {
return;
}
}
///
if (velocity1 > conf.threshold) {
start.x /= length;
start.y /= length;
self.start = start;
self.x = endx / length;
self.y = endy / length;
self.angle = -((((degree1 / conf.snap + 0.5) >> 0) * conf.snap || 360) - 360);
self.velocity = velocity1;
self.fingers = conf.gestureFingers;
self.state = "swipe";
conf.listener(event, self);
}
}
};
// Generate maintenance commands, and other configurations.
var self = root.pointerSetup(conf);
// Attach events.
Event.add(conf.target, "mousedown", conf.onPointerDown);
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.swipe = root.swipe;
return root;
})(Event.proxy);
/*
"Tap" and "Longpress" event proxy.
----------------------------------------------------
CONFIGURE: delay (longpress), timeout (tap).
----------------------------------------------------
Event.add(window, "tap", function(event, self) {
console.log(self.fingers);
});
----------------------------------------------------
multi-finger tap // touch an target for <= 250ms.
multi-finger longpress // touch an target for >= 500ms
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
root.tap =
root.longpress = function(conf) {
conf.delay = conf.delay || 500;
conf.timeout = conf.timeout || 250;
// Setting up local variables.
var timestamp, timeout;
// Tracking the events.
conf.onPointerDown = function (event) {
if (root.pointerStart(event, self, conf)) {
timestamp = (new Date).getTime();
// Initialize event listeners.
Event.add(conf.doc, "mousemove", conf.onPointerMove).listener(event);
Event.add(conf.doc, "mouseup", conf.onPointerUp);
// Make sure this is a "longpress" event.
if (conf.gesture !== "longpress") return;
timeout = setTimeout(function() {
if (event.cancelBubble && ++event.bubble > 1) return;
// Make sure no fingers have been changed.
var fingers = 0;
for (var key in conf.tracker) {
if (conf.tracker[key].end === true) return;
if (conf.cancel) return;
fingers ++;
}
// Send callback.
self.state = "start";
self.fingers = fingers;
conf.listener(event, self);
}, conf.delay);
}
};
conf.onPointerMove = function (event) {
var bbox = conf.bbox;
var touches = event.changedTouches || root.getCoords(event);
var length = touches.length;
for (var i = 0; i < length; i ++) {
var touch = touches[i];
var identifier = touch.identifier || Infinity;
var pt = conf.tracker[identifier];
if (!pt) continue;
if (conf.position === "relative") {
var x = (touch.pageX + bbox.scrollLeft - bbox.x1) * bbox.scaleX;
var y = (touch.pageY + bbox.scrollTop - bbox.y1) * bbox.scaleY;
} else {
var x = (touch.pageX - bbox.x1);
var y = (touch.pageY - bbox.y1);
}
if (!(x > 0 && x < bbox.width && // Within target coordinates..
y > 0 && y < bbox.height &&
Math.abs(x - pt.start.x) <= 25 && // Within drift deviance.
Math.abs(y - pt.start.y) <= 25)) {
// Cancel out this listener.
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
conf.cancel = true;
return;
}
}
};
conf.onPointerUp = function(event) {
if (root.pointerEnd(event, self, conf)) {
clearTimeout(timeout);
Event.remove(conf.doc, "mousemove", conf.onPointerMove);
Event.remove(conf.doc, "mouseup", conf.onPointerUp);
if (event.cancelBubble && ++event.bubble > 1) return;
// Callback release on longpress.
if (conf.gesture === "longpress") {
if (self.state === "start") {
self.state = "end";
conf.listener(event, self);
}
return;
}
// Cancel event due to movement.
if (conf.cancel) return;
// Ensure delay is within margins.
if ((new Date).getTime() - timestamp > conf.timeout) return;
// Send callback.
self.state = "tap";
self.fingers = conf.gestureFingers;
conf.listener(event, self);
}
};
// Generate maintenance commands, and other configurations.
var self = root.pointerSetup(conf);
// Attach events.
Event.add(conf.target, "mousedown", conf.onPointerDown);
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.tap = root.tap;
Event.Gesture._gestureHandlers.longpress = root.longpress;
return root;
})(Event.proxy);
/*
"Mouse Wheel" event proxy.
----------------------------------------------------
Event.add(window, "wheel", function(event, self) {
console.log(self.state, self.wheelDelta);
});
*/
if (typeof(Event) === "undefined") var Event = {};
if (typeof(Event.proxy) === "undefined") Event.proxy = {};
Event.proxy = (function(root) { "use strict";
root.wheel = function(conf) {
// Configure event listener.
var interval;
var timeout = conf.timeout || 150;
var count = 0;
// Externally accessible data.
var self = {
gesture: "wheel",
state: "start",
wheelDelta: 0,
target: conf.target,
listener: conf.listener,
remove: function() {
conf.target[remove](type, onMouseWheel, false);
}
};
// Tracking the events.
var onMouseWheel = function(event) {
event = event || window.event;
self.state = count++ ? "change" : "start";
self.wheelDelta = event.detail ? event.detail * -20 : event.wheelDelta;
conf.listener(event, self);
clearTimeout(interval);
interval = setTimeout(function() {
count = 0;
self.state = "end";
self.wheelDelta = 0;
conf.listener(event, self);
}, timeout);
};
// Attach events.
var add = document.addEventListener ? "addEventListener" : "attachEvent";
var remove = document.removeEventListener ? "removeEventListener" : "detachEvent";
var type = Event.supports("mousewheel") ? "mousewheel" : "DOMMouseScroll";
conf.target[add](type, onMouseWheel, false);
// Return this object.
return self;
};
Event.Gesture = Event.Gesture || {};
Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {};
Event.Gesture._gestureHandlers.wheel = root.wheel;
return root;
})(Event.proxy);