vendor: 14,709 bytes, lines 1-586
1// jquery.event.move 2// 3// 1.3.6 4// 5// Stephen Band 6// 7// Triggers 'movestart', 'move' and 'moveend' events after 8// mousemoves following a mousedown cross a distance threshold, 9// similar to the native 'dragstart', 'drag' and 'dragend' events. 10// Move events are throttled to animation frames. Move event objects 11// have the properties: 12// 13// pageX: 14// pageY: Page coordinates of pointer. 15// startX: 16// startY: Page coordinates of pointer at movestart. 17// distX: 18// distY: Distance the pointer has moved since movestart. 19// deltaX: 20// deltaY: Distance the finger has moved since last event. 21// velocityX: 22// velocityY: Average velocity over last few events. 23 24 25(function (module) { 26 if (typeof define === 'function' && define.amd) { 27 // AMD. Register as an anonymous module. 28 define(['jquery'], module); 29 } else { 30 // Browser globals 31 module(jQuery); 32 } 33})(function(jQuery, undefined){ 34 35 var // Number of pixels a pressed pointer travels before movestart 36 // event is fired. 37 threshold = 6, 38 39 add = jQuery.event.add, 40 41 remove = jQuery.event.remove, 42 43 // Just sugar, so we can have arguments in the same order as 44 // add and remove. 45 trigger = function(node, type, data) { 46 jQuery.event.trigger(type, data, node); 47 }, 48 49 // Shim for requestAnimationFrame, falling back to timer. See: 50 // see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 51 requestFrame = (function(){ 52 return ( 53 window.requestAnimationFrame || 54 window.webkitRequestAnimationFrame || 55 window.mozRequestAnimationFrame || 56 window.oRequestAnimationFrame || 57 window.msRequestAnimationFrame || 58 function(fn, element){ 59 return window.setTimeout(function(){ 60 fn(); 61 }, 25); 62 } 63 ); 64 })(), 65 66 ignoreTags = { 67 textarea: true, 68 input: true, 69 select: true, 70 button: true 71 }, 72 73 mouseevents = { 74 move: 'mousemove', 75 cancel: 'mouseup dragstart', 76 end: 'mouseup' 77 }, 78 79 touchevents = { 80 move: 'touchmove', 81 cancel: 'touchend', 82 end: 'touchend' 83 }; 84 85 86 // Constructors 87 88 function Timer(fn){ 89 var callback = fn, 90 active = false, 91 running = false; 92 93 function trigger(time) { 94 if (active){ 95 callback(); 96 requestFrame(trigger); 97 running = true; 98 active = false; 99 } 100 else { 101 running = false; 102 } 103 } 104 105 this.kick = function(fn) { 106 active = true; 107 if (!running) { trigger(); } 108 }; 109 110 this.end = function(fn) { 111 var cb = callback; 112 113 if (!fn) { return; } 114 115 // If the timer is not running, simply call the end callback. 116 if (!running) { 117 fn(); 118 } 119 // If the timer is running, and has been kicked lately, then 120 // queue up the current callback and the end callback, otherwise 121 // just the end callback. 122 else { 123 callback = active ? 124 function(){ cb(); fn(); } : 125 fn ; 126 127 active = true; 128 } 129 }; 130 } 131 132 133 // Functions 134 135 function returnTrue() { 136 return true; 137 } 138 139 function returnFalse() { 140 return false; 141 } 142 143 function preventDefault(e) { 144 e.preventDefault(); 145 } 146 147 function preventIgnoreTags(e) { 148 // Don't prevent interaction with form elements. 149 if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; } 150 151 e.preventDefault(); 152 } 153 154 function isLeftButton(e) { 155 // Ignore mousedowns on any button other than the left (or primary) 156 // mouse button, or when a modifier key is pressed. 157 return (e.which === 1 && !e.ctrlKey && !e.altKey); 158 } 159 160 function identifiedTouch(touchList, id) { 161 var i, l; 162 163 if (touchList.identifiedTouch) { 164 return touchList.identifiedTouch(id); 165 } 166 167 // touchList.identifiedTouch() does not exist in 168 // webkit yet⦠we must do the search ourselves... 169 170 i = -1; 171 l = touchList.length; 172 173 while (++i < l) { 174 if (touchList[i].identifier === id) { 175 return touchList[i]; 176 } 177 } 178 } 179 180 function changedTouch(e, event) { 181 var touch = identifiedTouch(e.changedTouches, event.identifier); 182 183 // This isn't the touch you're looking for. 184 if (!touch) { return; } 185 186 // Chrome Android (at least) includes touches that have not 187 // changed in e.changedTouches. That's a bit annoying. Check 188 // that this touch has changed. 189 if (touch.pageX === event.pageX && touch.pageY === event.pageY) { return; } 190 191 return touch; 192 } 193 194 195 // Handlers that decide when the first movestart is triggered 196 197 function mousedown(e){ 198 var data; 199 200 if (!isLeftButton(e)) { return; } 201 202 data = { 203 target: e.target, 204 startX: e.pageX, 205 startY: e.pageY, 206 timeStamp: e.timeStamp 207 }; 208 209 add(document, mouseevents.move, mousemove, data); 210 add(document, mouseevents.cancel, mouseend, data); 211 } 212 213 function mousemove(e){ 214 var data = e.data; 215 216 checkThreshold(e, data, e, removeMouse); 217 } 218 219 function mouseend(e) { 220 removeMouse(); 221 } 222 223 function removeMouse() { 224 remove(document, mouseevents.move, mousemove); 225 remove(document, mouseevents.cancel, mouseend); 226 } 227 228 function touchstart(e) { 229 var touch, template; 230 231 // Don't get in the way of interaction with form elements. 232 if (ignoreTags[ e.target.tagName.toLowerCase() ]) { return; } 233 234 touch = e.changedTouches[0]; 235 236 // iOS live updates the touch objects whereas Android gives us copies. 237 // That means we can't trust the touchstart object to stay the same, 238 // so we must copy the data. This object acts as a template for 239 // movestart, move and moveend event objects. 240 template = { 241 target: touch.target, 242 startX: touch.pageX, 243 startY: touch.pageY, 244 timeStamp: e.timeStamp, 245 identifier: touch.identifier 246 }; 247 248 // Use the touch identifier as a namespace, so that we can later 249 // remove handlers pertaining only to this touch. 250 add(document, touchevents.move + '.' + touch.identifier, touchmove, template); 251 add(document, touchevents.cancel + '.' + touch.identifier, touchend, template); 252 } 253 254 function touchmove(e){ 255 var data = e.data, 256 touch = changedTouch(e, data); 257 258 if (!touch) { return; } 259 260 checkThreshold(e, data, touch, removeTouch); 261 } 262 263 function touchend(e) { 264 var template = e.data, 265 touch = identifiedTouch(e.changedTouches, template.identifier); 266 267 if (!touch) { return; } 268 269 removeTouch(template.identifier); 270 } 271 272 function removeTouch(identifier) { 273 remove(document, '.' + identifier, touchmove); 274 remove(document, '.' + identifier, touchend); 275 } 276 277 278 // Logic for deciding when to trigger a movestart. 279 280 function checkThreshold(e, template, touch, fn) { 281 var distX = touch.pageX - template.startX, 282 distY = touch.pageY - template.startY; 283 284 // Do nothing if the threshold has not been crossed. 285 if ((distX * distX) + (distY * distY) < (threshold * threshold)) { return; } 286 287 triggerStart(e, template, touch, distX, distY, fn); 288 } 289 290 function handled() { 291 // this._handled should return false once, and after return true. 292 this._handled = returnTrue; 293 return false; 294 } 295 296 function flagAsHandled(e) { 297 e._handled(); 298 } 299 300 function triggerStart(e, template, touch, distX, distY, fn) { 301 var node = template.target, 302 touches, time; 303 304 touches = e.targetTouches; 305 time = e.timeStamp - template.timeStamp; 306 307 // Create a movestart object with some special properties that 308 // are passed only to the movestart handlers. 309 template.type = 'movestart'; 310 template.distX = distX; 311 template.distY = distY; 312 template.deltaX = distX; 313 template.deltaY = distY; 314 template.pageX = touch.pageX; 315 template.pageY = touch.pageY; 316 template.velocityX = distX / time; 317 template.velocityY = distY / time; 318 template.targetTouches = touches; 319 template.finger = touches ? 320 touches.length : 321 1 ; 322 323 // The _handled method is fired to tell the default movestart 324 // handler that one of the move events is bound. 325 template._handled = handled; 326 327 // Pass the touchmove event so it can be prevented if or when 328 // movestart is handled. 329 template._preventTouchmoveDefault = function() { 330 e.preventDefault(); 331 }; 332 333 // Trigger the movestart event. 334 trigger(template.target, template); 335 336 // Unbind handlers that tracked the touch or mouse up till now. 337 fn(template.identifier); 338 } 339 340 341 // Handlers that control what happens following a movestart 342 343 function activeMousemove(e) { 344 var timer = e.data.timer; 345 346 e.data.touch = e; 347 e.data.timeStamp = e.timeStamp; 348 timer.kick(); 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 e.data.touch = touch; 383 e.data.timeStamp = e.timeStamp; 384 timer.kick(); 385 } 386 387 function activeTouchend(e) { 388 var event = e.data.event, 389 timer = e.data.timer, 390 touch = identifiedTouch(e.changedTouches, event.identifier); 391 392 // This isn't the touch you're looking for. 393 if (!touch) { return; } 394 395 removeActiveTouch(event); 396 endEvent(event, timer); 397 } 398 399 function removeActiveTouch(event) { 400 remove(document, '.' + event.identifier, activeTouchmove); 401 remove(document, '.' + event.identifier, activeTouchend); 402 } 403 404 405 // Logic for triggering move and moveend events 406 407 function updateEvent(event, touch, timeStamp, timer) { 408 var time = timeStamp - event.timeStamp; 409 410 event.type = 'move'; 411 event.distX = touch.pageX - event.startX; 412 event.distY = touch.pageY - event.startY; 413 event.deltaX = touch.pageX - event.pageX; 414 event.deltaY = touch.pageY - event.pageY; 415 416 // Average the velocity of the last few events using a decay 417 // curve to even out spurious jumps in values. 418 event.velocityX = 0.3 * event.velocityX + 0.7 * event.deltaX / time; 419 event.velocityY = 0.3 * event.velocityY + 0.7 * event.deltaY / time; 420 event.pageX = touch.pageX; 421 event.pageY = touch.pageY; 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 event, 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 function update(time) { 497 updateEvent(event, data.touch, data.timeStamp); 498 trigger(e.target, event); 499 } 500 501 event = { 502 target: e.target, 503 startX: e.startX, 504 startY: e.startY, 505 pageX: e.pageX, 506 pageY: e.pageY, 507 distX: e.distX, 508 distY: e.distY, 509 deltaX: e.deltaX, 510 deltaY: e.deltaY, 511 velocityX: e.velocityX, 512 velocityY: e.velocityY, 513 timeStamp: e.timeStamp, 514 identifier: e.identifier, 515 targetTouches: e.targetTouches, 516 finger: e.finger 517 }; 518 519 data = { 520 event: event, 521 timer: new Timer(update), 522 touch: undefined, 523 timeStamp: undefined 524 }; 525 526 if (e.identifier === undefined) { 527 // We're dealing with a mouse 528 // Stop clicks from propagating during a move 529 add(e.target, 'click', returnFalse); 530 add(document, mouseevents.move, activeMousemove, data); 531 add(document, mouseevents.end, activeMouseend, data); 532 } 533 else { 534 // We're dealing with a touch. Stop touchmove doing 535 // anything defaulty. 536 e._preventTouchmoveDefault(); 537 add(document, touchevents.move + '.' + e.identifier, activeTouchmove, data); 538 add(document, touchevents.end + '.' + e.identifier, activeTouchend, data); 539 } 540 } 541 }; 542 543 jQuery.event.special.move = { 544 setup: function() { 545 // Bind a noop to movestart. Why? It's the movestart 546 // setup that decides whether other move events are fired. 547 add(this, 'movestart.move', jQuery.noop); 548 }, 549 550 teardown: function() { 551 remove(this, 'movestart.move', jQuery.noop); 552 } 553 }; 554 555 jQuery.event.special.moveend = { 556 setup: function() { 557 // Bind a noop to movestart. Why? It's the movestart 558 // setup that decides whether other move events are fired. 559 add(this, 'movestart.moveend', jQuery.noop); 560 }, 561 562 teardown: function() { 563 remove(this, 'movestart.moveend', jQuery.noop); 564 } 565 }; 566 567 add(document, 'mousedown.move', mousedown); 568 add(document, 'touchstart.move', touchstart); 569 570 // Make jQuery copy touch event properties over to the jQuery event 571 // object, if they are not already listed. But only do the ones we 572 // really need. IE7/8 do not have Array#indexOf(), but nor do they 573 // have touch events, so let's assume we can ignore them. 574 if (typeof Array.prototype.indexOf === 'function') { 575 (function(jQuery, undefined){ 576 var props = ["changedTouches", "targetTouches"], 577 l = props.length; 578 579 while (l--) { 580 if (jQuery.event.props.indexOf(props[l]) === -1) { 581 jQuery.event.props.push(props[l]); 582 } 583 } 584 })(jQuery); 585 }; 586});
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.