// The MIT License (MIT) // Copyright (c) 2016 Quentin Engles // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. (function() { // more_events function MoreEvents(context){ this.listeners = {}; this.__context = context || this; } MoreEvents.prototype = { constructor: MoreEvents, on: function(event, listener){ this.listeners[event] = this.listeners[event] || []; this.listeners[event].push(listener); return this; }, one: function(event, listener){ function onceListener(){ listener.apply(this, arguments); this.off(event, onceListener); return this; } return this.on(event, onceListener); }, emit: function(event){ if(typeof this.listeners[event] === 'undefined' || !this.listeners[event].length) return this; var args = Array.prototype.slice.call(arguments, 1), canRun = this.listeners[event].length; do{ this.listeners[event][--canRun].apply(this.__context, args); }while(canRun); return this; }, off: function(event, listener){ if(this.listeners[event] === undefined || !this.listeners[event].length) return this; this.listeners[event] = this.listeners[event].filter(function(item){ return item !== listener; }); return this; }, dispose: function(){ for(var n in this){ this[n] = null; } } }; // pointer_point var Emitter = MoreEvents; if(!Date.now){ Date.now = function(){ return new Date().getTime() } } function LocalDimensions(point, rect){ for(var n in rect) setProp(this, n, rect[n]); setProp(this, 'x', point.x - rect.left+1); setProp(this, 'y', point.y - rect.top+1); setProp(this, 'north', (((rect.bottom - rect.top) / 2)-this.y)); setProp(this, 'south', ((-(rect.bottom - rect.top) / 2)+this.y)); setProp(this, 'east', (((rect.right - rect.left) / 2)-this.x)); setProp(this, 'west', ((-(rect.right - rect.left) / 2)+this.x)); function setProp(self, name, value){ Object.defineProperty(self, name, { value: value, configurable: true, writable: false }); } } function Point(elements){ var self = this, el = []; if(typeof elements.length === 'undefined'){ elements = [elements]; } for(var i=0; i (startY - buf) && self.y < (startY + buf) && self.x > (startX - buf) && self.x < (startX + buf))){ //If there is scrolling there was a touch flick. if(!scrolling){ //No touch flick so self.previous = null; self.origin = null; e.preventDefault(); return false; } } } scrolling = false; self.previous = null; self.origin = null; } function toPoint(event){ var dot, eventDoc, doc, body, pageX, pageY; var target, newTarget = null, leaving = null; event = event || window.event; // IE-ism target = event.target || event.srcElement; //Supporting touch //http://www.creativebloq.com/javascript/make-your-site-work-touch-devices-51411644 if(event.targetTouches) { event.pageX = event.targetTouches[0].clientX; event.pageY = event.targetTouches[0].clientY; event.clientX = event.targetTouches[0].clientX; event.clientY = event.targetTouches[0].clientY; }else // If pageX/Y aren't available and clientX/Y are, // calculate pageX/Y - logic taken from jQuery. // (This is to support old IE) if (event.pageX === null && event.clientX !== null) { eventDoc = (event.target && event.target.ownerDocument) || document; doc = eventDoc.documentElement; body = eventDoc.body; event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0 ); } if(self.x && self.y){ if(event.pageX < self.x) direction.h = 'left'; else if(event.pageX > self.x) direction.h = 'right'; if(event.pageY < self.y) direction.v = 'up'; else if(event.pageY > self.y) direction.v = 'down'; lastmousex = self.x; lastmousey = self.y; } pos = {}; //Prefer the viewport with clientX, and clientY. //pageX, and pageY change too often. pos.x = event.clientX;//event.pageX; pos.y = event.clientY;//event.pageY; if(self.current === null || self.outside(self.current)){ for(var i=0; i downTime + (special.hold[i].data || 2000)){ special.hold[i].callback.call(this, el, rect); } } } downTime = 0; }); function removeSpecial(event, cb){ for(var i=0; i rect.top && this.y < rect.bottom && this.x > rect.left && this.x < rect.right); }, outside: function(el){ if(!el) throw new TypeError('Cannot be outside '+el); return !this.inside(el); } }; function elementFromPoint(x, y){ if(document.getElementFromPoint) return document.getElementFromPoint(x, y); else return document.elementFromPoint(x, y); return null; } function safeObject(src){ var obj = {}; for(var n in src) obj[n] = src[n]; return obj; } function getRect(el){ if(el === window){ return { top: 0, left: 0, right: window.innerWidth, bottom: window.innerHeight, width: window.innerWidth, height: window.innerHeight }; }else{ return el.getBoundingClientRect(); } } var pointer = function(element){ return new Point(element); }; var createPointCB = function createPointCB(object){ // A persistent object (as opposed to returned object) is used to save memory // This is good to prevent layout thrashing, or for games, and such // NOTE // This uses IE fixes which should be OK to remove some day. :) // Some speed will be gained by removal of these. // pointCB should be saved in a variable on return // This allows the usage of element.removeEventListener return function pointCB(event){ event = event || window.event; // IE-ism object.target = event.target || event.srcElement || event.originalTarget; object.element = this; object.type = event.type; // Support touch // http://www.creativebloq.com/javascript/make-your-site-work-touch-devices-51411644 if(event.targetTouches){ object.x = event.targetTouches[0].clientX; object.y = event.targetTouches[0].clientY; object.pageX = event.pageX; object.pageY = event.pageY; }else{ // If pageX/Y aren't available and clientX/Y are, // calculate pageX/Y - logic taken from jQuery. // (This is to support old IE) // NOTE Hopefully this can be removed soon. if (event.pageX === null && event.clientX !== null) { var eventDoc = (event.target && event.target.ownerDocument) || document; var doc = eventDoc.documentElement; var body = eventDoc.body; object.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); object.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0 ); }else{ object.pageX = event.pageX; object.pageY = event.pageY; } // pageX, and pageY change with page scroll // so we're not going to use those for x, and y. // NOTE Most browsers also alias clientX/Y with x/y // so that's something to consider down the road. object.x = event.clientX; object.y = event.clientY; } }; //NOTE Remember accessibility, Aria roles, and labels. }; // Autscroller function AutoScroller(elements, options){ var self = this, pixels = 2; options = options || {}; this.margin = options.margin || -1; this.scrolling = false; this.scrollWhenOutside = options.scrollWhenOutside || false; var point = {}, pointCB = createPointCB(point), down = false; window.addEventListener('mousemove', pointCB, false); window.addEventListener('touchmove', pointCB, false); if(!isNaN(options.pixels)){ pixels = options.pixels; } if(typeof options.autoScroll === 'boolean'){ this.autoScroll = options.autoScroll ? function(){return true;} : function(){return false;}; }else if(typeof options.autoScroll === 'undefined'){ this.autoScroll = function(){return false;}; }else if(typeof options.autoScroll === 'function'){ this.autoScroll = options.autoScroll; } this.destroy = function() { window.removeEventListener('mousemove', pointCB, false); window.removeEventListener('touchmove', pointCB, false); window.removeEventListener('mousedown', onDown, false); window.removeEventListener('touchstart', onDown, false); window.removeEventListener('mouseup', onUp, false); window.removeEventListener('touchend', onUp, false); }; var hasWindow = null, temp = []; for(var i=0; i rect.bottom - self.margin){ autoScrollV(el, 1, rect); } if(point.x < rect.left + self.margin){ autoScrollH(el, -1, rect); }else if(point.x > rect.right - self.margin){ autoScrollH(el, 1, rect); } } function autoScrollV(el, amount, rect){ if(!self.autoScroll()) return; if(!self.scrollWhenOutside && !inside(point, el, rect)) return; if(el === window){ window.scrollTo(el.pageXOffset, el.pageYOffset + amount); }else{ el.scrollTop = el.scrollTop + amount; } setTimeout(function(){ if(point.y < rect.top + self.margin){ autoScrollV(el, amount, rect); }else if(point.y > rect.bottom - self.margin){ autoScrollV(el, amount, rect); } }, self.interval); } function autoScrollH(el, amount, rect){ if(!self.autoScroll()) return; if(!self.scrollWhenOutside && !inside(point, el, rect)) return; if(el === window){ window.scrollTo(el.pageXOffset + amount, el.pageYOffset); }else{ el.scrollLeft = el.scrollLeft + amount; } setTimeout(function(){ if(point.x < rect.left + self.margin){ autoScrollH(el, amount, rect); }else if(point.x > rect.right - self.margin){ autoScrollH(el, amount, rect); } }, self.interval); } } function getRect(el){ if(el === window){ return { top: 0, left: 0, right: window.innerWidth, bottom: window.innerHeight, width: window.innerWidth, height: window.innerHeight }; }else{ try{ return el.getBoundingClientRect(); }catch(e){ throw new TypeError("Can't call getBoundingClientRect on "+el); } } } function inside(point, el, rect){ rect = rect || getRect(el); return (point.y > rect.top && point.y < rect.bottom && point.x > rect.left && point.x < rect.right); } function AutoScrollerFactory(element, options){ return new AutoScroller(element, options); } window.autoScroll = AutoScrollerFactory; }());