diff --git a/lib/event.js b/lib/event.js index 65120712..e7790348 100644 --- a/lib/event.js +++ b/lib/event.js @@ -1,1904 +1,1829 @@ /* - ---------------------------------------------------- - 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); + ---------------------------------------------------- + Event.js : 1.1.3 : 2013/07/17 : MIT License + ---------------------------------------------------- + https://github.com/mudcube/Event.js + ---------------------------------------------------- + https://github.com/rykerwilliams/Event.js + ---------------------------------------------------- + 1 : click, dblclick, dbltap + 1+ : tap, longpress, drag, swipe + 2+ : pinch, rotate + : mousewheel, devicemotion, shake + ---------------------------------------------------- + Ideas for the future + ---------------------------------------------------- + * GamePad, and other input abstractions. + * Event batching - i.e. for every x fingers down a new gesture is created. + ---------------------------------------------------- + http://www.w3.org/TR/2011/WD-touch-events-20110505/ + ---------------------------------------------------- + +*/ - // Attempts to perform as fast as possible. - Event.add(type, listener, configure); - Event.remove(type, listener, configure); +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(eventjs) === "undefined") var eventjs = Event; - * 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. +(function(root) { "use strict"; - * 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); +// Add custom *EventListener commands to HTMLElements (set false to prevent funkiness). +root.modifyEventListener = true; - // 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, self.start, self.x, self.y); - }); - // "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; +// Add bulk *EventListener commands on NodeLists from querySelectorAll and others (set false to prevent funkiness). +root.modifySelectors = true; // Event maintenance. - root.add = function(target, type, listener, configure) { - return eventManager(target, type, listener, configure, "add"); - }; +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.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.stop = function(event) { + if (!event) return; + 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.prevent = function(event) { + if (!event) return; + if (event.preventDefault) event.preventDefault(); + if (event.preventManipulation) event.preventManipulation(); // MS + event.returnValue = false; // <= IE8 +}; - root.cancel = function(event) { - root.stop(event); - root.prevent(event); - }; +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; - } - }; +root.getEventSupport = 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; - }; +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]; - }; +var eventManager = function(target, type, listener, configure, trigger, fromOverwrite) { + configure = configure || {}; + // Check whether target is a configuration variable; + if (String(target) === "[object Object]") { + var data = target; + target = data.target; + type = data.type; + listener = data.listener; + delete data.target; + delete data.type; + delete data.listener; + for (var key in data) { + configure[key] = data[key]; + } + } + /// + if (!target || !type || !listener) return; + // 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); + setTimeout(listener, 1); + } + }, 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!", arguments); // No results. + if (target.length === 1) { // Single target. + target = target[0]; + } + } + + /// Handle multiple targets. + var event; + var events = {}; + if (target.length > 0 && target !== window) { + 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(target) !== "object") return createError("Target is not defined!", arguments); + if (typeof(listener) !== "function") return createError("Listener is not a function!", arguments); + // Generate a unique wrapper identifier. + var useCapture = configure.useCapture || false; + var id = getID(target) + "." + getID(listener) + "." + (useCapture ? 1 : 0); + // Handle the event. + if (root.Gesture && root.Gesture._gestureHandlers[type]) { // Fire custom event. + id = type + id; + 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]) { + wrappers[id].add(); + 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); + } + return wrappers[id]; + } else { // Fire native event. + var eventList = getEventList(type); + for (var n = 0, eventId; n < eventList.length; n ++) { + type = eventList[n]; + eventId = type + "." + id; + if (trigger === "remove") { // Remove event listener. + if (!wrappers[eventId]) continue; // Already removed. + target[remove](type, listener, useCapture); + delete wrappers[eventId]; + } else if (trigger === "add") { // Attach event listener. + if (wrappers[eventId]) return wrappers[eventId]; // Already attached. + target[add](type, listener, useCapture); + // Record wrapper. + wrappers[eventId] = { + id: eventId, + type: type, + target: target, + listener: listener, + remove: function() { + for (var n = 0; n < eventList.length; n ++) { + root.remove(target, eventList[n], listener, configure); + } + } + }; + } + } + return wrappers[eventId]; + } +}; /// 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(); - } - } - }; - }; +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); - }; +var createError = function(message, data) { + if (typeof(console) === "undefined") return; + if (typeof(console.error) === "undefined") return; + console.error(message, data); +}; /// 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; - } - }; - })(); +var pointerDefs = { + "msPointer": [ "MSPointerDown", "MSPointerMove", "MSPointerUp" ], + "touch": [ "touchstart", "touchmove", "touchend" ], + "mouse": [ "mousedown", "mousemove", "mouseup" ] +}; + +var pointerDetect = { + // MSPointer + "MSPointerDown": 0, + "MSPointerMove": 1, + "MSPointerUp": 2, + // Touch + "touchstart": 0, + "touchmove": 1, + "touchend": 2, + // Mouse + "mousedown": 0, + "mousemove": 1, + "mouseup": 2 +}; + +var getEventSupport = (function() { + root.supports = {}; + if (window.navigator.msPointerEnabled) { + root.supports.msPointer = true; + } + if (root.getEventSupport("touchstart")) { + root.supports.touch = true; + } + if (root.getEventSupport("mousedown")) { + root.supports.mouse = true; + } +})(); + +var getEventList = (function() { + return function(type) { + var prefix = document.addEventListener ? "" : "on"; // IE + var idx = pointerDetect[type]; + if (isFinite(idx)) { + var types = []; + for (var key in root.supports) { + types.push(prefix + pointerDefs[key][idx]); + } + return types; + } else { + return [ prefix + 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; - }; +var wrappers = {}; +var counter = 0; +var getID = function(object) { + if (object === window) return "#window"; + if (object === document) return "#document"; + if (!object.uniqueID) object.uniqueID = "e" + counter ++; + return object.uniqueID; +}; /// Detect platforms native *EventListener command. - var add = document.addEventListener ? "addEventListener" : "attachEvent"; - var remove = document.removeEventListener ? "removeEventListener" : "detachEvent"; +var add = document.addEventListener ? "addEventListener" : "attachEvent"; +var remove = document.removeEventListener ? "removeEventListener" : "detachEvent"; - /* - Pointer.js - ------------------------ - Modified from; https://github.com/borismus/pointer.js - */ +/* + 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) { - if (k === "target") - continue; - newEvent[k] = self[k]; - } - target.dispatchEvent(newEvent); - }; +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) { + if (k === "target") continue; + newEvent[k] = self[k]; + } + /// + var type = newEvent.type; + if (root.Gesture && root.Gesture._gestureHandlers[type]) { // capture custom events. +// target.dispatchEvent(newEvent); + self.oldListener.call(target, newEvent, self, false); + } +}; /// Allows *EventListener to use custom event proxies. - if (root.modifyEventListener && window.HTMLElement) - (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); - })(); +if (root.modifyEventListener && window.HTMLElement) (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. + var types = getEventList(type); + for (var n = 0; n < types.length; n ++) { + handler.call(this, types[n], 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); - } - }; - })(); +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; +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. - */ + ---------------------------------------------------- + Event.proxy : 0.4.3 : 2013/07/17 : MIT License + ---------------------------------------------------- + https://github.com/mudcube/Event.js + ---------------------------------------------------- +*/ -if (typeof(Event) === "undefined") - var Event = {}; -if (typeof(Event.proxy) === "undefined") - Event.proxy = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +Event.proxy = (function(root) { "use strict"; - /* - Create a new pointer gesture instance. - */ +/* + 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. - delete conf.fingers; //- - /// 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.listener = conf.listener; - 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; - }; +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. + delete conf.fingers; //- + /// Convenience data. + self = self || {}; + self.enabled = true; + self.gesture = conf.gesture; + self.target = conf.target; + self.env = conf.env; + /// + if (Event.modifyEventListener && conf.fromOverwrite) { + conf.oldListener = conf.listener; + conf.listener = Event.createPointerEvent; + } + /// Convenience commands. + var fingers = 0; + var type = self.gesture.indexOf("pointer") === 0 && Event.modifyEventListener ? "pointer" : "mouse"; + if (conf.oldListener) self.oldListener = conf.oldListener; + self.listener = conf.listener; + self.proxy = function(listener) { + self.defaultListener = conf.listener; + conf.listener = listener; + listener(conf.event, self); + }; + self.add = function() { + if (self.enabled === true) return; + if (conf.onPointerDown) Event.add(conf.target, type + "down", conf.onPointerDown); + if (conf.onPointerMove) Event.add(conf.doc, type + "move", conf.onPointerMove); + if (conf.onPointerUp) Event.add(conf.doc, type + "up", conf.onPointerUp); + self.enabled = true; + }; + self.remove = function() { + if (self.enabled === false) return; + 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.reset(); + self.enabled = false; + }; + self.pause = function(opt) { + 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); + fingers = conf.fingers; + conf.fingers = 0; + }; + self.resume = function(opt) { + if (conf.onPointerMove && (!opt || opt.move)) Event.add(conf.doc, type + "move", conf.onPointerMove); + if (conf.onPointerUp && (!opt || opt.up)) Event.add(conf.doc, type + "up", conf.onPointerUp); + conf.fingers = fingers; + }; + self.reset = function() { + conf.tracker = {}; + conf.fingers = 0; + }; + /// + return self; +}; - /* - Begin proxied pointer command. - */ +/* + 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; - }; +var sp = Event.supports; +Event.pointerType = sp.mouse ? "mouse" : sp.touch ? "touch" : "mspointer"; +root.pointerStart = function(event, self, conf) { + var type = (event.type || "mousedown").toUpperCase(); + if (type.indexOf("MOUSE") === 0) Event.pointerType = "mouse"; + else if (type.indexOf("TOUCH") === 0) Event.pointerType = "touch"; + else if (type.indexOf("MSPOINTER") === 0) Event.pointerType = "mspointer"; + /// + 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 "differenceFromLast": // Since last coordinate recorded. + pt.offsetX = touch.pageX; + pt.offsetY = touch.pageY; + 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); + var y = (touch.pageY + bbox.scrollTop - pt.offsetY); + } 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. - */ +/* + 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]; - var sid = touch.identifier; - exists[sid || Infinity] = true; - } - for (var sid in conf.tracker) { - var track = conf.tracker[sid]; - if (exists[sid] || track.up) - continue; - if (onPointerUp) { // add changedTouches to mouse. - onPointerUp({ - pageX: track.pageX, - pageY: track.pageY, - changedTouches: [{ - pageX: track.pageX, - pageY: track.pageY, - identifier: sid === "Infinity" ? Infinity : sid - }] - }, "up"); - } - track.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; - var track = conf.tracker[sid]; - if (track && !track.up) { - if (onPointerUp) { // add changedTouches to mouse. - onPointerUp({ - changedTouches: [{ - pageX: track.pageX, - pageY: track.pageY, - identifier: sid === "Infinity" ? Infinity : sid - }] - }, "up"); - } - track.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; - }; +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]; + var sid = touch.identifier; + exists[sid || Infinity] = true; + } + for (var sid in conf.tracker) { + var track = conf.tracker[sid]; + if (exists[sid] || track.up) continue; + if (onPointerUp) { // add changedTouches to mouse. + onPointerUp({ + pageX: track.pageX, + pageY: track.pageY, + changedTouches: [{ + pageX: track.pageX, + pageY: track.pageY, + identifier: sid === "Infinity" ? Infinity : sid + }] + }, "up"); + } + track.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; + var track = conf.tracker[sid]; + if (track && !track.up) { + if (onPointerUp) { // add changedTouches to mouse. + onPointerUp({ + changedTouches: [{ + pageX: track.pageX, + pageY: track.pageY, + identifier: sid === "Infinity" ? Infinity : sid + }] + }, "up"); + } + track.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); - */ +/* + 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); - }; +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: event.pointerId || Infinity // pointerId is MS + }); + }; + } 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); - */ +/* + 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); - }; +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 && 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. - */ +/* + 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; - }; +root.getBoundingBox = function(o) { + if (o === window || o === document) o = document.body; + /// + var bbox = {}; + var bcr = o.getBoundingClientRect(); + bbox.width = bcr.width; + bbox.height = bcr.height; + bbox.x1 = bcr.left; + bbox.y1 = bcr.top; + bbox.x2 = bbox.x1 + bbox.width; + bbox.y2 = bbox.y1 + bbox.height; + bbox.scaleX = bcr.width / o.offsetWidth || 1; + bbox.scaleY = bcr.height / o.offsetHeight || 1; + bbox.scrollLeft = 0; + bbox.scrollTop = 0; - /* - Keep track of metaKey, the proper ctrlKey for users platform. - */ + /// Get the scroll of container element. + var tmp = o.parentNode; + while (tmp !== null) { + if (tmp === document.body) break; + if (tmp.scrollTop === undefined) break; + var style = window.getComputedStyle(tmp); + var position = style.getPropertyValue("position"); + if (position === "absolute") { + break; + } else if (position === "fixed") { + bbox.scrollTop -= tmp.parentNode.scrollTop; + break; + } else { + bbox.scrollLeft += tmp.scrollLeft; + bbox.scrollTop += tmp.scrollTop; + } + tmp = tmp.parentNode; + }; + /// + return bbox; +}; - (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"; - } - }; - })(); +/* + Keep track of metaKey, the proper ctrlKey for users platform. +*/ - return root; +(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.metaTrackerReset = function() { + root.metaKey = false; + root.ctrlKey = false; + root.shiftKey = false; + root.altKey = false; + }; + root.metaTracker = function(event) { + var check = !!watch[event.keyCode]; + if (check) root.metaKey = event.type === "keydown"; + root.ctrlKey = event.ctrlKey; + root.shiftKey = event.shiftKey; + root.altKey = event.altKey; + return check; + }; +})(); + +return root; })(Event.proxy); /* - "Click" event proxy. - ---------------------------------------------------- - Event.add(window, "click", function(event, self) {}); - */ + ---------------------------------------------------- + "MutationObserver" event proxy. + ---------------------------------------------------- + Author: Selvakumar Arumugam (MIT LICENSE) + http://stackoverflow.com/questions/10868104/can-you-have-a-javascript-hook-trigger-after-a-dom-elements-style-object-change + ---------------------------------------------------- +*/ +if (typeof(Event) === "undefined") var Event = {}; -if (typeof(Event) === "undefined") - var Event = {}; -if (typeof(Event.proxy) === "undefined") - Event.proxy = {}; +Event.MutationObserver = (function() { + var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + var DOMAttrModifiedSupported = (function() { + var p = document.createElement("p"); + var flag = false; + var fn = function() { flag = true }; + if (p.addEventListener) { + p.addEventListener("DOMAttrModified", fn, false); + } else if (p.attachEvent) { + p.attachEvent("onDOMAttrModified", fn); + } else { + return false; + } + /// + p.setAttribute("id", "target"); + /// + return flag; + })(); + /// + return function(container, callback) { + if (MutationObserver) { + var options = { + subtree: false, + attributes: true + }; + var observer = new MutationObserver(function(mutations) { + mutations.forEach(function(e) { + callback.call(e.target, e.attributeName); + }); + }); + observer.observe(container, options) + } else if (DOMAttrModifiedSupported) { + Event.add(container, "DOMAttrModified", function(e) { + callback.call(container, e.attrName); + }); + } else if ("onpropertychange" in document.body) { + Event.add(container, "propertychange", function(e) { + callback.call(container, window.event.propertyName); + }); + } + } +})(); +/* + "Click" event proxy. + ---------------------------------------------------- + Event.add(window, "click", function(event, self) {}); +*/ -Event.proxy = (function(root) { - "use strict"; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; - 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.proxy = (function(root) { "use strict"; - Event.Gesture = Event.Gesture || {}; - Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; - Event.Gesture._gestureHandlers.click = root.click; +root.click = function(conf) { + conf.gesture = conf.gesture || "click"; + 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); + var ay = (pointer.pageY + bbox.scrollTop - bbox.y1); + } 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) { + /// + for (var key in conf.tracker) break; //- should be modularized? in dblclick too + var point = conf.tracker[key]; + self.x = point.start.x; + self.y = point.start.y; + /// + 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; +}; - return root; +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. - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +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; - }; +root.dbltap = +root.dblclick = function(conf) { + conf.gesture = conf.gesture || "dbltap"; + 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); + var ay = (pointer1.pageY + bbox.scrollTop - bbox.y1); + } 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; + for (var key in conf.tracker) break; + var point = conf.tracker[key]; + self.x = point.start.x; + self.y = point.start.y; + 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; +Event.Gesture = Event.Gesture || {}; +Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; +Event.Gesture._gestureHandlers.dbltap = root.dbltap; +Event.Gesture._gestureHandlers.dblclick = root.dblclick; - return root; +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); - }); - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +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"; - Event.prevent(event); - } - }); - }; +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"; + Event.prevent(event); + } + }); +}; - root.drag = function(conf) { - conf.gesture = "drag"; - conf.onPointerDown = function(event) { - if (root.pointerStart(event, self, conf)) { - if (!conf.monitor) { - 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) { - if (!conf.tracker) - return conf.onPointerDown(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]; - // 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 = conf.fingers; - 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)) { - if (!conf.monitor) { - 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); - if (conf.monitor) { - Event.add(conf.doc, "mousemove", conf.onPointerMove); - Event.add(conf.doc, "mouseup", conf.onPointerUp); - } - } - // Return this object. - return self; - }; +root.drag = function(conf) { + conf.gesture = "drag"; + conf.onPointerDown = function (event) { + if (root.pointerStart(event, self, conf)) { + if (!conf.monitor) { + 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) { + if (!conf.tracker) return conf.onPointerDown(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]; + // 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 = conf.fingers; + if (conf.position === "differenceFromLast") { + self.x = (pt.pageX - pt.offsetX); + self.y = (pt.pageY - pt.offsetY); + pt.offsetX = pt.pageX; + pt.offsetY = pt.pageY; + } else if (conf.position === "relative") { + self.x = (pt.pageX + bbox.scrollLeft - pt.offsetX); + self.y = (pt.pageY + bbox.scrollTop - pt.offsetY); + } 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)) { + if (!conf.monitor) { + 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); + if (conf.monitor) { + Event.add(conf.doc, "mousemove", conf.onPointerMove); + Event.add(conf.doc, "mouseup", conf.onPointerUp); + } + } + // Return this object. + return self; +}; - Event.Gesture = Event.Gesture || {}; - Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; - Event.Gesture._gestureHandlers.drag = root.drag; +Event.Gesture = Event.Gesture || {}; +Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; +Event.Gesture._gestureHandlers.drag = root.drag; - return root; +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); - }); - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +Event.proxy = (function(root) { "use strict"; - var RAD_DEG = Math.PI / 180; +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; - }; +root.gesture = function(conf) { + conf.gesture = conf.gesture || "gesture"; + 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); + pt.move.y = (touch.pageY + bbox.scrollTop - bbox.y1); + } 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; +Event.Gesture = Event.Gesture || {}; +Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; +Event.Gesture._gestureHandlers.gesture = root.gesture; - return root; +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); - }); - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +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; - }; +root.pointerdown = +root.pointermove = +root.pointerup = function(conf) { + conf.gesture = conf.gesture || "pointer"; + 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; +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; +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); - }); - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +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; - }; +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; +Event.Gesture = Event.Gesture || {}; +Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; +Event.Gesture._gestureHandlers.shake = root.shake; - return root; +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); - }); - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +Event.proxy = (function(root) { "use strict"; - var RAD_DEG = Math.PI / 180; +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; +root.swipe = function(conf) { + conf.snap = conf.snap || 90; // angle snap. + conf.threshold = conf.threshold || 1; // velocity threshold. + conf.gesture = conf.gesture || "swipe"; + // 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; + } + } + /// + var fingers = conf.gestureFingers; + if (conf.minFingers <= fingers && conf.maxFingers >= fingers) { + 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 = fingers; + 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; +}; - endx += touch.move.x; - endy += touch.move.y; - start.x += touch.start.x; - start.y += touch.start.y; - length++; +Event.Gesture = Event.Gesture || {}; +Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; +Event.Gesture._gestureHandlers.swipe = root.swipe; - - 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; +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 - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +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; - }; +root.longpress = function(conf) { + conf.gesture = "longpress"; + return root.tap(conf); +}; - Event.Gesture = Event.Gesture || {}; - Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; - Event.Gesture._gestureHandlers.tap = root.tap; - Event.Gesture._gestureHandlers.longpress = root.longpress; +root.tap = function(conf) { + conf.delay = conf.delay || 500; + conf.timeout = conf.timeout || 250; + conf.driftDeviance = conf.driftDeviance || 10; + conf.gesture = conf.gesture || "tap"; + // 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) { + var point = conf.tracker[key]; + if (point.end === true) return; + if (conf.cancel) return; + fingers ++; + } + // Send callback. + if (conf.minFingers <= fingers && conf.maxFingers >= fingers) { + self.state = "start"; + self.fingers = fingers; + self.x = point.start.x; + self.y = point.start.y; + 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); + var y = (touch.pageY + bbox.scrollTop - bbox.y1); + } else { + var x = (touch.pageX - bbox.x1); + var y = (touch.pageY - bbox.y1); + } + /// + var dx = x - pt.start.x; + var dy = y - pt.start.y; + var distance = Math.sqrt(dx * dx + dy * dy); + if (!(x > 0 && x < bbox.width && // Within target coordinates.. + y > 0 && y < bbox.height && + distance <= conf.driftDeviance)) { // Within drift deviance. + // 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. + var fingers = conf.gestureFingers; + if (conf.minFingers <= fingers && conf.maxFingers >= fingers) { + 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; +}; - return root; +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); - }); - */ + "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 = {}; +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; -Event.proxy = (function(root) { - "use strict"; +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; - }; +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, + preventElasticBounce: function() { + var target = this.target; + var scrollTop = target.scrollTop; + var top = scrollTop + target.offsetHeight; + var height = target.scrollHeight; + if (top === height && this.wheelDelta <= 0) Event.cancel(event); + else if (scrollTop === 0 && this.wheelDelta >= 0) Event.cancel(event); + Event.stop(event); + }, + add: function() { + conf.target[add](type, onMouseWheel, false); + }, + 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.getEventSupport("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; +Event.Gesture = Event.Gesture || {}; +Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; +Event.Gesture._gestureHandlers.wheel = root.wheel; - return root; +return root; + +})(Event.proxy); +/* + "Orientation Change" + ---------------------------------------------------- + https://developer.apple.com/library/safari/documentation/SafariDOMAdditions/Reference/DeviceOrientationEventClassRef/DeviceOrientationEvent/DeviceOrientationEvent.html#//apple_ref/doc/uid/TP40010526 + ---------------------------------------------------- + Event.add(window, "deviceorientation", function(event, self) {}); +*/ + +if (typeof(Event) === "undefined") var Event = {}; +if (typeof(Event.proxy) === "undefined") Event.proxy = {}; + +Event.proxy = (function(root) { "use strict"; + +root.orientation = function(conf) { + // Externally accessible data. + var self = { + gesture: "orientationchange", + previous: null, /* Report the previous orientation */ + current: window.orientation, + target: conf.target, + listener: conf.listener, + remove: function() { + window.removeEventListener('orientationchange', onOrientationChange, false); + } + }; + + // Tracking the events. + var onOrientationChange = function(e) { + + self.previous = self.current; + self.current = window.orientation; + if(self.previous !== null && self.previous != self.current) { + conf.listener(e, self); + return; + } + + + }; + // Attach events. + if (window.DeviceOrientationEvent) { + window.addEventListener("orientationchange", onOrientationChange, false); + } + // Return this object. + return self; +}; + +Event.Gesture = Event.Gesture || {}; +Event.Gesture._gestureHandlers = Event.Gesture._gestureHandlers || {}; +Event.Gesture._gestureHandlers.orientation = root.orientation; + +return root; })(Event.proxy); diff --git a/src/mixins/canvas_events.mixin.js b/src/mixins/canvas_events.mixin.js index ea70a739..4a810ddc 100644 --- a/src/mixins/canvas_events.mixin.js +++ b/src/mixins/canvas_events.mixin.js @@ -31,16 +31,16 @@ * @private */ _initEvents: function () { - var _this = this; this._onMouseDown = this._onMouseDown.bind(this); this._onMouseMove = this._onMouseMove.bind(this); this._onMouseUp = this._onMouseUp.bind(this); this._onResize = this._onResize.bind(this); - - this._onGesture = function(e, s) { - _this.__onTransformGesture(e, s); - }; + this._onGesture = this._onGesture.bind(this); + this._onDrag = this._onDrag.bind(this); + this._onShake = this._onShake.bind(this); + this._onOrientationChange = this._onOrientationChange.bind(this); + this._onMouseWheel = this._onMouseWheel.bind(this); addListener(fabric.window, 'resize', this._onResize); @@ -50,14 +50,63 @@ if (typeof Event !== 'undefined' && 'add' in Event) { Event.add(this.upperCanvasEl, 'gesture', this._onGesture); + Event.add(this.upperCanvasEl, 'drag', this._onDrag); + Event.add(this.upperCanvasEl, 'orientation', this._onOrientationChange); + Event.add(this.upperCanvasEl, 'shake', this._onShake); } } else { addListener(this.upperCanvasEl, 'mousedown', this._onMouseDown); addListener(this.upperCanvasEl, 'mousemove', this._onMouseMove); + addListener(this.upperCanvasEl, 'mousewheel', this._onMouseWheel); } }, + /** + * @private + * @param {Event} [e] Event object fired on Event.js gesture + * @param {Event} [self] Inner Event object + */ + _onGesture: function(e, s) { + this.__onTransformGesture(e, s); + }, + + /** + * @private + * @param {Event} [e] Event object fired on Event.js drag + * @param {Event} [self] Inner Event object + */ + _onDrag: function(e, s) { + this.__onDrag(e, s); + }, + + /** + * @private + * @param {Event} [e] Event object fired on Event.js wheel event + * @param {Event} [self] Inner Event object + */ + _onMouseWheel: function(e, s) { + this.__onMouseWheel(e, s); + }, + + /** + * @private + * @param {Event} [e] Event object fired on Event.js orientation change + * @param {Event} [self] Inner Event object + */ + _onOrientationChange: function(e,s) { + this.__onOrientationChange(e,s); + }, + + /** + * @private + * @param {Event} [e] Event object fired on Event.js shake + * @param {Event} [self] Inner Event object + */ + _onShake: function(e,s) { + this.__onShake(e,s); + }, + /** * @private * @param {Event} e Event object fired on mousedown diff --git a/src/mixins/canvas_gestures.mixin.js b/src/mixins/canvas_gestures.mixin.js index 2949a254..f615d293 100644 --- a/src/mixins/canvas_gestures.mixin.js +++ b/src/mixins/canvas_gestures.mixin.js @@ -28,6 +28,36 @@ this.fire('touch:gesture', {target: target, e: e, self: self}); }, + /** + * Method that defines actions when an Event.js drag is detected. + * + * @param e Event object by Event.js + * @param self Event proxy object by Event.js + */ + __onDrag: function(e, self) { + this.fire('touch:drag', {e: e, self: self}); + }, + + /** + * Method that defines actions when an Event.js orientation event is detected. + * + * @param e Event object by Event.js + * @param self Event proxy object by Event.js + */ + __onOrientationChange: function(e, self) { + this.fire('touch:orientation', {e: e, self: self}); + }, + + /** + * Method that defines actions when an Event.js shake event is detected. + * + * @param e Event object by Event.js + * @param self Event proxy object by Event.js + */ + __onShake: function(e, self) { + this.fire('touch:shake', {e: e, self: self}); + }, + /** * Scales an object by a factor * @param s {Number} The scale factor to apply to the current scale level diff --git a/src/static_canvas.class.js b/src/static_canvas.class.js index 203c1d89..cf0599a1 100644 --- a/src/static_canvas.class.js +++ b/src/static_canvas.class.js @@ -1044,17 +1044,22 @@ if (!this.interactive) return this; + removeListener(fabric.window, 'resize', this._onResize); + if (fabric.isTouchSupported) { removeListener(this.upperCanvasEl, 'touchstart', this._onMouseDown); removeListener(this.upperCanvasEl, 'touchmove', this._onMouseMove); if (typeof Event !== 'undefined' && 'remove' in Event) { Event.remove(this.upperCanvasEl, 'gesture', this._onGesture); + Event.remove(this.upperCanvasEl, 'drag', this._onDrag); + Event.remove(this.upperCanvasEl, 'orientation', this._onOrientationChange); + Event.remove(this.upperCanvasEl, 'shake', this._onShake); } } else { removeListener(this.upperCanvasEl, 'mousedown', this._onMouseDown); removeListener(this.upperCanvasEl, 'mousemove', this._onMouseMove); - removeListener(fabric.window, 'resize', this._onResize); + removeListener(this.upperCanvasEl, 'mousewheel', this._onMouseWheel); } return this; },