/*: ---------------------------------------------------- event.js : 1.1.5 : 2014/02/12 : MIT License ---------------------------------------------------- https://github.com/mudcube/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/ ---------------------------------------------------- */ if (typeof(eventjs) === "undefined") var eventjs = {}; (function(root) { "use strict"; // Add custom *EventListener commands to HTMLElements (set false to prevent funkiness). root.modifyEventListener = false; // Add bulk *EventListener commands on NodeLists from querySelectorAll and others (set false to prevent funkiness). root.modifySelectors = false; root.configure = function(conf) { if (isFinite(conf.modifyEventListener)) root.modifyEventListener = conf.modifyEventListener; if (isFinite(conf.modifySelectors)) root.modifySelectors = conf.modifySelectors; /// Augment event listeners if (eventListenersAgumented === false && root.modifyEventListener) { augmentEventListeners(); } if (selectorsAugmented === false && root.modifySelectors) { augmentSelectors(); } }; // Event maintenance. root.add = function(target, type, listener, configure) { return eventManager(target, type, listener, configure, "add"); }; root.remove = function(target, type, listener, configure) { return eventManager(target, type, listener, configure, "remove"); }; root.returnFalse = function(event) { return false; }; root.stop = function(event) { if (!event) return; if (event.stopPropagation) event.stopPropagation(); event.cancelBubble = true; // <= IE8 event.cancelBubbleCount = 0; }; root.prevent = function(event) { if (!event) return; if (event.preventDefault) { event.preventDefault(); } else if (event.preventManipulation) { event.preventManipulation(); // MS } else { event.returnValue = false; // <= IE8 } }; root.cancel = function(event) { root.stop(event); root.prevent(event); }; root.blur = function() { // Blurs the focused element. Useful when using eventjs.cancel as canceling will prevent focused elements from being blurred. var node = document.activeElement; if (!node) return; var nodeName = document.activeElement.nodeName; if (nodeName === "INPUT" || nodeName === "TEXTAREA" || node.contentEditable === "true") { if (node.blur) node.blur(); } }; // Check whether event is natively supported (via @kangax) 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; }; /// Handle custom *EventListener commands. 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; delete data.target; /// if (data.type && data.listener) { type = data.type; delete data.type; listener = data.listener; delete data.listener; for (var key in data) { configure[key] = data[key]; } } else { // specialness for (var param in data) { var value = data[param]; if (typeof(value) === "function") continue; configure[param] = value; } /// var ret = {}; for (var key in data) { var param = key.split(","); var o = data[key]; var conf = {}; for (var k in configure) { // clone base configuration conf[k] = configure[k]; } /// if (typeof(o) === "function") { // without configuration var listener = o; } else if (typeof(o.listener) === "function") { // with configuration var listener = o.listener; for (var k in o) { // merge configure into base configuration if (typeof(o[k]) === "function") continue; conf[k] = o[k]; } } else { // not a listener continue; } /// for (var n = 0; n < param.length; n ++) { ret[key] = eventjs.add(target, param[n], listener, conf, trigger); } } return ret; } } /// if (!target || !type || !listener) return; // Check for element to load on interval (before onload). if (typeof(target) === "string" && type === "ready") { if (window.eventjs_stallOnReady) { /// force stall for scripts to load type = "load"; target = window; } else { // 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 (typeof(type) === "string") { type = type.toLowerCase(); if (type.indexOf(" ") !== -1) { type = type.split(" "); } else if (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); } else if (type.indexOf("on") === 0) { // to support things like "onclick" instead of "click" type = type.slice(2); } // 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(); } } }; }; /// Display error message in console. 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 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.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"; /* 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]; } /// var type = newEvent.type; if (root.Gesture && root.Gesture._gestureHandlers[type]) { // capture custom events. // target.dispatchEvent(newEvent); self.oldListener.call(target, newEvent, self, false); } }; var eventListenersAgumented = false; var augmentEventListeners = function() { /// Allows *EventListener to use custom event proxies. if (!window.HTMLElement) return; 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); }; var selectorsAugmented = false; var augmentSelectors = function() { /// Allows querySelectorAll and other NodeLists to perform *EventListener commands in bulk. 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; })(eventjs); /*: ---------------------------------------------------- eventjs.proxy : 0.4.2 : 2013/07/17 : MIT License ---------------------------------------------------- https://github.com/mudcube/eventjs.js ---------------------------------------------------- */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; /* Create a new pointer gesture instance. */ root.pointerSetup = function(conf, self) { /// Configure. conf.target = conf.target || window; 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 (eventjs.modifyEventListener && conf.fromOverwrite) { conf.oldListener = conf.listener; conf.listener = eventjs.createPointerEvent; } /// Convenience commands. var fingers = 0; var type = self.gesture.indexOf("pointer") === 0 && eventjs.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) eventjs.add(conf.target, type + "down", conf.onPointerDown); if (conf.onPointerMove) eventjs.add(conf.doc, type + "move", conf.onPointerMove); if (conf.onPointerUp) eventjs.add(conf.doc, type + "up", conf.onPointerUp); self.enabled = true; }; self.remove = function() { if (self.enabled === false) return; if (conf.onPointerDown) eventjs.remove(conf.target, type + "down", conf.onPointerDown); if (conf.onPointerMove) eventjs.remove(conf.doc, type + "move", conf.onPointerMove); if (conf.onPointerUp) eventjs.remove(conf.doc, type + "up", conf.onPointerUp); self.reset(); self.enabled = false; }; self.pause = function(opt) { if (conf.onPointerMove && (!opt || opt.move)) eventjs.remove(conf.doc, type + "move", conf.onPointerMove); if (conf.onPointerUp && (!opt || opt.up)) eventjs.remove(conf.doc, type + "up", conf.onPointerUp); fingers = conf.fingers; conf.fingers = 0; }; self.resume = function(opt) { if (conf.onPointerMove && (!opt || opt.move)) eventjs.add(conf.doc, type + "move", conf.onPointerMove); if (conf.onPointerUp && (!opt || opt.up)) eventjs.add(conf.doc, type + "up", conf.onPointerUp); conf.fingers = fingers; }; self.reset = function() { conf.tracker = {}; conf.fingers = 0; }; /// return self; }; /* Begin proxied pointer command. */ var sp = eventjs.supports; // Default pointerType /// eventjs.isMouse = !!sp.mouse; eventjs.isMSPointer = !!sp.touch; eventjs.isTouch = !!sp.msPointer; /// root.pointerStart = function(event, self, conf) { /// tracks multiple inputs var type = (event.type || "mousedown").toUpperCase(); if (type.indexOf("MOUSE") === 0) { eventjs.isMouse = true; eventjs.isTouch = false; eventjs.isMSPointer = false; } else if (type.indexOf("TOUCH") === 0) { eventjs.isMouse = false; eventjs.isTouch = true; eventjs.isMSPointer = false; } else if (type.indexOf("MSPOINTER") === 0) { eventjs.isMouse = false; eventjs.isTouch = false; eventjs.isMSPointer = true; } /// 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 - bbox.scrollLeft; pt.offsetY = bbox.y1 - bbox.scrollTop; break; } /// var x = touch.pageX - pt.offsetX; var y = touch.pageY - pt.offsetY; /// pt.rotation = 0; pt.scale = 1; pt.startTime = pt.moveTime = (new Date()).getTime(); pt.move = { x: x, y: y }; pt.start = { x: x, y: y }; /// conf.fingers ++; }; /// conf.event = event; if (self.defaultListener) { conf.listener = self.defaultListener; delete self.defaultListener; } /// var isTouchStart = !conf.fingers; var track = conf.tracker; var touches = event.changedTouches || root.getCoords(event); var length = touches.length; // Adding touch events to tracking. for (var i = 0; i < length; i ++) { var touch = touches[i]; var sid = touch.identifier || Infinity; // Touch ID. // Track the current state of the touches. if (conf.fingers) { if (conf.fingers >= conf.maxFingers) { var ids = []; for (var sid in conf.tracker) ids.push(sid); self.identifier = ids.join(","); return isTouchStart; } var fingers = 0; // Finger ID. for (var rid in track) { // Replace removed finger. if (track[rid].up) { delete track[rid]; addTouchStart(touch, sid); conf.cancel = true; break; } fingers ++; } // Add additional finger. if (track[sid]) continue; addTouchStart(touch, sid); } else { // Start tracking fingers. track = conf.tracker = {}; self.bbox = conf.bbox = root.getBoundingBox(conf.target); conf.fingers = 0; conf.cancel = false; addTouchStart(touch, sid); } } /// var ids = []; for (var sid in conf.tracker) ids.push(sid); self.identifier = ids.join(","); /// return isTouchStart; }; /* End proxied pointer command. */ root.pointerEnd = function(event, self, conf, onPointerUp) { // Record changed touches have ended (iOS changedTouches is not reliable). var touches = event.touches || []; var length = touches.length; var exists = {}; for (var i = 0; i < length; i ++) { var touch = touches[i]; 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); */ 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) { var doc = document.documentElement; event = event || window.event; return Array({ type: "mouse", x: event.clientX + doc.scrollLeft, y: event.clientY + doc.scrollTop, pageX: event.clientX + doc.scrollLeft, pageY: event.clientY + doc.scrollTop, identifier: Infinity }); }; } return root.getCoords(event); }; /* Returns single coords in an object. ------------------------------------------------------------ var mouse = root.getCoord(event); */ root.getCoord = function(event) { if ("ontouchstart" in window) { // Mobile browsers. var pX = 0; var pY = 0; root.getCoord = function(event) { var touches = event.changedTouches; if (touches && 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) { var doc = document.documentElement; event = event || window.event; return { x: event.clientX + doc.scrollLeft, y: event.clientY + doc.scrollTop }; }; } return root.getCoord(event); }; /* Get target scale and position in space. */ var getPropertyAsFloat = function(o, type) { var n = parseFloat(o.getPropertyValue(type), 10); return isFinite(n) ? n : 0; }; 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.scaleX = bcr.width / o.offsetWidth || 1; bbox.scaleY = bcr.height / o.offsetHeight || 1; bbox.scrollLeft = 0; bbox.scrollTop = 0; /// var style = window.getComputedStyle(o); var borderBox = style.getPropertyValue("box-sizing") === "border-box"; /// if (borderBox === false) { var left = getPropertyAsFloat(style, "border-left-width"); var right = getPropertyAsFloat(style, "border-right-width"); var bottom = getPropertyAsFloat(style, "border-bottom-width"); var top = getPropertyAsFloat(style, "border-top-width"); bbox.border = [ left, right, top, bottom ]; bbox.x1 += left; bbox.y1 += top; bbox.width -= right + left; bbox.height -= bottom + top; } /* var left = getPropertyAsFloat(style, "padding-left"); var right = getPropertyAsFloat(style, "padding-right"); var bottom = getPropertyAsFloat(style, "padding-bottom"); var top = getPropertyAsFloat(style, "padding-top"); bbox.padding = [ left, right, top, bottom ];*/ /// bbox.x2 = bbox.x1 + bbox.width; bbox.y2 = bbox.y1 + bbox.height; /// Get the scroll of container element. var position = style.getPropertyValue("position"); var tmp = position === "fixed" ? o : 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") { } else if (position === "fixed") { // bbox.scrollTop += document.body.scrollTop; // bbox.scrollLeft += document.body.scrollLeft; bbox.scrollTop -= tmp.parentNode.scrollTop; bbox.scrollLeft -= tmp.parentNode.scrollLeft; break; } else { bbox.scrollLeft += tmp.scrollLeft; bbox.scrollTop += tmp.scrollTop; } /// tmp = tmp.parentNode; }; /// bbox.scrollBodyLeft = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft; bbox.scrollBodyTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; /// bbox.scrollLeft -= bbox.scrollBodyLeft; bbox.scrollTop -= bbox.scrollBodyTop; /// return bbox; }; /* Keep track of metaKey, the proper ctrlKey for users platform. ---------------------------------------------------- http://www.quirksmode.org/js/keys.html ----------------------------------- http://unixpapa.com/js/key.html */ (function() { var agent = navigator.userAgent.toLowerCase(); var mac = agent.indexOf("macintosh") !== -1; var metaKeys; if (mac && agent.indexOf("khtml") !== -1) { // chrome, safari. metaKeys = { 91: true, 93: true }; } else if (mac && agent.indexOf("firefox") !== -1) { // mac firefox. metaKeys = { 224: true }; } else { // windows, linux, or mac opera. metaKeys = { 17: true }; } (root.metaTrackerReset = function() { eventjs.fnKey = root.fnKey = false; eventjs.metaKey = root.metaKey = false; eventjs.escKey = root.escKey = false; eventjs.ctrlKey = root.ctrlKey = false; eventjs.shiftKey = root.shiftKey = false; eventjs.altKey = root.altKey = false; })(); root.metaTracker = function(event) { var isKeyDown = event.type === "keydown"; if (event.keyCode === 27) eventjs.escKey = root.escKey = isKeyDown; if (metaKeys[event.keyCode]) eventjs.metaKey = root.metaKey = isKeyDown; eventjs.ctrlKey = root.ctrlKey = event.ctrlKey; eventjs.shiftKey = root.shiftKey = event.shiftKey; eventjs.altKey = root.altKey = event.altKey; }; })(); return root; })(eventjs.proxy); /*: ---------------------------------------------------- "MutationObserver" event proxy. ---------------------------------------------------- author: Selvakumar Arumugam - MIT LICENSE src: http://stackoverflow.com/questions/10868104/can-you-have-a-javascript-hook-trigger-after-a-dom-elements-style-object-change ---------------------------------------------------- */ if (typeof(eventjs) === "undefined") var eventjs = {}; eventjs.MutationObserver = (function() { var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; var DOMAttrModifiedSupported = !MutationObserver && (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) { eventjs.add(container, "DOMAttrModified", function(e) { callback.call(container, e.attrName); }); } else if ("onpropertychange" in document.body) { eventjs.add(container, "propertychange", function(e) { callback.call(container, window.event.propertyName); }); } } })(); /*: "Click" event proxy. ---------------------------------------------------- eventjs.add(window, "click", function(event, self) {}); */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; root.click = function(conf) { conf.gesture = conf.gesture || "click"; conf.maxFingers = conf.maxFingers || conf.fingers || 1; /// Tracking the events. conf.onPointerDown = function (event) { if (root.pointerStart(event, self, conf)) { eventjs.add(conf.target, "mouseup", conf.onPointerUp); } }; conf.onPointerUp = function(event) { if (root.pointerEnd(event, self, conf)) { eventjs.remove(conf.target, "mouseup", conf.onPointerUp); var pointers = event.changedTouches || root.getCoords(event); var pointer = pointers[0]; var bbox = conf.bbox; var newbbox = root.getBoundingBox(conf.target); var y = pointer.pageY - newbbox.scrollBodyTop; var x = pointer.pageX - newbbox.scrollBodyLeft; //// if (x > bbox.x1 && y > bbox.y1 && x < bbox.x2 && y < bbox.y2 && bbox.scrollTop === newbbox.scrollTop) { // has not been scrolled /// 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. eventjs.add(conf.target, "mousedown", conf.onPointerDown); // Return this object. return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.click = root.click; return root; })(eventjs.proxy); /*: "Double-Click" aka "Double-Tap" event proxy. ---------------------------------------------------- eventjs.add(window, "dblclick", function(event, self) {}); ---------------------------------------------------- Touch an target twice for <= 700ms, with less than 25 pixel drift. */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; 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)) { eventjs.add(conf.target, "mousemove", conf.onPointerMove).listener(event); eventjs.add(conf.target, "mouseup", conf.onPointerUp); } }; conf.onPointerMove = function (event) { if (time0 && !time1) { var pointers = event.changedTouches || root.getCoords(event); pointer1 = pointers[0]; } var bbox = conf.bbox; 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. eventjs.remove(conf.target, "mousemove", conf.onPointerMove); clearTimeout(timeout); time0 = time1 = 0; } }; conf.onPointerUp = function(event) { if (root.pointerEnd(event, self, conf)) { eventjs.remove(conf.target, "mousemove", conf.onPointerMove); eventjs.remove(conf.target, "mouseup", conf.onPointerUp); } if (time0 && time1) { if (time1 <= delay) { // && !(event.cancelBubble && ++event.cancelBubbleCount > 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. eventjs.add(conf.target, "mousedown", conf.onPointerDown); // Return this object. return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.dbltap = root.dbltap; eventjs.Gesture._gestureHandlers.dblclick = root.dblclick; return root; })(eventjs.proxy); /*: "Drag" event proxy (1+ fingers). ---------------------------------------------------- CONFIGURE: maxFingers, position. ---------------------------------------------------- eventjs.add(window, "drag", function(event, self) { console.log(self.gesture, self.state, self.start, self.x, self.y, self.bbox); }); */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.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"; eventjs.prevent(event); } }); }; root.drag = function(conf) { conf.gesture = "drag"; conf.onPointerDown = function (event) { if (root.pointerStart(event, self, conf)) { if (!conf.monitor) { eventjs.add(conf.doc, "mousemove", conf.onPointerMove); eventjs.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 { 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) { eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); eventjs.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 { // eventjs.add(conf.target, "mousedown", conf.onPointerDown); if (conf.monitor) { eventjs.add(conf.doc, "mousemove", conf.onPointerMove); eventjs.add(conf.doc, "mouseup", conf.onPointerUp); } } // Return this object. return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.drag = root.drag; return root; })(eventjs.proxy); /*: "Gesture" event proxy (2+ fingers). ---------------------------------------------------- CONFIGURE: minFingers, maxFingers. ---------------------------------------------------- eventjs.add(window, "gesture", function(event, self) { console.log( self.x, // centroid self.y, self.rotation, self.scale, self.fingers, self.state ); }); */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; var RAD_DEG = Math.PI / 180; var getCentroid = function(self, points) { 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 ++; } self.x = centroidx /= length; self.y = centroidy /= length; 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)) { eventjs.add(conf.doc, "mousemove", conf.onPointerMove); eventjs.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); getCentroid(self, conf.tracker); 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. 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. getCentroid(self, points); /// for (var sid in points) { var touch = points[sid]; if (touch.up) continue; var start = touch.start; if (!start.distance) { var dx = start.x - self.x; var dy = start.y - self.y; start.distance = Math.sqrt(dx * dx + dy * dy); start.angle = Math.atan2(dx, dy) / RAD_DEG; } // Calculate scale. var dx = touch.move.x - self.x; var dy = touch.move.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If touch start.distance from centroid is 0, scale should not be updated. // This prevents dividing by 0 in cases where start.distance is oddly 0. if (start.distance !== 0) { 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)) { eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); eventjs.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. eventjs.add(conf.target, "mousedown", conf.onPointerDown); // Return this object. return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.gesture = root.gesture; return root; })(eventjs.proxy); /*: "Pointer" event proxy (1+ fingers). ---------------------------------------------------- CONFIGURE: minFingers, maxFingers. ---------------------------------------------------- eventjs.add(window, "gesture", function(event, self) { console.log(self.rotation, self.scale, self.fingers, self.state); }); */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; 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. eventjs.add(conf.target, "mousedown", conf.onPointerDown); eventjs.add(conf.target, "mousemove", conf.onPointerMove); eventjs.add(conf.doc, "mouseup", conf.onPointerUp); // Return this object. conf.target.isPointerEmitter = true; return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.pointerdown = root.pointerdown; eventjs.Gesture._gestureHandlers.pointermove = root.pointermove; eventjs.Gesture._gestureHandlers.pointerup = root.pointerup; return root; })(eventjs.proxy); /*: "Device Motion" and "Shake" event proxy. ---------------------------------------------------- http://developer.android.com/reference/android/hardware/Sensoreventjs.html#values ---------------------------------------------------- eventjs.add(window, "shake", function(event, self) {}); eventjs.add(window, "devicemotion", function(event, self) { console.log(self.acceleration, self.accelerationIncludingGravity); }); */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.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; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.shake = root.shake; return root; })(eventjs.proxy); /*: "Swipe" event proxy (1+ fingers). ---------------------------------------------------- CONFIGURE: snap, threshold, maxFingers. ---------------------------------------------------- eventjs.add(window, "swipe", function(event, self) { console.log(self.velocity, self.angle); }); */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; var RAD_DEG = Math.PI / 180; root.swipe = function(conf) { conf.snap = conf.snap || 90; // angle snap. conf.threshold = conf.threshold || 1; // velocity threshold. conf.gesture = conf.gesture || "swipe"; // Tracking the events. conf.onPointerDown = function (event) { if (root.pointerStart(event, self, conf)) { eventjs.add(conf.doc, "mousemove", conf.onPointerMove).listener(event); eventjs.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)) { eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); eventjs.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. eventjs.add(conf.target, "mousedown", conf.onPointerDown); // Return this object. return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.swipe = root.swipe; return root; })(eventjs.proxy); /*: "Tap" and "Longpress" event proxy. ---------------------------------------------------- CONFIGURE: delay (longpress), timeout (tap). ---------------------------------------------------- eventjs.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(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; root.longpress = function(conf) { conf.gesture = "longpress"; return root.tap(conf); }; 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. eventjs.add(conf.doc, "mousemove", conf.onPointerMove).listener(event); eventjs.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.cancelBubbleCount > 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; var x = (touch.pageX - bbox.x1 - parseInt(window.scrollX)); var y = (touch.pageY - bbox.y1 - parseInt(window.scrollY)); /// 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. eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); conf.cancel = true; return; } } }; conf.onPointerUp = function(event) { if (root.pointerEnd(event, self, conf)) { clearTimeout(timeout); eventjs.remove(conf.doc, "mousemove", conf.onPointerMove); eventjs.remove(conf.doc, "mouseup", conf.onPointerUp); if (event.cancelBubble && ++event.cancelBubbleCount > 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. eventjs.add(conf.target, "mousedown", conf.onPointerDown); // Return this object. return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.tap = root.tap; eventjs.Gesture._gestureHandlers.longpress = root.longpress; return root; })(eventjs.proxy); /*: "Mouse Wheel" event proxy. ---------------------------------------------------- eventjs.add(window, "wheel", function(event, self) { console.log(self.state, self.wheelDelta); }); */ if (typeof(eventjs) === "undefined") var eventjs = {}; if (typeof(eventjs.proxy) === "undefined") eventjs.proxy = {}; eventjs.proxy = (function(root) { "use strict"; root.wheelPreventElasticBounce = function(el) { if (!el) return; if (typeof(el) === "string") el = document.querySelector(el); eventjs.add(el, "wheel", function(event, self) { self.preventElasticBounce(); eventjs.stop(event); }); }; 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(event) { var target = this.target; var scrollTop = target.scrollTop; var top = scrollTop + target.offsetHeight; var height = target.scrollHeight; if (top === height && this.wheelDelta <= 0) eventjs.cancel(event); else if (scrollTop === 0 && this.wheelDelta >= 0) eventjs.cancel(event); eventjs.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 = eventjs.getEventSupport("mousewheel") ? "mousewheel" : "DOMMouseScroll"; conf.target[add](type, onMouseWheel, false); // Return this object. return self; }; eventjs.Gesture = eventjs.Gesture || {}; eventjs.Gesture._gestureHandlers = eventjs.Gesture._gestureHandlers || {}; eventjs.Gesture._gestureHandlers.wheel = root.wheel; return root; })(eventjs.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);