1/* Event Swipe */ 2// jquery.event.move 3// 4// 1.3.1 5// 6// Stephen Band 7// 8// Triggers 'movestart', 'move' and 'moveend' events after 9// mousemoves following a mousedown cross a distance threshold, 10// similar to the native 'dragstart', 'drag' and 'dragend' events. 11// Move events are throttled to animation frames. Move event objects 12// have the properties: 13// 14// pageX: 15// pageY: Page coordinates of pointer. 16// startX: 17// startY: Page coordinates of pointer at movestart. 18// distX: 19// distY: Distance the pointer has moved since movestart. 20// deltaX: 21// deltaY: Distance the finger has moved since last event. 22// velocityX: 23// velocityY: Average velocity over last few events. 24 25 26(function (module) { 27 if (typeof define === 'function' && define.amd) { 28 // AMD. Register as an anonymous module. 29 define(['jquery'], module); 30 } else { 31 // Browser globals 32 module(jQuery); 33 } 34})(function(jQuery, undefined){ 35 36 var // Number of pixels a pressed pointer travels before movestart 37 // event is fired. 38 threshold = 6, 39 40 add = jQuery.event.add, 41 42 remove = jQuery.event.remove, 43 44 // Just sugar, so we can have arguments in the same order as 45 // add and remove. 46 trigger = function(node, type, data) { 47 jQuery.event.trigger(type, data, node); 48 }, 49 50 // Shim for requestAnimationFrame, falling back to timer. See: 51 // see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 52 requestFrame = (function(){ 53 return ( 54 window.requestAnimationFrame || 55 window.webkitRequestAnimationFrame || 56 window.mozRequestAnimationFrame || 57 window.oRequestAnimationFrame || 58 window.msRequestAnimationFrame || 59 function(fn, element){ 60 return window.setTimeout(function(){ 61 fn(); 62 }, 25); 63 } 64 ); 65 })(), 66 67 ignoreTags = { 68 textarea: true, 69 input: true, 70 select: true, 71 button: true 72 }, 73 74 mouseevents = { 75 move: 'mousemove', 76 cancel: 'mouseup dragstart', 77 end: 'mouseup' 78 }, 79 80 touchevents = { 81 move: 'touchmove', 82 cancel: 'touchend', 83 end: 'touchend' 84 }; 85 86 87 // Constructors 88 89 function Timer(fn){ 90 var callback = fn, 91 active = false, 92 running = false; 93 94 function trigger(time) { 95 if (active){ 96 callback(); 97 requestFrame(trigger); 98 running = true; 99 active = false; 100 } 101 else { 102 running = false; 103 } 104 } 105 106 this.kick = function(fn) { 107 active = true; 108 if (!running) { trigger(); } 109 }; 110 111 this.end = function(fn) { 112 var cb = callback; 113 114 if (!fn) { return; } 115 116 // If the timer is not running, simply call the end callback. 117 if (!running) { 118 fn(); 119 } 120 // If the timer is running, and has been kicked lately, then 121 // queue up the current callback and the end callback, otherwize 122 // just the end callback. 123 else { 124 callback = active ? 125 function(){ cb(); fn(); } : 126 fn ; 127 128 active = true; 129 } 130 }; 131 } 132 133 134 // Functions 135 136 function returnTrue() { 137 return true; 138 } 139 140 function returnFalse() { 141 return false; 142 } 143 144 function preventDefault(e) { 145 e.preventDefault(); 146 } 147 148 function preventIgnoreTags(e) { 149 // Don't prevent interaction with form elements. 150 if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; } 151 152 e.preventDefault(); 153 } 154 155 function isLeftButton(e) { 156 // Ignore mousedowns on any button other than the left (or primary) 157 // mouse button, or when a modifier key is pressed. 158 return (e.which === 1 && !e.ctrlKey && !e.altKey); 159 } 160 161 function identifiedTouch(touchList, id) { 162 var i, l; 163 164 if (touchList.identifiedTouch) { 165 return touchList.identifiedTouch(id); 166 } 167 168 // touchList.identifiedTouch() does not exist in 169 // webkit yet⦠we must do the search ourselves... 170 171 i = -1; 172 l = touchList.length; 173 174 while (++i < l) { 175 if (touchList[i].identifier === id) { 176 return touchList[i]; 177 } 178 } 179 } 180 181 function changedTouch(e, event) { 182 var touch = identifiedTouch(e.changedTouches, event.identifier); 183 184 // This isn't the touch you're looking for. 185 if (!touch) { return; } 186 187 // Chrome Android (at least) includes touches that have not 188 // changed in e.changedTouches. That's a bit annoying. Check 189 // that this touch has changed. 190 if (touch.pageX === event.pageX && touch.pageY === event.pageY) { return; } 191 192 return touch; 193 } 194 195 196 // Handlers that decide when the first movestart is triggered 197 198 function mousedown(e){ 199 var data; 200 201 if (!isLeftButton(e)) { return; } 202 203 data = { 204 target: e.target, 205 startX: e.pageX, 206 startY: e.pageY, 207 timeStamp: e.timeStamp 208 }; 209 210 add(document, mouseevents.move, mousemove, data); 211 add(document, mouseevents.cancel, mouseend, data); 212 } 213 214 function mousemove(e){ 215 var data = e.data; 216 217 checkThreshold(e, data, e, removeMouse); 218 } 219 220 function mouseend(e) { 221 removeMouse(); 222 } 223 224 function removeMouse() { 225 remove(document, mouseevents.move, mousemove); 226 remove(document, mouseevents.cancel, mouseend); 227 } 228 229 function touchstart(e) { 230 var touch, template; 231 232 // Don't get in the way of interaction with form elements. 233 if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; } 234 235 touch = e.changedTouches[0]; 236
237 // iOS live updates the touch objects whereas Android gives us copies. 238 // That means we can't trust the touchstart object to stay the same, 239 // so we must copy the data. This object acts as a template for 240 // movestart, move and moveend event objects. 241 template = { 242 target: touch.target, 243 startX: touch.pageX, 244 startY: touch.pageY, 245 timeStamp: e.timeStamp, 246 identifier: touch.identifier 247 }; 248 249 // Use the touch identifier as a namespace, so that we can later 250 // remove handlers pertaining only to this touch. 251 add(document, touchevents.move + '.' + touch.identifier, touchmove, template); 252 add(document, touchevents.cancel + '.' + touch.identifier, touchend, template); 253 } 254 255 function touchmove(e){ 256 var data = e.data, 257 touch = changedTouch(e, data); 258 259 if (!touch) { return; } 260 261 checkThreshold(e, data, touch, removeTouch); 262 } 263 264 function touchend(e) { 265 var template = e.data, 266 touch = identifiedTouch(e.changedTouches, template.identifier); 267 268 if (!touch) { return; } 269 270 removeTouch(template.identifier); 271 } 272 273 function removeTouch(identifier) { 274 remove(document, '.' + identifier, touchmove); 275 remove(document, '.' + identifier, touchend); 276 } 277 278 279 // Logic for deciding when to trigger a movestart. 280 281 function checkThreshold(e, template, touch, fn) { 282 var distX = touch.pageX - template.startX, 283 distY = touch.pageY - template.startY; 284 285 // Do nothing if the threshold has not been crossed. 286 if ((distX * distX) + (distY * distY) < (threshold * threshold)) { return; } 287 288 triggerStart(e, template, touch, distX, distY, fn); 289 } 290 291 function handled() { 292 // this._handled should return false once, and after return true. 293 this._handled = returnTrue; 294 return false; 295 } 296 297 function flagAsHandled(e) { 298 e._handled(); 299 } 300 301 function triggerStart(e, template, touch, distX, distY, fn) { 302 var node = template.target, 303 touches, time; 304 305 touches = e.targetTouches; 306 time = e.timeStamp - template.timeStamp; 307 308 // Create a movestart object with some special properties that 309 // are passed only to the movestart handlers. 310 template.type = 'movestart'; 311 template.distX = distX; 312 template.distY = distY; 313 template.deltaX = distX; 314 template.deltaY = distY; 315 template.pageX = touch.pageX; 316 template.pageY = touch.pageY; 317 template.velocityX = distX / time; 318 template.velocityY = distY / time; 319 template.targetTouches = touches; 320 template.finger = touches ? 321 touches.length : 322 1 ; 323 324 // The _handled method is fired to tell the default movestart 325 // handler that one of the move events is bound. 326 template._handled = handled; 327 328 // Pass the touchmove event so it can be prevented if or when 329 // movestart is handled. 330 template._preventTouchmoveDefault = function() { 331 e.preventDefault(); 332 }; 333 334 // Trigger the movestart event. 335 trigger(template.target, template); 336 337 // Unbind handlers that tracked the touch or mouse up till now. 338 fn(template.identifier); 339 } 340 341 342 // Handlers that control what happens following a movestart 343 344 function activeMousemove(e) { 345 var event = e.data.event, 346 timer = e.data.timer; 347 348 updateEvent(event, e, e.timeStamp, timer); 349 } 350 351 function activeMouseend(e) { 352 var event = e.data.event, 353 timer = e.data.timer; 354 355 removeActiveMouse(); 356 357 endEvent(event, timer, function() { 358 // Unbind the click suppressor, waiting until after mouseup 359 // has been handled. 360 setTimeout(function(){ 361 remove(event.target, 'click', returnFalse); 362 }, 0); 363 }); 364 } 365 366 function removeActiveMouse(event) { 367 remove(document, mouseevents.move, activeMousemove); 368 remove(document, mouseevents.end, activeMouseend); 369 } 370 371 function activeTouchmove(e) { 372 var event = e.data.event, 373 timer = e.data.timer, 374 touch = changedTouch(e, event); 375 376 if (!touch) { return; } 377 378 // Stop the interface from gesturing 379 e.preventDefault(); 380 381 event.targetTouches = e.targetTouches; 382 updateEvent(event, touch, e.timeStamp, timer); 383 } 384 385 function activeTouchend(e) { 386 var event = e.data.event, 387 timer = e.data.timer, 388 touch = identifiedTouch(e.changedTouches, event.identifier); 389 390 // This isn't the touch you're looking for. 391 if (!touch) { return; } 392 393 removeActiveTouch(event); 394 endEvent(event, timer); 395 } 396 397 function removeActiveTouch(event) {
398 remove(document, '.' + event.identifier, activeTouchmove); 399 remove(document, '.' + event.identifier, activeTouchend); 400 } 401 402 403 // Logic for triggering move and moveend events 404 405 function updateEvent(event, touch, timeStamp, timer) { 406 var time = timeStamp - event.timeStamp; 407 408 event.type = 'move'; 409 event.distX = touch.pageX - event.startX; 410 event.distY = touch.pageY - event.startY; 411 event.deltaX = touch.pageX - event.pageX; 412 event.deltaY = touch.pageY - event.pageY; 413 414 // Average the velocity of the last few events using a decay 415 // curve to even out spurious jumps in values. 416 event.velocityX = 0.3 * event.velocityX + 0.7 * event.deltaX / time; 417 event.velocityY = 0.3 * event.velocityY + 0.7 * event.deltaY / time; 418 event.pageX = touch.pageX; 419 event.pageY = touch.pageY; 420 421 timer.kick(); 422 } 423 424 function endEvent(event, timer, fn) { 425 timer.end(function(){ 426 event.type = 'moveend'; 427 428 trigger(event.target, event); 429 430 return fn && fn(); 431 }); 432 } 433 434 435 // jQuery special event definition 436 437 function setup(data, namespaces, eventHandle) { 438 // Stop the node from being dragged 439 //add(this, 'dragstart.move drag.move', preventDefault); 440 441 // Prevent text selection and touch interface scrolling 442 //add(this, 'mousedown.move', preventIgnoreTags); 443 444 // Tell movestart default handler that we've handled this 445 add(this, 'movestart.move', flagAsHandled); 446 447 // Don't bind to the DOM. For speed. 448 return true; 449 } 450 451 function teardown(namespaces) { 452 remove(this, 'dragstart drag', preventDefault); 453 remove(this, 'mousedown touchstart', preventIgnoreTags); 454 remove(this, 'movestart', flagAsHandled); 455 456 // Don't bind to the DOM. For speed. 457 return true; 458 } 459 460 function addMethod(handleObj) { 461 // We're not interested in preventing defaults for handlers that 462 // come from internal move or moveend bindings 463 if (handleObj.namespace === "move" || handleObj.namespace === "moveend") { 464 return; 465 } 466 467 // Stop the node from being dragged 468 add(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid, preventDefault, undefined, handleObj.selector); 469 470 // Prevent text selection and touch interface scrolling 471 add(this, 'mousedown.' + handleObj.guid, preventIgnoreTags, undefined, handleObj.selector); 472 } 473 474 function removeMethod(handleObj) { 475 if (handleObj.namespace === "move" || handleObj.namespace === "moveend") { 476 return; 477 } 478 479 remove(this, 'dragstart.' + handleObj.guid + ' drag.' + handleObj.guid); 480 remove(this, 'mousedown.' + handleObj.guid); 481 } 482 483 jQuery.event.special.movestart = { 484 setup: setup, 485 teardown: teardown, 486 add: addMethod, 487 remove: removeMethod, 488 489 _default: function(e) { 490 var template, data; 491 492 // If no move events were bound to any ancestors of this 493 // target, high tail it out of here. 494 if (!e._handled()) { return; } 495 496 template = { 497 target: e.target, 498 startX: e.startX, 499 startY: e.startY, 500 pageX: e.pageX, 501 pageY: e.pageY, 502 distX: e.distX, 503 distY: e.distY, 504 deltaX: e.deltaX, 505 deltaY: e.deltaY, 506 velocityX: e.velocityX, 507 velocityY: e.velocityY, 508 timeStamp: e.timeStamp, 509 identifier: e.identifier, 510 targetTouches: e.targetTouches, 511 finger: e.finger 512 }; 513 514 data = { 515 event: template, 516 timer: new Timer(function(time){ 517 trigger(e.target, template); 518 }) 519 }; 520 521 if (e.identifier === undefined) { 522 // We're dealing with a mouse 523 // Stop clicks from propagating during a move 524 add(e.target, 'click', returnFalse); 525 add(document, mouseevents.move, activeMousemove, data); 526 add(document, mouseevents.end, activeMouseend, data); 527 } 528 else { 529 // We're dealing with a touch. Stop touchmove doing 530 // anything defaulty. 531 e._preventTouchmoveDefault(); 532 add(document, touchevents.move + '.' + e.identifier, activeTouchmove, data); 533 add(document, touchevents.end + '.' + e.identifier, activeTouchend, data); 534 } 535 } 536 }; 537 538 jQuery.event.special.move = { 539 setup: function() { 540 // Bind a noop to movestart. Why? It's the movestart 541 // setup that decides whether other move events are fired. 542 add(this, 'movestart.move', jQuery.noop); 543 }, 544 545 teardown: function() { 546 remove(this, 'movestart.move', jQuery.noop); 547 } 548 }; 549 550 jQuery.event.special.moveend = { 551 setup: function() { 552 // Bind a noop to movestart. Why? It's the movestart 553 // setup that decides whether other move events are fired. 554 add(this, 'movestart.moveend', jQuery.noop); 555 }, 556 557 teardown: function() { 558 remove(this, 'movestart.moveend', jQuery.noop); 559 } 560 }; 561 562 add(document, 'mousedown.move', mousedown);
563 add(document, 'touchstart.move', touchstart); 564 565 // Make jQuery copy touch event properties over to the jQuery event 566 // object, if they are not already listed. But only do the ones we 567 // really need. IE7/8 do not have Array#indexOf(), but nor do they 568 // have touch events, so let's assume we can ignore them. 569 if (typeof Array.prototype.indexOf === 'function') { 570 (function(jQuery, undefined){ 571 var props = ["changedTouches", "targetTouches"], 572 l = props.length; 573 574 while (l--) { 575 if (jQuery.event.props.indexOf(props[l]) === -1) { 576 jQuery.event.props.push(props[l]); 577 } 578 } 579 })(jQuery); 580 } 581}); 582 583// jQuery.event.swipe 584// 0.5 585// Stephen Band 586 587// Dependencies 588// jQuery.event.move 1.2 589 590// One of swipeleft, swiperight, swipeup or swipedown is triggered on 591// moveend, when the move has covered a threshold ratio of the dimension 592// of the target node, or has gone really fast. Threshold and velocity 593// sensitivity changed with: 594// 595// jQuery.event.special.swipe.settings.threshold 596// jQuery.event.special.swipe.settings.sensitivity 597 598(function (module) { 599 if (typeof define === 'function' && define.amd) { 600 // AMD. Register as an anonymous module. 601 define(['jquery'], module); 602 } else { 603 // Browser globals 604 module(jQuery); 605 } 606})(function(jQuery, undefined){ 607 var add = jQuery.event.add, 608 609 remove = jQuery.event.remove, 610 611 // Just sugar, so we can have arguments in the same order as 612 // add and remove. 613 trigger = function(node, type, data) { 614 jQuery.event.trigger(type, data, node); 615 }, 616 617 settings = { 618 // Ratio of distance over target finger must travel to be 619 // considered a swipe. 620 threshold: 0.4, 621 // Faster fingers can travel shorter distances to be considered 622 // swipes. 'sensitivity' controls how much. Bigger is shorter. 623 sensitivity: 6 624 }; 625 626 function moveend(e) { 627 var w, h, event; 628 629 w = e.currentTarget.offsetWidth; 630 h = e.currentTarget.offsetHeight; 631 632 // Copy over some useful properties from the move event 633 event = { 634 distX: e.distX, 635 distY: e.distY, 636 velocityX: e.velocityX, 637 velocityY: e.velocityY, 638 finger: e.finger 639 }; 640 641 // Find out which of the four directions was swiped 642 if (e.distX > e.distY) { 643 if (e.distX > -e.distY) { 644 if (e.distX/w > settings.threshold || e.velocityX * e.distX/w * settings.sensitivity > 1) { 645 event.type = 'swiperight'; 646 trigger(e.currentTarget, event); 647 } 648 } 649 else { 650 if (-e.distY/h > settings.threshold || e.velocityY * e.distY/w * settings.sensitivity > 1) { 651 event.type = 'swipeup'; 652 trigger(e.currentTarget, event); 653 } 654 } 655 } 656 else { 657 if (e.distX > -e.distY) { 658 if (e.distY/h > settings.threshold || e.velocityY * e.distY/w * settings.sensitivity > 1) { 659 event.type = 'swipedown'; 660 trigger(e.currentTarget, event); 661 } 662 } 663 else { 664 if (-e.distX/w > settings.threshold || e.velocityX * e.distX/w * settings.sensitivity > 1) { 665 event.type = 'swipeleft'; 666 trigger(e.currentTarget, event); 667 } 668 } 669 } 670 } 671 672 function getData(node) { 673 var data = jQuery.data(node, 'event_swipe'); 674 675 if (!data) { 676 data = { count: 0 }; 677 jQuery.data(node, 'event_swipe', data); 678 } 679 680 return data; 681 } 682 683 jQuery.event.special.swipe = 684 jQuery.event.special.swipeleft = 685 jQuery.event.special.swiperight = 686 jQuery.event.special.swipeup = 687 jQuery.event.special.swipedown = { 688 setup: function( data, namespaces, eventHandle ) { 689 var data = getData(this); 690 691 // If another swipe event is already setup, don't setup again. 692 if (data.count++ > 0) { return; } 693 694 add(this, 'moveend', moveend); 695 696 return true; 697 }, 698 699 teardown: function() { 700 var data = getData(this); 701 702 // If another swipe event is still setup, don't teardown. 703 if (--data.count > 0) { return; } 704 705 remove(this, 'moveend', moveend); 706 707 return true; 708 }, 709 710 settings: settings 711 }; 712});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.