123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- // jquery.event.move
- //
- // 1.3.1
- //
- // Stephen Band
- //
- // Triggers 'movestart', 'move' and 'moveend' events after
- // mousemoves following a mousedown cross a distance threshold,
- // similar to the native 'dragstart', 'drag' and 'dragend' events.
- // Move events are throttled to animation frames. Move event objects
- // have the properties:
- //
- // pageX:
- // pageY: Page coordinates of pointer.
- // startX:
- // startY: Page coordinates of pointer at movestart.
- // distX:
- // distY: Distance the pointer has moved since movestart.
- // deltaX:
- // deltaY: Distance the finger has moved since last event.
- // velocityX:
- // velocityY: Average velocity over last few events.
- (function (module) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], module);
- } else {
- // Browser globals
- module(jQuery);
- }
- })(function(jQuery, undefined){
- var // Number of pixels a pressed pointer travels before movestart
- // event is fired.
- threshold = 6,
-
- add = jQuery.event.add,
-
- remove = jQuery.event.remove,
- // Just sugar, so we can have arguments in the same order as
- // add and remove.
- trigger = function(node, type, data) {
- jQuery.event.trigger(type, data, node);
- },
- // Shim for requestAnimationFrame, falling back to timer. See:
- // see http://paulirish.com/2011/requestanimationframe-for-smart-animating/
- requestFrame = (function(){
- return (
- window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- window.oRequestAnimationFrame ||
- window.msRequestAnimationFrame ||
- function(fn, element){
- return window.setTimeout(function(){
- fn();
- }, 25);
- }
- );
- })(),
-
- ignoreTags = {
- textarea: true,
- input: true,
- select: true,
- button: true
- },
-
- mouseevents = {
- move: 'mousemove',
- cancel: 'mouseup dragstart',
- end: 'mouseup'
- },
-
- touchevents = {
- move: 'touchmove',
- cancel: 'touchend',
- end: 'touchend'
- };
- // Constructors
-
- function Timer(fn){
- var callback = fn,
- active = false,
- running = false;
-
- function trigger(time) {
- if (active){
- callback();
- requestFrame(trigger);
- running = true;
- active = false;
- }
- else {
- running = false;
- }
- }
-
- this.kick = function(fn) {
- active = true;
- if (!running) { trigger(); }
- };
-
- this.end = function(fn) {
- var cb = callback;
-
- if (!fn) { return; }
-
- // If the timer is not running, simply call the end callback.
- if (!running) {
- fn();
- }
- // If the timer is running, and has been kicked lately, then
- // queue up the current callback and the end callback, otherwise
- // just the end callback.
- else {
- callback = active ?
- function(){ cb(); fn(); } :
- fn ;
-
- active = true;
- }
- };
- }
- // Functions
-
- function returnTrue() {
- return true;
- }
-
- function returnFalse() {
- return false;
- }
-
- function preventDefault(e) {
- e.preventDefault();
- }
-
- function preventIgnoreTags(e) {
- // Don't prevent interaction with form elements.
- if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; }
-
- e.preventDefault();
- }
- function isLeftButton(e) {
- // Ignore mousedowns on any button other than the left (or primary)
- // mouse button, or when a modifier key is pressed.
- return (e.which === 1 && !e.ctrlKey && !e.altKey);
- }
- function identifiedTouch(touchList, id) {
- var i, l;
- if (touchList.identifiedTouch) {
- return touchList.identifiedTouch(id);
- }
-
- // touchList.identifiedTouch() does not exist in
- // webkit yet… we must do the search ourselves...
-
- i = -1;
- l = touchList.length;
-
- while (++i < l) {
- if (touchList[i].identifier === id) {
- return touchList[i];
- }
- }
- }
- function changedTouch(e, event) {
- var touch = identifiedTouch(e.changedTouches, event.identifier);
- // This isn't the touch you're looking for.
- if (!touch) { return; }
- // Chrome Android (at least) includes touches that have not
- // changed in e.changedTouches. That's a bit annoying. Check
- // that this touch has changed.
- if (touch.pageX === event.pageX && touch.pageY === event.pageY) { return; }
- return touch;
- }
- // Handlers that decide when the first movestart is triggered
-
- function mousedown(e){
- var data;
- if (!isLeftButton(e)) { return; }
- data = {
- target: e.target,
- startX: e.pageX,
- startY: e.pageY,
- timeStamp: e.timeStamp
- };
- add(document, mouseevents.move, mousemove, data);
- add(document, mouseevents.cancel, mouseend, data);
- }
- function mousemove(e){
- var data = e.data;
- checkThreshold(e, data, e, removeMouse);
- }
- function mouseend(e) {
- removeMouse();
- }
- function removeMouse() {
- remove(document, mouseevents.move, mousemove);
- remove(document, mouseevents.cancel, removeMouse);
- }
- function touchstart(e) {
- var touch, template;
- // Don't get in the way of interaction with form elements.
- if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; }
- touch = e.changedTouches[0];
-
- // iOS live updates the touch objects whereas Android gives us copies.
- // That means we can't trust the touchstart object to stay the same,
- // so we must copy the data. This object acts as a template for
- // movestart, move and moveend event objects.
- template = {
- target: touch.target,
- startX: touch.pageX,
- startY: touch.pageY,
- timeStamp: e.timeStamp,
- identifier: touch.identifier
- };
- // Use the touch identifier as a namespace, so that we can later
- // remove handlers pertaining only to this touch.
- add(document, touchevents.move + '.' + touch.identifier, touchmove, template);
- add(document, touchevents.cancel + '.' + touch.identifier, touchend, template);
- }
- function touchmove(e){
- var data = e.data,
- touch = changedTouch(e, data);
- if (!touch) { return; }
- checkThreshold(e, data, touch, removeTouch);
- }
- function touchend(e) {
- var template = e.data,
- touch = identifiedTouch(e.changedTouches, template.identifier);
- if (!touch) { return; }
- removeTouch(template.identifier);
- }
- function removeTouch(identifier) {
- remove(document, '.' + identifier, touchmove);
- remove(document, '.' + identifier, touchend);
- }
- // Logic for deciding when to trigger a movestart.
- function checkThreshold(e, template, touch, fn) {
- var distX = touch.pageX - template.startX,
- distY = touch.pageY - template.startY;
- // Do nothing if the threshold has not been crossed.
- if ((distX * distX) + (distY * distY) < (threshold * threshold)) { return; }
- triggerStart(e, template, touch, distX, distY, fn);
- }
- function handled() {
- // this._handled should return false once, and after return true.
- this._handled = returnTrue;
- return false;
- }
- function flagAsHandled(e) {
- e._handled();
- }
- function triggerStart(e, template, touch, distX, distY, fn) {
- var node = template.target,
- touches, time;
- touches = e.targetTouches;
- time = e.timeStamp - template.timeStamp;
- // Create a movestart object with some special properties that
- // are passed only to the movestart handlers.
- template.type = 'movestart';
- template.distX = distX;
- template.distY = distY;
- template.deltaX = distX;
- template.deltaY = distY;
- template.pageX = touch.pageX;
- template.pageY = touch.pageY;
- template.velocityX = distX / time;
- template.velocityY = distY / time;
- template.targetTouches = touches;
- template.finger = touches ?
- touches.length :
- 1 ;
- // The _handled method is fired to tell the default movestart
- // handler that one of the move events is bound.
- template._handled = handled;
-
- // Pass the touchmove event so it can be prevented if or when
- // movestart is handled.
- template._preventTouchmoveDefault = function() {
- e.preventDefault();
- };
- // Trigger the movestart event.
- trigger(template.target, template);
- // Unbind handlers that tracked the touch or mouse up till now.
- fn(template.identifier);
- }
- // Handlers that control what happens following a movestart
- function activeMousemove(e) {
- var event = e.data.event,
- timer = e.data.timer;
- updateEvent(event, e, e.timeStamp, timer);
- }
- function activeMouseend(e) {
- var event = e.data.event,
- timer = e.data.timer;
-
- removeActiveMouse();
- endEvent(event, timer, function() {
- // Unbind the click suppressor, waiting until after mouseup
- // has been handled.
- setTimeout(function(){
- remove(event.target, 'click', returnFalse);
- }, 0);
- });
- }
- function removeActiveMouse(event) {
- remove(document, mouseevents.move, activeMousemove);
- remove(document, mouseevents.end, activeMouseend);
- }
- function activeTouchmove(e) {
- var event = e.data.event,
- timer = e.data.timer,
- touch = changedTouch(e, event);
- if (!touch) { return; }
- // Stop the interface from gesturing
- e.preventDefault();
- event.targetTouches = e.targetTouches;
- updateEvent(event, touch, e.timeStamp, timer);
- }
- function activeTouchend(e) {
- var event = e.data.event,
- timer = e.data.timer,
- touch = identifiedTouch(e.changedTouches, event.identifier);
- // This isn't the touch you're looking for.
- if (!touch) { return; }
- removeActiveTouch(event);
- endEvent(event, timer);
- }
- function removeActiveTouch(event) {
- remove(document, '.' + event.identifier, activeTouchmove);
- remove(document, '.' + event.identifier, activeTouchend);
- }
- // Logic for triggering move and moveend events
- function updateEvent(event, touch, timeStamp, timer) {
- var time = timeStamp - event.timeStamp;
- event.type = 'move';
- event.distX = touch.pageX - event.startX;
- event.distY = touch.pageY - event.startY;
- event.deltaX = touch.pageX - event.pageX;
- event.deltaY = touch.pageY - event.pageY;
-
- // Average the velocity of the last few events using a decay
- // curve to even out spurious jumps in values.
- event.velocityX = 0.3 * event.velocityX + 0.7 * event.deltaX / time;
- event.velocityY = 0.3 * event.velocityY + 0.7 * event.deltaY / time;
- event.pageX = touch.pageX;
- event.pageY = touch.pageY;
- timer.kick();
- }
- function endEvent(event, timer, fn) {
- timer.end(function(){
- event.type = 'moveend';
- trigger(event.target, event);
-
- return fn && fn();
- });
- }
- // jQuery special event definition
- function setup(data, namespaces, eventHandle) {
- // Stop the node from being dragged
- //add(this, 'dragstart.move drag.move', preventDefault);
-
- // Prevent text selection and touch interface scrolling
- //add(this, 'mousedown.move', preventIgnoreTags);
-
- // Tell movestart default handler that we've handled this
- add(this, 'movestart.move', flagAsHandled);
- // Don't bind to the DOM. For speed.
- return true;
- }
-
- function teardown(namespaces) {
- remove(this, 'dragstart drag', preventDefault);
- remove(this, 'mousedown touchstart', preventIgnoreTags);
- remove(this, 'movestart', flagAsHandled);
-
- // Don't bind to the DOM. For speed.
- return true;
- }
-
- function addMethod(handleObj) {
- // We're not interested in preventing defaults for handlers that
- // come from internal move or moveend bindings
- if (handleObj.namespace === "move" || handleObj.namespace === "moveend") {
- return;
- }
-
- // Stop the node from being dragged
- add(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid, preventDefault, undefined, handleObj.selector);
-
- // Prevent text selection and touch interface scrolling
- add(this, 'mousedown.' + handleObj.guid, preventIgnoreTags, undefined, handleObj.selector);
- }
-
- function removeMethod(handleObj) {
- if (handleObj.namespace === "move" || handleObj.namespace === "moveend") {
- return;
- }
-
- remove(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid);
- remove(this, 'mousedown.' + handleObj.guid);
- }
-
- jQuery.event.special.movestart = {
- setup: setup,
- teardown: teardown,
- add: addMethod,
- remove: removeMethod,
- _default: function(e) {
- var template, data;
-
- // If no move events were bound to any ancestors of this
- // target, high tail it out of here.
- if (!e._handled()) { return; }
- template = {
- target: e.target,
- startX: e.startX,
- startY: e.startY,
- pageX: e.pageX,
- pageY: e.pageY,
- distX: e.distX,
- distY: e.distY,
- deltaX: e.deltaX,
- deltaY: e.deltaY,
- velocityX: e.velocityX,
- velocityY: e.velocityY,
- timeStamp: e.timeStamp,
- identifier: e.identifier,
- targetTouches: e.targetTouches,
- finger: e.finger
- };
- data = {
- event: template,
- timer: new Timer(function(time){
- trigger(e.target, template);
- })
- };
-
- if (e.identifier === undefined) {
- // We're dealing with a mouse
- // Stop clicks from propagating during a move
- add(e.target, 'click', returnFalse);
- add(document, mouseevents.move, activeMousemove, data);
- add(document, mouseevents.end, activeMouseend, data);
- }
- else {
- // We're dealing with a touch. Stop touchmove doing
- // anything defaulty.
- e._preventTouchmoveDefault();
- add(document, touchevents.move + '.' + e.identifier, activeTouchmove, data);
- add(document, touchevents.end + '.' + e.identifier, activeTouchend, data);
- }
- }
- };
- jQuery.event.special.move = {
- setup: function() {
- // Bind a noop to movestart. Why? It's the movestart
- // setup that decides whether other move events are fired.
- add(this, 'movestart.move', jQuery.noop);
- },
-
- teardown: function() {
- remove(this, 'movestart.move', jQuery.noop);
- }
- };
-
- jQuery.event.special.moveend = {
- setup: function() {
- // Bind a noop to movestart. Why? It's the movestart
- // setup that decides whether other move events are fired.
- add(this, 'movestart.moveend', jQuery.noop);
- },
-
- teardown: function() {
- remove(this, 'movestart.moveend', jQuery.noop);
- }
- };
- add(document, 'mousedown.move', mousedown);
- add(document, 'touchstart.move', touchstart);
- // Make jQuery copy touch event properties over to the jQuery event
- // object, if they are not already listed. But only do the ones we
- // really need. IE7/8 do not have Array#indexOf(), but nor do they
- // have touch events, so let's assume we can ignore them.
- if (typeof Array.prototype.indexOf === 'function') {
- (function(jQuery, undefined){
- var props = ["changedTouches", "targetTouches"],
- l = props.length;
-
- while (l--) {
- if (jQuery.event.props.indexOf(props[l]) === -1) {
- jQuery.event.props.push(props[l]);
- }
- }
- })(jQuery);
- };
- });
- // jQuery.event.swipe
- // 0.5
- // Stephen Band
- // Dependencies
- // jQuery.event.move 1.2
- // One of swipeleft, swiperight, swipeup or swipedown is triggered on
- // moveend, when the move has covered a threshold ratio of the dimension
- // of the target node, or has gone really fast. Threshold and velocity
- // sensitivity changed with:
- //
- // jQuery.event.special.swipe.settings.threshold
- // jQuery.event.special.swipe.settings.sensitivity
- (function (module) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], module);
- } else {
- // Browser globals
- module(jQuery);
- }
- })(function(jQuery, undefined){
- var add = jQuery.event.add,
-
- remove = jQuery.event.remove,
- // Just sugar, so we can have arguments in the same order as
- // add and remove.
- trigger = function(node, type, data) {
- jQuery.event.trigger(type, data, node);
- },
- settings = {
- // Ratio of distance over target finger must travel to be
- // considered a swipe.
- threshold: 0.2,
- // Faster fingers can travel shorter distances to be considered
- // swipes. 'sensitivity' controls how much. Bigger is shorter.
- sensitivity: 10
- };
- function moveend(e) {
- var w, h, event;
- w = e.target.offsetWidth;
- h = e.target.offsetHeight;
- // Copy over some useful properties from the move event
- event = {
- distX: e.distX,
- distY: e.distY,
- velocityX: e.velocityX,
- velocityY: e.velocityY,
- finger: e.finger
- };
- // Find out which of the four directions was swiped
- if (e.distX > e.distY) {
- if (e.distX > -e.distY) {
- if (e.distX/w > settings.threshold || e.velocityX * e.distX/w * settings.sensitivity > 1) {
- event.type = 'swiperight';
- trigger(e.currentTarget, event);
- }
- }
- else {
- if (-e.distY/h > settings.threshold || e.velocityY * e.distY/w * settings.sensitivity > 1) {
- event.type = 'swipeup';
- trigger(e.currentTarget, event);
- }
- }
- }
- else {
- if (e.distX > -e.distY) {
- if (e.distY/h > settings.threshold || e.velocityY * e.distY/w * settings.sensitivity > 1) {
- event.type = 'swipedown';
- trigger(e.currentTarget, event);
- }
- }
- else {
- if (-e.distX/w > settings.threshold || e.velocityX * e.distX/w * settings.sensitivity > 1) {
- event.type = 'swipeleft';
- trigger(e.currentTarget, event);
- }
- }
- }
- }
- function getData(node) {
- var data = jQuery.data(node, 'event_swipe');
-
- if (!data) {
- data = { count: 0 };
- jQuery.data(node, 'event_swipe', data);
- }
-
- return data;
- }
- jQuery.event.special.swipe =
- jQuery.event.special.swipeleft =
- jQuery.event.special.swiperight =
- jQuery.event.special.swipeup =
- jQuery.event.special.swipedown = {
- setup: function( data, namespaces, eventHandle ) {
- var data = getData(this);
- // If another swipe event is already setup, don't setup again.
- if (data.count++ > 0) { return; }
- add(this, 'moveend', moveend);
- return true;
- },
- teardown: function() {
- var data = getData(this);
- // If another swipe event is still setup, don't teardown.
- if (--data.count > 0) { return; }
- remove(this, 'moveend', moveend);
- return true;
- },
- settings: settings
- };
- });
|