vendor: 18,241 bytes, lines 1-706
1/*! Hammer.JS - v2.0.4 - 2014-09-28 2 * http://hammerjs.github.io/ 3 * 4 * Copyright (c) 2014 Jorik Tangelder; 5 * Licensed under the MIT license */ 6(function(window, document, exportName, undefined) { 7 'use strict'; 8 9 var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o']; 10 var TEST_ELEMENT = document.createElement('div'); 11 12 var TYPE_FUNCTION = 'function'; 13 14 var round = Math.round; 15 var abs = Math.abs; 16 var now = Date.now; 17 18 /** 19 * set a timeout with a given scope 20 * @param {Function} fn 21 * @param {Number} timeout 22 * @param {Object} context 23 * @returns {number} 24 */ 25 function setTimeoutContext(fn, timeout, context) { 26 return setTimeout(bindFn(fn, context), timeout); 27 } 28 29 /** 30 * if the argument is an array, we want to execute the fn on each entry 31 * if it aint an array we don't want to do a thing. 32 * this is used by all the methods that accept a single and array argument. 33 * @param {*|Array} arg 34 * @param {String} fn 35 * @param {Object} [context] 36 * @returns {Boolean} 37 */ 38 function invokeArrayArg(arg, fn, context) { 39 if (Array.isArray(arg)) { 40 each(arg, context[fn], context); 41 return true; 42 } 43 return false; 44 } 45 46 /** 47 * walk objects and arrays 48 * @param {Object} obj 49 * @param {Function} iterator 50 * @param {Object} context 51 */ 52 function each(obj, iterator, context) { 53 var i; 54 55 if (!obj) { 56 return; 57 } 58 59 if (obj.forEach) { 60 obj.forEach(iterator, context); 61 } else if (obj.length !== undefined) { 62 i = 0; 63 while (i < obj.length) { 64 iterator.call(context, obj[i], i, obj); 65 i++; 66 } 67 } else { 68 for (i in obj) { 69 obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); 70 } 71 } 72 } 73 74 /** 75 * extend object. 76 * means that properties in dest will be overwritten by the ones in src. 77 * @param {Object} dest 78 * @param {Object} src 79 * @param {Boolean} [merge] 80 * @returns {Object} dest 81 */ 82 function extend(dest, src, merge) { 83 var keys = Object.keys(src); 84 var i = 0; 85 while (i < keys.length) { 86 if (!merge || (merge && dest[keys[i]] === undefined)) { 87 dest[keys[i]] = src[keys[i]]; 88 } 89 i++; 90 } 91 return dest; 92 } 93 94 /** 95 * merge the values from src in the dest. 96 * means that properties that exist in dest will not be overwritten by src 97 * @param {Object} dest 98 * @param {Object} src 99 * @returns {Object} dest 100 */ 101 function merge(dest, src) { 102 return extend(dest, src, true); 103 } 104 105 /** 106 * simple class inheritance 107 * @param {Function} child 108 * @param {Function} base 109 * @param {Object} [properties] 110 */ 111 function inherit(child, base, properties) { 112 var baseP = base.prototype, 113 childP; 114 115 childP = child.prototype = Object.create(baseP); 116 childP.constructor = child; 117 childP._super = baseP; 118 119 if (properties) { 120 extend(childP, properties); 121 } 122 } 123 124 /** 125 * simple function bind 126 * @param {Function} fn 127 * @param {Object} context 128 * @returns {Function} 129 */ 130 function bindFn(fn, context) { 131 return function boundFn() { 132 return fn.apply(context, arguments); 133 }; 134 } 135 136 /** 137 * let a boolean value also be a function that must return a boolean 138 * this first item in args will be used as the context 139 * @param {Boolean|Function} val 140 * @param {Array} [args] 141 * @returns {Boolean} 142 */ 143 function boolOrFn(val, args) { 144 if (typeof val == TYPE_FUNCTION) { 145 return val.apply(args ? args[0] || undefined : undefined, args); 146 } 147 return val; 148 } 149 150 /** 151 * use the val2 when val1 is undefined 152 * @param {*} val1 153 * @param {*} val2 154 * @returns {*} 155 */ 156 function ifUndefined(val1, val2) { 157 return (val1 === undefined) ? val2 : val1; 158 } 159 160 /** 161 * addEventListener with multiple events at once 162 * @param {EventTarget} target 163 * @param {String} types 164 * @param {Function} handler 165 */ 166 function addEventListeners(target, types, handler) { 167 each(splitStr(types), function(type) { 168 target.addEventListener(type, handler, false); 169 }); 170 } 171 172 /** 173 * removeEventListener with multiple events at once 174 * @param {EventTarget} target 175 * @param {String} types 176 * @param {Function} handler 177 */ 178 function removeEventListeners(target, types, handler) { 179 each(splitStr(types), function(type) { 180 target.removeEventListener(type, handler, false); 181 }); 182 } 183 184 /** 185 * find if a node is in the given parent 186 * @method hasParent 187 * @param {HTMLElement} node 188 * @param {HTMLElement} parent 189 * @return {Boolean} found 190 */ 191 function hasParent(node, parent) { 192 while (node) { 193 if (node == parent) { 194 return true; 195 } 196 node = node.parentNode; 197 } 198 return false; 199 } 200 201 /** 202 * small indexOf wrapper 203 * @param {String} str 204 * @param {String} find 205 * @returns {Boolean} found 206 */ 207 function inStr(str, find) { 208 return str.indexOf(find) > -1; 209 } 210 211 /** 212 * split string on whitespace 213 * @param {String} str 214 * @returns {Array} words 215 */ 216 function splitStr(str) { 217 return str.trim().split(/\s+/g); 218 } 219 220 /** 221 * find if a array contains the object using indexOf or a simple polyFill 222 * @param {Array} src 223 * @param {String} find 224 * @param {String} [findByKey] 225 * @return {Boolean|Number} false when not found, or the index 226 */ 227 function inArray(src, find, findByKey) { 228 if (src.indexOf && !findByKey) { 229 return src.indexOf(find); 230 } else { 231 var i = 0; 232 while (i < src.length) { 233 if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { 234 return i; 235 } 236 i++; 237 } 238 return -1; 239 } 240 } 241 242 /** 243 * convert array-like objects to real arrays 244 * @param {Object} obj 245 * @returns {Array} 246 */ 247 function toArray(obj) { 248 return Array.prototype.slice.call(obj, 0); 249 } 250 251 /** 252 * unique array with objects based on a key (like 'id') or just by the array's value 253 * @param {Array} src [{id:1},{id:2},{id:1}] 254 * @param {String} [key] 255 * @param {Boolean} [sort=False] 256 * @returns {Array} [{id:1},{id:2}] 257 */ 258 function uniqueArray(src, key, sort) { 259 var results = []; 260 var values = []; 261 var i = 0; 262 263 while (i < src.length) { 264 var val = key ? src[i][key] : src[i]; 265 if (inArray(values, val) < 0) { 266 results.push(src[i]); 267 } 268 values[i] = val; 269 i++; 270 } 271 272 if (sort) { 273 if (!key) { 274 results = results.sort(); 275 } else { 276 results = results.sort(function sortUniqueArray(a, b) { 277 return a[key] > b[key]; 278 }); 279 } 280 } 281 282 return results; 283 } 284 285 /** 286 * get the prefixed property 287 * @param {Object} obj 288 * @param {String} property 289 * @returns {String|Undefined} prefixed 290 */ 291 function prefixed(obj, property) { 292 var prefix, prop; 293 var camelProp = property[0].toUpperCase() + property.slice(1); 294 295 var i = 0; 296 while (i < VENDOR_PREFIXES.length) { 297 prefix = VENDOR_PREFIXES[i]; 298 prop = (prefix) ? prefix + camelProp : property; 299 300 if (prop in obj) { 301 return prop; 302 } 303 i++; 304 } 305 return undefined; 306 } 307 308 /** 309 * get a unique id 310 * @returns {number} uniqueId 311 */ 312 var _uniqueId = 1; 313 314 function uniqueId() { 315 return _uniqueId++; 316 } 317 318 /** 319 * get the window object of an element 320 * @param {HTMLElement} element 321 * @returns {DocumentView|Window} 322 */ 323 function getWindowForElement(element) { 324 var doc = element.ownerDocument; 325 return (doc.defaultView || doc.parentWindow); 326 } 327 328 var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; 329 330 var SUPPORT_TOUCH = ('ontouchstart' in window); 331 var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; 332 var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); 333 334 var INPUT_TYPE_TOUCH = 'touch'; 335 var INPUT_TYPE_PEN = 'pen'; 336 var INPUT_TYPE_MOUSE = 'mouse'; 337 var INPUT_TYPE_KINECT = 'kinect'; 338 339 var COMPUTE_INTERVAL = 25; 340 341 var INPUT_START = 1; 342 var INPUT_MOVE = 2; 343 var INPUT_END = 4; 344 var INPUT_CANCEL = 8; 345 346 var DIRECTION_NONE = 1; 347 var DIRECTION_LEFT = 2; 348 var DIRECTION_RIGHT = 4; 349 var DIRECTION_UP = 8; 350 var DIRECTION_DOWN = 16; 351 352 var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; 353 var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; 354 var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; 355 356 var PROPS_XY = ['x', 'y']; 357 var PROPS_CLIENT_XY = ['clientX', 'clientY']; 358 359 /** 360 * create new input type manager 361 * @param {Manager} manager 362 * @param {Function} callback 363 * @returns {Input} 364 * @constructor 365 */ 366 function Input(manager, callback) { 367 var self = this; 368 this.manager = manager; 369 this.callback = callback; 370 this.element = manager.element; 371 this.target = manager.options.inputTarget; 372 373 // smaller wrapper around the handler, for the scope and the enabled state of the manager, 374 // so when disabled the input events are completely bypassed. 375 this.domHandler = function(ev) { 376 if (boolOrFn(manager.options.enable, [manager])) { 377 self.handler(ev); 378 } 379 }; 380 381 this.init(); 382 383 } 384 385 Input.prototype = { 386 /** 387 * should handle the inputEvent data and trigger the callback 388 * @virtual 389 */ 390 handler: function() { 391 }, 392 393 /** 394 * bind the events 395 */ 396 init: function() { 397 this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); 398 this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); 399 this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 400 }, 401 402 /** 403 * unbind the events 404 */ 405 destroy: function() { 406 this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); 407 this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); 408 this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 409 } 410 }; 411 412 /** 413 * create new input type manager 414 * called by the Manager constructor 415 * @param {Hammer} manager 416 * @returns {Input} 417 */ 418 function createInputInstance(manager) { 419 var Type; 420 var inputClass = manager.options.inputClass; 421 422 if (inputClass) { 423 Type = inputClass; 424 } else if (SUPPORT_POINTER_EVENTS) { 425 Type = PointerEventInput; 426 } else if (SUPPORT_ONLY_TOUCH) { 427 Type = TouchInput; 428 } else if (!SUPPORT_TOUCH) { 429 Type = MouseInput; 430 } else { 431 Type = TouchMouseInput; 432 } 433 return new (Type)(manager, inputHandler); 434 } 435 436 /** 437 * handle input events 438 * @param {Manager} manager 439 * @param {String} eventType 440 * @param {Object} input 441 */ 442 function inputHandler(manager, eventType, input) { 443 var pointersLen = input.pointers.length; 444 var changedPointersLen = input.changedPointers.length; 445 var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); 446 var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); 447 448 input.isFirst = !!isFirst; 449 input.isFinal = !!isFinal; 450 451 if (isFirst) { 452 manager.session = {}; 453 } 454 455 // source event is the normalized value of the domEvents 456 // like 'touchstart, mouseup, pointerdown' 457 input.eventType = eventType; 458 459 // compute scale, rotation etc 460 computeInputData(manager, input); 461 462 // emit secret event 463 manager.emit('hammer.input', input); 464 465 manager.recognize(input); 466 manager.session.prevInput = input; 467 } 468 469 /** 470 * extend the data with some usable properties like scale, rotate, velocity etc 471 * @param {Object} manager 472 * @param {Object} input 473 */ 474 function computeInputData(manager, input) { 475 var session = manager.session; 476 var pointers = input.pointers; 477 var pointersLength = pointers.length; 478 479 // store the first input to calculate the distance and direction 480 if (!session.firstInput) { 481 session.firstInput = simpleCloneInputData(input); 482 } 483 484 // to compute scale and rotation we need to store the multiple touches 485 if (pointersLength > 1 && !session.firstMultiple) { 486 session.firstMultiple = simpleCloneInputData(input); 487 } else if (pointersLength === 1) { 488 session.firstMultiple = false; 489 } 490 491 var firstInput = session.firstInput; 492 var firstMultiple = session.firstMultiple; 493 var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; 494 495 var center = input.center = getCenter(pointers); 496 input.timeStamp = now(); 497 input.deltaTime = input.timeStamp - firstInput.timeStamp; 498 499 input.angle = getAngle(offsetCenter, center); 500 input.distance = getDistance(offsetCenter, center); 501 502 computeDeltaXY(session, input); 503 input.offsetDirection = getDirection(input.deltaX, input.deltaY); 504 505 input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; 506 input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; 507 508 computeIntervalInputData(session, input); 509 510 // find the correct target 511 var target = manager.element; 512 if (hasParent(input.srcEvent.target, target)) { 513 target = input.srcEvent.target; 514 } 515 input.target = target; 516 } 517 518 function computeDeltaXY(session, input) { 519 var center = input.center; 520 var offset = session.offsetDelta || {}; 521 var prevDelta = session.prevDelta || {}; 522 var prevInput = session.prevInput || {}; 523 524 if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { 525 prevDelta = session.prevDelta = { 526 x: prevInput.deltaX || 0, 527 y: prevInput.deltaY || 0 528 }; 529 530 offset = session.offsetDelta = { 531 x: center.x, 532 y: center.y 533 }; 534 } 535 536 input.deltaX = prevDelta.x + (center.x - offset.x); 537 input.deltaY = prevDelta.y + (center.y - offset.y); 538 } 539 540 /** 541 * velocity is calculated every x ms 542 * @param {Object} session 543 * @param {Object} input 544 */ 545 function computeIntervalInputData(session, input) { 546 var last = session.lastInterval || input, 547 deltaTime = input.timeStamp - last.timeStamp, 548 velocity, velocityX, velocityY, direction; 549 550 if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { 551 var deltaX = last.deltaX - input.deltaX; 552 var deltaY = last.deltaY - input.deltaY; 553 554 var v = getVelocity(deltaTime, deltaX, deltaY); 555 velocityX = v.x; 556 velocityY = v.y; 557 velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; 558 direction = getDirection(deltaX, deltaY); 559 560 session.lastInterval = input; 561 } else { 562 // use latest velocity info if it doesn't overtake a minimum period 563 velocity = last.velocity; 564 velocityX = last.velocityX; 565 velocityY = last.velocityY; 566 direction = last.direction; 567 } 568 569 input.velocity = velocity; 570 input.velocityX = velocityX; 571 input.velocityY = velocityY; 572 input.direction = direction; 573 } 574 575 /** 576 * create a simple clone from the input used for storage of firstInput and firstMultiple 577 * @param {Object} input 578 * @returns {Object} clonedInputData 579 */ 580 function simpleCloneInputData(input) { 581 // make a simple copy of the pointers because we will get a reference if we don't 582 // we only need clientXY for the calculations 583 var pointers = []; 584 var i = 0; 585 while (i < input.pointers.length) { 586 pointers[i] = { 587 clientX: round(input.pointers[i].clientX), 588 clientY: round(input.pointers[i].clientY) 589 }; 590 i++; 591 } 592 593 return { 594 timeStamp: now(), 595 pointers: pointers, 596 center: getCenter(pointers), 597 deltaX: input.deltaX, 598 deltaY: input.deltaY 599 }; 600 } 601 602 /** 603 * get the center of all the pointers 604 * @param {Array} pointers 605 * @return {Object} center contains `x` and `y` properties 606 */ 607 function getCenter(pointers) { 608 var pointersLength = pointers.length; 609 610 // no need to loop when only one touch 611 if (pointersLength === 1) { 612 return { 613 x: round(pointers[0].clientX), 614 y: round(pointers[0].clientY) 615 }; 616 } 617 618 var x = 0, y = 0, i = 0; 619 while (i < pointersLength) { 620 x += pointers[i].clientX; 621 y += pointers[i].clientY; 622 i++; 623 } 624 625 return { 626 x: round(x / pointersLength), 627 y: round(y / pointersLength) 628 }; 629 } 630 631 /** 632 * calculate the velocity between two points. unit is in px per ms. 633 * @param {Number} deltaTime 634 * @param {Number} x 635 * @param {Number} y 636 * @return {Object} velocity `x` and `y` 637 */ 638 function getVelocity(deltaTime, x, y) { 639 return { 640 x: x / deltaTime || 0, 641 y: y / deltaTime || 0 642 }; 643 } 644 645 /** 646 * get the direction between two points 647 * @param {Number} x 648 * @param {Number} y 649 * @return {Number} direction 650 */ 651 function getDirection(x, y) { 652 if (x === y) { 653 return DIRECTION_NONE; 654 } 655 656 if (abs(x) >= abs(y)) { 657 return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; 658 } 659 return y > 0 ? DIRECTION_UP : DIRECTION_DOWN; 660 } 661 662 /** 663 * calculate the absolute distance between two points 664 * @param {Object} p1 {x, y} 665 * @param {Object} p2 {x, y} 666 * @param {Array} [props] containing x and y keys 667 * @return {Number} distance 668 */ 669 function getDistance(p1, p2, props) { 670 if (!props) { 671 props = PROPS_XY; 672 } 673 var x = p2[props[0]] - p1[props[0]], 674 y = p2[props[1]] - p1[props[1]]; 675 676 return Math.sqrt((x * x) + (y * y)); 677 } 678 679 /** 680 * calculate the angle between two coordinates 681 * @param {Object} p1 682 * @param {Object} p2 683 * @param {Array} [props] containing x and y keys 684 * @return {Number} angle 685 */ 686 function getAngle(p1, p2, props) { 687 if (!props) { 688 props = PROPS_XY; 689 } 690 var x = p2[props[0]] - p1[props[0]], 691 y = p2[props[1]] - p1[props[1]]; 692 return Math.atan2(y, x) * 180 / Math.PI; 693 } 694 695 /** 696 * calculate the rotation degrees between two pointersets 697 * @param {Array} start array of pointers 698 * @param {Array} end array of pointers 699 * @return {Number} rotation 700 */ 701 function getRotation(start, end) { 702 return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY); 703 } 704 705 /** 706 * calculate the scale factor between two pointersets
vendor: 19,643 bytes, lines 707-1416
707 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out 708 * @param {Array} start array of pointers 709 * @param {Array} end array of pointers 710 * @return {Number} scale 711 */ 712 function getScale(start, end) { 713 return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); 714 } 715 716 var MOUSE_INPUT_MAP = { 717 mousedown: INPUT_START, 718 mousemove: INPUT_MOVE, 719 mouseup: INPUT_END 720 }; 721 722 var MOUSE_ELEMENT_EVENTS = 'mousedown'; 723 var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; 724 725 /** 726 * Mouse events input 727 * @constructor 728 * @extends Input 729 */ 730 function MouseInput() { 731 this.evEl = MOUSE_ELEMENT_EVENTS; 732 this.evWin = MOUSE_WINDOW_EVENTS; 733 734 this.allow = true; // used by Input.TouchMouse to disable mouse events 735 this.pressed = false; // mousedown state 736 737 Input.apply(this, arguments); 738 } 739 740 inherit(MouseInput, Input, { 741 /** 742 * handle mouse events 743 * @param {Object} ev 744 */ 745 handler: function MEhandler(ev) { 746 var eventType = MOUSE_INPUT_MAP[ev.type]; 747 748 // on start we want to have the left mouse button down 749 if (eventType & INPUT_START && ev.button === 0) { 750 this.pressed = true; 751 } 752 753 if (eventType & INPUT_MOVE && ev.which !== 1) { 754 eventType = INPUT_END; 755 } 756 757 // mouse must be down, and mouse events are allowed (see the TouchMouse input) 758 if (!this.pressed || !this.allow) { 759 return; 760 } 761 762 if (eventType & INPUT_END) { 763 this.pressed = false; 764 } 765 766 this.callback(this.manager, eventType, { 767 pointers: [ev], 768 changedPointers: [ev], 769 pointerType: INPUT_TYPE_MOUSE, 770 srcEvent: ev 771 }); 772 } 773 }); 774 775 var POINTER_INPUT_MAP = { 776 pointerdown: INPUT_START, 777 pointermove: INPUT_MOVE, 778 pointerup: INPUT_END, 779 pointercancel: INPUT_CANCEL, 780 pointerout: INPUT_CANCEL 781 }; 782 783// in IE10 the pointer types is defined as an enum 784 var IE10_POINTER_TYPE_ENUM = { 785 2: INPUT_TYPE_TOUCH, 786 3: INPUT_TYPE_PEN, 787 4: INPUT_TYPE_MOUSE, 788 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 789 }; 790 791 var POINTER_ELEMENT_EVENTS = 'pointerdown'; 792 var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; 793 794// IE10 has prefixed support, and case-sensitive 795 if (window.MSPointerEvent) { 796 POINTER_ELEMENT_EVENTS = 'MSPointerDown'; 797 POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; 798 } 799 800 /** 801 * Pointer events input 802 * @constructor 803 * @extends Input 804 */ 805 function PointerEventInput() { 806 this.evEl = POINTER_ELEMENT_EVENTS; 807 this.evWin = POINTER_WINDOW_EVENTS; 808 809 Input.apply(this, arguments); 810 811 this.store = (this.manager.session.pointerEvents = []); 812 } 813 814 inherit(PointerEventInput, Input, { 815 /** 816 * handle mouse events 817 * @param {Object} ev 818 */ 819 handler: function PEhandler(ev) { 820 var store = this.store; 821 var removePointer = false; 822 823 var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); 824 var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; 825 var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; 826 827 var isTouch = (pointerType == INPUT_TYPE_TOUCH); 828 829 // get index of the event in the store 830 var storeIndex = inArray(store, ev.pointerId, 'pointerId'); 831 832 // start and mouse must be down 833 if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { 834 if (storeIndex < 0) { 835 store.push(ev); 836 storeIndex = store.length - 1; 837 } 838 } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 839 removePointer = true; 840 } 841 842 // it not found, so the pointer hasn't been down (so it's probably a hover) 843 if (storeIndex < 0) { 844 return; 845 } 846 847 // update the event in the store 848 store[storeIndex] = ev; 849 850 this.callback(this.manager, eventType, { 851 pointers: store, 852 changedPointers: [ev], 853 pointerType: pointerType, 854 srcEvent: ev 855 }); 856 857 if (removePointer) { 858 // remove from the store 859 store.splice(storeIndex, 1); 860 } 861 } 862 }); 863 864 var SINGLE_TOUCH_INPUT_MAP = { 865 touchstart: INPUT_START, 866 touchmove: INPUT_MOVE, 867 touchend: INPUT_END, 868 touchcancel: INPUT_CANCEL 869 }; 870 871 var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; 872 var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; 873 874 /** 875 * Touch events input 876 * @constructor 877 * @extends Input 878 */ 879 function SingleTouchInput() { 880 this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; 881 this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; 882 this.started = false; 883 884 Input.apply(this, arguments); 885 } 886 887 inherit(SingleTouchInput, Input, { 888 handler: function TEhandler(ev) { 889 var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; 890 891 // should we handle the touch events? 892 if (type === INPUT_START) { 893 this.started = true; 894 } 895 896 if (!this.started) { 897 return; 898 } 899 900 var touches = normalizeSingleTouches.call(this, ev, type); 901 902 // when done, reset the started state 903 if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { 904 this.started = false; 905 } 906 907 this.callback(this.manager, type, { 908 pointers: touches[0], 909 changedPointers: touches[1], 910 pointerType: INPUT_TYPE_TOUCH, 911 srcEvent: ev 912 }); 913 } 914 }); 915 916 /** 917 * @this {TouchInput} 918 * @param {Object} ev 919 * @param {Number} type flag 920 * @returns {undefined|Array} [all, changed] 921 */ 922 function normalizeSingleTouches(ev, type) { 923 var all = toArray(ev.touches); 924 var changed = toArray(ev.changedTouches); 925 926 if (type & (INPUT_END | INPUT_CANCEL)) { 927 all = uniqueArray(all.concat(changed), 'identifier', true); 928 } 929 930 return [all, changed]; 931 } 932 933 var TOUCH_INPUT_MAP = { 934 touchstart: INPUT_START, 935 touchmove: INPUT_MOVE, 936 touchend: INPUT_END, 937 touchcancel: INPUT_CANCEL 938 }; 939 940 var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; 941 942 /** 943 * Multi-user touch events input 944 * @constructor 945 * @extends Input 946 */ 947 function TouchInput() { 948 this.evTarget = TOUCH_TARGET_EVENTS; 949 this.targetIds = {}; 950 951 Input.apply(this, arguments); 952 } 953 954 inherit(TouchInput, Input, { 955 handler: function MTEhandler(ev) { 956 var type = TOUCH_INPUT_MAP[ev.type]; 957 var touches = getTouches.call(this, ev, type); 958 if (!touches) { 959 return; 960 } 961 962 this.callback(this.manager, type, { 963 pointers: touches[0], 964 changedPointers: touches[1], 965 pointerType: INPUT_TYPE_TOUCH, 966 srcEvent: ev 967 }); 968 } 969 }); 970 971 /** 972 * @this {TouchInput} 973 * @param {Object} ev 974 * @param {Number} type flag 975 * @returns {undefined|Array} [all, changed] 976 */ 977 function getTouches(ev, type) { 978 var allTouches = toArray(ev.touches); 979 var targetIds = this.targetIds; 980 981 // when there is only one touch, the process can be simplified 982 if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { 983 targetIds[allTouches[0].identifier] = true; 984 return [allTouches, allTouches]; 985 } 986 987 var i, 988 targetTouches, 989 changedTouches = toArray(ev.changedTouches), 990 changedTargetTouches = [], 991 target = this.target; 992 993 // get target touches from touches 994 targetTouches = allTouches.filter(function(touch) { 995 return hasParent(touch.target, target); 996 }); 997 998 // collect touches 999 if (type === INPUT_START) { 1000 i = 0; 1001 while (i < targetTouches.length) { 1002 targetIds[targetTouches[i].identifier] = true; 1003 i++; 1004 } 1005 } 1006 1007 // filter changed touches to only contain touches that exist in the collected target ids 1008 i = 0; 1009 while (i < changedTouches.length) { 1010 if (targetIds[changedTouches[i].identifier]) { 1011 changedTargetTouches.push(changedTouches[i]); 1012 } 1013 1014 // cleanup removed touches 1015 if (type & (INPUT_END | INPUT_CANCEL)) { 1016 delete targetIds[changedTouches[i].identifier]; 1017 } 1018 i++; 1019 } 1020 1021 if (!changedTargetTouches.length) { 1022 return; 1023 } 1024 1025 return [ 1026 // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' 1027 uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), 1028 changedTargetTouches 1029 ]; 1030 } 1031 1032 /** 1033 * Combined touch and mouse input 1034 * 1035 * Touch has a higher priority then mouse, and while touching no mouse events are allowed. 1036 * This because touch devices also emit mouse events while doing a touch. 1037 * 1038 * @constructor 1039 * @extends Input 1040 */ 1041 function TouchMouseInput() { 1042 Input.apply(this, arguments); 1043 1044 var handler = bindFn(this.handler, this); 1045 this.touch = new TouchInput(this.manager, handler); 1046 this.mouse = new MouseInput(this.manager, handler); 1047 } 1048 1049 inherit(TouchMouseInput, Input, { 1050 /** 1051 * handle mouse and touch events 1052 * @param {Hammer} manager 1053 * @param {String} inputEvent 1054 * @param {Object} inputData 1055 */ 1056 handler: function TMEhandler(manager, inputEvent, inputData) { 1057 var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), 1058 isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); 1059 1060 // when we're in a touch event, so block all upcoming mouse events 1061 // most mobile browser also emit mouseevents, right after touchstart 1062 if (isTouch) { 1063 this.mouse.allow = false; 1064 } else if (isMouse && !this.mouse.allow) { 1065 return; 1066 } 1067 1068 // reset the allowMouse when we're done 1069 if (inputEvent & (INPUT_END | INPUT_CANCEL)) { 1070 this.mouse.allow = true; 1071 } 1072 1073 this.callback(manager, inputEvent, inputData); 1074 }, 1075 1076 /** 1077 * remove the event listeners 1078 */ 1079 destroy: function destroy() { 1080 this.touch.destroy(); 1081 this.mouse.destroy(); 1082 } 1083 }); 1084 1085 var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); 1086 var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; 1087 1088// magical touchAction value 1089 var TOUCH_ACTION_COMPUTE = 'compute'; 1090 var TOUCH_ACTION_AUTO = 'auto'; 1091 var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented 1092 var TOUCH_ACTION_NONE = 'none'; 1093 var TOUCH_ACTION_PAN_X = 'pan-x'; 1094 var TOUCH_ACTION_PAN_Y = 'pan-y'; 1095 1096 /** 1097 * Touch Action 1098 * sets the touchAction property or uses the js alternative 1099 * @param {Manager} manager 1100 * @param {String} value 1101 * @constructor 1102 */ 1103 function TouchAction(manager, value) { 1104 this.manager = manager; 1105 this.set(value); 1106 } 1107 1108 TouchAction.prototype = { 1109 /** 1110 * set the touchAction value on the element or enable the polyfill 1111 * @param {String} value 1112 */ 1113 set: function(value) { 1114 // find out the touch-action by the event handlers 1115 if (value == TOUCH_ACTION_COMPUTE) { 1116 value = this.compute(); 1117 } 1118 1119 if (NATIVE_TOUCH_ACTION) { 1120 this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; 1121 } 1122 this.actions = value.toLowerCase().trim(); 1123 }, 1124 1125 /** 1126 * just re-set the touchAction value 1127 */ 1128 update: function() { 1129 this.set(this.manager.options.touchAction); 1130 }, 1131 1132 /** 1133 * compute the value for the touchAction property based on the recognizer's settings 1134 * @returns {String} value 1135 */ 1136 compute: function() { 1137 var actions = []; 1138 each(this.manager.recognizers, function(recognizer) { 1139 if (boolOrFn(recognizer.options.enable, [recognizer])) { 1140 actions = actions.concat(recognizer.getTouchAction()); 1141 } 1142 }); 1143 return cleanTouchActions(actions.join(' ')); 1144 }, 1145 1146 /** 1147 * this method is called on each input cycle and provides the preventing of the browser behavior 1148 * @param {Object} input 1149 */ 1150 preventDefaults: function(input) { 1151 // not needed with native support for the touchAction property 1152 if (NATIVE_TOUCH_ACTION) { 1153 return; 1154 } 1155 1156 var srcEvent = input.srcEvent; 1157 var direction = input.offsetDirection; 1158 1159 // if the touch action did prevented once this session 1160 if (this.manager.session.prevented) { 1161 srcEvent.preventDefault(); 1162 return; 1163 } 1164 1165 var actions = this.actions; 1166 var hasNone = inStr(actions, TOUCH_ACTION_NONE); 1167 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); 1168 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); 1169 1170 if (hasNone || 1171 (hasPanY && direction & DIRECTION_HORIZONTAL) || 1172 (hasPanX && direction & DIRECTION_VERTICAL)) { 1173 return this.preventSrc(srcEvent); 1174 } 1175 }, 1176 1177 /** 1178 * call preventDefault to prevent the browser's default behavior (scrolling in most cases) 1179 * @param {Object} srcEvent 1180 */ 1181 preventSrc: function(srcEvent) { 1182 this.manager.session.prevented = true; 1183 srcEvent.preventDefault(); 1184 } 1185 }; 1186 1187 /** 1188 * when the touchActions are collected they are not a valid value, so we need to clean things up. * 1189 * @param {String} actions 1190 * @returns {*} 1191 */ 1192 function cleanTouchActions(actions) { 1193 // none 1194 if (inStr(actions, TOUCH_ACTION_NONE)) { 1195 return TOUCH_ACTION_NONE; 1196 } 1197 1198 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); 1199 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); 1200 1201 // pan-x and pan-y can be combined 1202 if (hasPanX && hasPanY) { 1203 return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y; 1204 } 1205 1206 // pan-x OR pan-y 1207 if (hasPanX || hasPanY) { 1208 return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; 1209 } 1210 1211 // manipulation 1212 if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { 1213 return TOUCH_ACTION_MANIPULATION; 1214 } 1215 1216 return TOUCH_ACTION_AUTO; 1217 } 1218 1219 /** 1220 * Recognizer flow explained; * 1221 * All recognizers have the initial state of POSSIBLE when a input session starts. 1222 * The definition of a input session is from the first input until the last input, with all it's movement in it. * 1223 * Example session for mouse-input: mousedown -> mousemove -> mouseup 1224 * 1225 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed 1226 * which determines with state it should be. 1227 * 1228 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to 1229 * POSSIBLE to give it another change on the next cycle. 1230 * 1231 * Possible 1232 * | 1233 * +-----+---------------+ 1234 * | | 1235 * +-----+-----+ | 1236 * | | | 1237 * Failed Cancelled | 1238 * +-------+------+ 1239 * | | 1240 * Recognized Began 1241 * | 1242 * Changed 1243 * | 1244 * Ended/Recognized 1245 */ 1246 var STATE_POSSIBLE = 1; 1247 var STATE_BEGAN = 2; 1248 var STATE_CHANGED = 4; 1249 var STATE_ENDED = 8; 1250 var STATE_RECOGNIZED = STATE_ENDED; 1251 var STATE_CANCELLED = 16; 1252 var STATE_FAILED = 32; 1253 1254 /** 1255 * Recognizer 1256 * Every recognizer needs to extend from this class. 1257 * @constructor 1258 * @param {Object} options 1259 */ 1260 function Recognizer(options) { 1261 this.id = uniqueId(); 1262 1263 this.manager = null; 1264 this.options = merge(options || {}, this.defaults); 1265 1266 // default is enable true 1267 this.options.enable = ifUndefined(this.options.enable, true); 1268 1269 this.state = STATE_POSSIBLE; 1270 1271 this.simultaneous = {}; 1272 this.requireFail = []; 1273 } 1274 1275 Recognizer.prototype = { 1276 /** 1277 * @virtual 1278 * @type {Object} 1279 */ 1280 defaults: {}, 1281 1282 /** 1283 * set options 1284 * @param {Object} options 1285 * @return {Recognizer} 1286 */ 1287 set: function(options) { 1288 extend(this.options, options); 1289 1290 // also update the touchAction, in case something changed about the directions/enabled state 1291 this.manager && this.manager.touchAction.update(); 1292 return this; 1293 }, 1294 1295 /** 1296 * recognize simultaneous with an other recognizer. 1297 * @param {Recognizer} otherRecognizer 1298 * @returns {Recognizer} this 1299 */ 1300 recognizeWith: function(otherRecognizer) { 1301 if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { 1302 return this; 1303 } 1304 1305 var simultaneous = this.simultaneous; 1306 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 1307 if (!simultaneous[otherRecognizer.id]) { 1308 simultaneous[otherRecognizer.id] = otherRecognizer; 1309 otherRecognizer.recognizeWith(this); 1310 } 1311 return this; 1312 }, 1313 1314 /** 1315 * drop the simultaneous link. it doesnt remove the link on the other recognizer. 1316 * @param {Recognizer} otherRecognizer 1317 * @returns {Recognizer} this 1318 */ 1319 dropRecognizeWith: function(otherRecognizer) { 1320 if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { 1321 return this; 1322 } 1323 1324 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 1325 delete this.simultaneous[otherRecognizer.id]; 1326 return this; 1327 }, 1328 1329 /** 1330 * recognizer can only run when an other is failing 1331 * @param {Recognizer} otherRecognizer 1332 * @returns {Recognizer} this 1333 */ 1334 requireFailure: function(otherRecognizer) { 1335 if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { 1336 return this; 1337 } 1338 1339 var requireFail = this.requireFail; 1340 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 1341 if (inArray(requireFail, otherRecognizer) === -1) { 1342 requireFail.push(otherRecognizer); 1343 otherRecognizer.requireFailure(this); 1344 } 1345 return this; 1346 }, 1347 1348 /** 1349 * drop the requireFailure link. it does not remove the link on the other recognizer. 1350 * @param {Recognizer} otherRecognizer 1351 * @returns {Recognizer} this 1352 */ 1353 dropRequireFailure: function(otherRecognizer) { 1354 if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { 1355 return this; 1356 } 1357 1358 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 1359 var index = inArray(this.requireFail, otherRecognizer); 1360 if (index > -1) { 1361 this.requireFail.splice(index, 1); 1362 } 1363 return this; 1364 }, 1365 1366 /** 1367 * has require failures boolean 1368 * @returns {boolean} 1369 */ 1370 hasRequireFailures: function() { 1371 return this.requireFail.length > 0; 1372 }, 1373 1374 /** 1375 * if the recognizer can recognize simultaneous with an other recognizer 1376 * @param {Recognizer} otherRecognizer 1377 * @returns {Boolean} 1378 */ 1379 canRecognizeWith: function(otherRecognizer) { 1380 return !!this.simultaneous[otherRecognizer.id]; 1381 }, 1382 1383 /** 1384 * You should use `tryEmit` instead of `emit` directly to check 1385 * that all the needed recognizers has failed before emitting. 1386 * @param {Object} input 1387 */ 1388 emit: function(input) { 1389 var self = this; 1390 var state = this.state; 1391 1392 function emit(withState) { 1393 self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input); 1394 } 1395 1396 // 'panstart' and 'panmove' 1397 if (state < STATE_ENDED) { 1398 emit(true); 1399 } 1400 1401 emit(); // simple 'eventName' events 1402 1403 // panend and pancancel 1404 if (state >= STATE_ENDED) { 1405 emit(true); 1406 } 1407 }, 1408 1409 /** 1410 * Check that all the require failure recognizers has failed, 1411 * if true, it emits a gesture event, 1412 * otherwise, setup the state to FAILED. 1413 * @param {Object} input 1414 */ 1415 tryEmit: function(input) { 1416 if (this.canEmit()) {
vendor: 6,981 bytes, lines 1417-1686
1417 return this.emit(input); 1418 } 1419 // it's failing anyway 1420 this.state = STATE_FAILED; 1421 }, 1422 1423 /** 1424 * can we emit? 1425 * @returns {boolean} 1426 */ 1427 canEmit: function() { 1428 var i = 0; 1429 while (i < this.requireFail.length) { 1430 if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { 1431 return false; 1432 } 1433 i++; 1434 } 1435 return true; 1436 }, 1437 1438 /** 1439 * update the recognizer 1440 * @param {Object} inputData 1441 */ 1442 recognize: function(inputData) { 1443 // make a new copy of the inputData 1444 // so we can change the inputData without messing up the other recognizers 1445 var inputDataClone = extend({}, inputData); 1446 1447 // is is enabled and allow recognizing? 1448 if (!boolOrFn(this.options.enable, [this, inputDataClone])) { 1449 this.reset(); 1450 this.state = STATE_FAILED; 1451 return; 1452 } 1453 1454 // reset when we've reached the end 1455 if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { 1456 this.state = STATE_POSSIBLE; 1457 } 1458 1459 this.state = this.process(inputDataClone); 1460 1461 // the recognizer has recognized a gesture 1462 // so trigger an event 1463 if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { 1464 this.tryEmit(inputDataClone); 1465 } 1466 }, 1467 1468 /** 1469 * return the state of the recognizer 1470 * the actual recognizing happens in this method 1471 * @virtual 1472 * @param {Object} inputData 1473 * @returns {Const} STATE 1474 */ 1475 process: function(inputData) { 1476 }, // jshint ignore:line 1477 1478 /** 1479 * return the preferred touch-action 1480 * @virtual 1481 * @returns {Array} 1482 */ 1483 getTouchAction: function() { 1484 }, 1485 1486 /** 1487 * called when the gesture isn't allowed to recognize 1488 * like when another is being recognized or it is disabled 1489 * @virtual 1490 */ 1491 reset: function() { 1492 } 1493 }; 1494 1495 /** 1496 * get a usable string, used as event postfix 1497 * @param {Const} state 1498 * @returns {String} state 1499 */ 1500 function stateStr(state) { 1501 if (state & STATE_CANCELLED) { 1502 return 'cancel'; 1503 } else if (state & STATE_ENDED) { 1504 return 'end'; 1505 } else if (state & STATE_CHANGED) { 1506 return 'move'; 1507 } else if (state & STATE_BEGAN) { 1508 return 'start'; 1509 } 1510 return ''; 1511 } 1512 1513 /** 1514 * direction cons to string 1515 * @param {Const} direction 1516 * @returns {String} 1517 */ 1518 function directionStr(direction) { 1519 if (direction == DIRECTION_DOWN) { 1520 return 'down'; 1521 } else if (direction == DIRECTION_UP) { 1522 return 'up'; 1523 } else if (direction == DIRECTION_LEFT) { 1524 return 'left'; 1525 } else if (direction == DIRECTION_RIGHT) { 1526 return 'right'; 1527 } 1528 return ''; 1529 } 1530 1531 /** 1532 * get a recognizer by name if it is bound to a manager 1533 * @param {Recognizer|String} otherRecognizer 1534 * @param {Recognizer} recognizer 1535 * @returns {Recognizer} 1536 */ 1537 function getRecognizerByNameIfManager(otherRecognizer, recognizer) { 1538 var manager = recognizer.manager; 1539 if (manager) { 1540 return manager.get(otherRecognizer); 1541 } 1542 return otherRecognizer; 1543 } 1544 1545 /** 1546 * This recognizer is just used as a base for the simple attribute recognizers. 1547 * @constructor 1548 * @extends Recognizer 1549 */ 1550 function AttrRecognizer() { 1551 Recognizer.apply(this, arguments); 1552 } 1553 1554 inherit(AttrRecognizer, Recognizer, { 1555 /** 1556 * @namespace 1557 * @memberof AttrRecognizer 1558 */ 1559 defaults: { 1560 /** 1561 * @type {Number} 1562 * @default 1 1563 */ 1564 pointers: 1 1565 }, 1566 1567 /** 1568 * Used to check if it the recognizer receives valid input, like input.distance > 10. 1569 * @memberof AttrRecognizer 1570 * @param {Object} input 1571 * @returns {Boolean} recognized 1572 */ 1573 attrTest: function(input) { 1574 var optionPointers = this.options.pointers; 1575 return optionPointers === 0 || input.pointers.length === optionPointers; 1576 }, 1577 1578 /** 1579 * Process the input and return the state for the recognizer 1580 * @memberof AttrRecognizer 1581 * @param {Object} input 1582 * @returns {*} State 1583 */ 1584 process: function(input) { 1585 var state = this.state; 1586 var eventType = input.eventType; 1587 1588 var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); 1589 var isValid = this.attrTest(input); 1590 1591 // on cancel input and we've recognized before, return STATE_CANCELLED 1592 if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { 1593 return state | STATE_CANCELLED; 1594 } else if (isRecognized || isValid) { 1595 if (eventType & INPUT_END) { 1596 return state | STATE_ENDED; 1597 } else if (!(state & STATE_BEGAN)) { 1598 return STATE_BEGAN; 1599 } 1600 return state | STATE_CHANGED; 1601 } 1602 return STATE_FAILED; 1603 } 1604 }); 1605 1606 /** 1607 * Pan 1608 * Recognized when the pointer is down and moved in the allowed direction. 1609 * @constructor 1610 * @extends AttrRecognizer 1611 */ 1612 function PanRecognizer() { 1613 AttrRecognizer.apply(this, arguments); 1614 1615 this.pX = null; 1616 this.pY = null; 1617 } 1618 1619 inherit(PanRecognizer, AttrRecognizer, { 1620 /** 1621 * @namespace 1622 * @memberof PanRecognizer 1623 */ 1624 defaults: { 1625 event: 'pan', 1626 threshold: 10, 1627 pointers: 1, 1628 direction: DIRECTION_ALL 1629 }, 1630 1631 getTouchAction: function() { 1632 var direction = this.options.direction; 1633 var actions = []; 1634 if (direction & DIRECTION_HORIZONTAL) { 1635 actions.push(TOUCH_ACTION_PAN_Y); 1636 } 1637 if (direction & DIRECTION_VERTICAL) { 1638 actions.push(TOUCH_ACTION_PAN_X); 1639 } 1640 return actions; 1641 }, 1642 1643 directionTest: function(input) { 1644 var options = this.options; 1645 var hasMoved = true; 1646 var distance = input.distance; 1647 var direction = input.direction; 1648 var x = input.deltaX; 1649 var y = input.deltaY; 1650 1651 // lock to axis? 1652 if (!(direction & options.direction)) { 1653 if (options.direction & DIRECTION_HORIZONTAL) { 1654 direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; 1655 hasMoved = x != this.pX; 1656 distance = Math.abs(input.deltaX); 1657 } else { 1658 direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; 1659 hasMoved = y != this.pY; 1660 distance = Math.abs(input.deltaY); 1661 } 1662 } 1663 input.direction = direction; 1664 return hasMoved && distance > options.threshold && direction & options.direction; 1665 }, 1666 1667 attrTest: function(input) { 1668 return AttrRecognizer.prototype.attrTest.call(this, input) && 1669 (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); 1670 }, 1671 1672 emit: function(input) { 1673 this.pX = input.deltaX; 1674 this.pY = input.deltaY; 1675 1676 var direction = directionStr(input.direction); 1677 if (direction) { 1678 this.manager.emit(this.options.event + direction, input); 1679 } 1680 1681 this._super.emit.call(this, input); 1682 } 1683 }); 1684 1685 /** 1686 * Pinch
vendor: 2,856 bytes, lines 1687-1796
1687 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). 1688 * @constructor 1689 * @extends AttrRecognizer 1690 */ 1691 function PinchRecognizer() { 1692 AttrRecognizer.apply(this, arguments); 1693 } 1694 1695 inherit(PinchRecognizer, AttrRecognizer, { 1696 /** 1697 * @namespace 1698 * @memberof PinchRecognizer 1699 */ 1700 defaults: { 1701 event: 'pinch', 1702 threshold: 0, 1703 pointers: 2 1704 }, 1705 1706 getTouchAction: function() { 1707 return [TOUCH_ACTION_NONE]; 1708 }, 1709 1710 attrTest: function(input) { 1711 return this._super.attrTest.call(this, input) && 1712 (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); 1713 }, 1714 1715 emit: function(input) { 1716 this._super.emit.call(this, input); 1717 if (input.scale !== 1) { 1718 var inOut = input.scale < 1 ? 'in' : 'out'; 1719 this.manager.emit(this.options.event + inOut, input); 1720 } 1721 } 1722 }); 1723 1724 /** 1725 * Press 1726 * Recognized when the pointer is down for x ms without any movement. 1727 * @constructor 1728 * @extends Recognizer 1729 */ 1730 function PressRecognizer() { 1731 Recognizer.apply(this, arguments); 1732 1733 this._timer = null; 1734 this._input = null; 1735 } 1736 1737 inherit(PressRecognizer, Recognizer, { 1738 /** 1739 * @namespace 1740 * @memberof PressRecognizer 1741 */ 1742 defaults: { 1743 event: 'press', 1744 pointers: 1, 1745 time: 500, // minimal time of the pointer to be pressed 1746 threshold: 5 // a minimal movement is ok, but keep it low 1747 }, 1748 1749 getTouchAction: function() { 1750 return [TOUCH_ACTION_AUTO]; 1751 }, 1752 1753 process: function(input) { 1754 var options = this.options; 1755 var validPointers = input.pointers.length === options.pointers; 1756 var validMovement = input.distance < options.threshold; 1757 var validTime = input.deltaTime > options.time; 1758 1759 this._input = input; 1760 1761 // we only allow little movement 1762 // and we've reached an end event, so a tap is possible 1763 if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { 1764 this.reset(); 1765 } else if (input.eventType & INPUT_START) { 1766 this.reset(); 1767 this._timer = setTimeoutContext(function() { 1768 this.state = STATE_RECOGNIZED; 1769 this.tryEmit(); 1770 }, options.time, this); 1771 } else if (input.eventType & INPUT_END) { 1772 return STATE_RECOGNIZED; 1773 } 1774 return STATE_FAILED; 1775 }, 1776 1777 reset: function() { 1778 clearTimeout(this._timer); 1779 }, 1780 1781 emit: function(input) { 1782 if (this.state !== STATE_RECOGNIZED) { 1783 return; 1784 } 1785 1786 if (input && (input.eventType & INPUT_END)) { 1787 this.manager.emit(this.options.event + 'up', input); 1788 } else { 1789 this._input.timeStamp = now(); 1790 this.manager.emit(this.options.event, this._input); 1791 } 1792 } 1793 }); 1794 1795 /** 1796 * Rotate
vendor: 4,167 bytes, lines 1797-1943
1797 * Recognized when two or more pointer are moving in a circular motion. 1798 * @constructor 1799 * @extends AttrRecognizer 1800 */ 1801 function RotateRecognizer() { 1802 AttrRecognizer.apply(this, arguments); 1803 } 1804 1805 inherit(RotateRecognizer, AttrRecognizer, { 1806 /** 1807 * @namespace 1808 * @memberof RotateRecognizer 1809 */ 1810 defaults: { 1811 event: 'rotate', 1812 threshold: 0, 1813 pointers: 2 1814 }, 1815 1816 getTouchAction: function() { 1817 return [TOUCH_ACTION_NONE]; 1818 }, 1819 1820 attrTest: function(input) { 1821 return this._super.attrTest.call(this, input) && 1822 (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); 1823 } 1824 }); 1825 1826 /** 1827 * Swipe 1828 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. 1829 * @constructor 1830 * @extends AttrRecognizer 1831 */ 1832 function SwipeRecognizer() { 1833 AttrRecognizer.apply(this, arguments); 1834 } 1835 1836 inherit(SwipeRecognizer, AttrRecognizer, { 1837 /** 1838 * @namespace 1839 * @memberof SwipeRecognizer 1840 */ 1841 defaults: { 1842 event: 'swipe', 1843 threshold: 10, 1844 velocity: 0.65, 1845 direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, 1846 pointers: 1 1847 }, 1848 1849 getTouchAction: function() { 1850 return PanRecognizer.prototype.getTouchAction.call(this); 1851 }, 1852 1853 attrTest: function(input) { 1854 var direction = this.options.direction; 1855 var velocity; 1856 1857 if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { 1858 velocity = input.velocity; 1859 } else if (direction & DIRECTION_HORIZONTAL) { 1860 velocity = input.velocityX; 1861 } else if (direction & DIRECTION_VERTICAL) { 1862 velocity = input.velocityY; 1863 } 1864 1865 return this._super.attrTest.call(this, input) && 1866 direction & input.direction && 1867 input.distance > this.options.threshold && 1868 abs(velocity) > this.options.velocity && input.eventType & INPUT_END; 1869 }, 1870 1871 emit: function(input) { 1872 var direction = directionStr(input.direction); 1873 if (direction) { 1874 this.manager.emit(this.options.event + direction, input); 1875 } 1876 1877 this.manager.emit(this.options.event, input); 1878 } 1879 }); 1880 1881 /** 1882 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur 1883 * between the given interval and position. The delay option can be used to recognize multi-taps without firing 1884 * a single tap. 1885 * 1886 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of 1887 * multi-taps being recognized. 1888 * @constructor 1889 * @extends Recognizer 1890 */ 1891 function TapRecognizer() { 1892 Recognizer.apply(this, arguments); 1893 1894 // previous time and center, 1895 // used for tap counting 1896 this.pTime = false; 1897 this.pCenter = false; 1898 1899 this._timer = null; 1900 this._input = null; 1901 this.count = 0; 1902 } 1903 1904 inherit(TapRecognizer, Recognizer, { 1905 /** 1906 * @namespace 1907 * @memberof PinchRecognizer 1908 */ 1909 defaults: { 1910 event: 'tap', 1911 pointers: 1, 1912 taps: 1, 1913 interval: 300, // max time between the multi-tap taps 1914 time: 250, // max time of the pointer to be down (like finger on the screen) 1915 threshold: 2, // a minimal movement is ok, but keep it low 1916 posThreshold: 10 // a multi-tap can be a bit off the initial position 1917 }, 1918 1919 getTouchAction: function() { 1920 return [TOUCH_ACTION_MANIPULATION]; 1921 }, 1922 1923 process: function(input) { 1924 var options = this.options; 1925 1926 var validPointers = input.pointers.length === options.pointers; 1927 var validMovement = input.distance < options.threshold; 1928 var validTouchTime = input.deltaTime < options.time; 1929 1930 this.reset(); 1931 1932 if ((input.eventType & INPUT_START) && (this.count === 0)) { 1933 return this.failTimeout(); 1934 } 1935 1936 // we only allow little movement 1937 // and we've reached an end event, so a tap is possible 1938 if (validMovement && validTouchTime && validPointers) { 1939 if (input.eventType != INPUT_END) { 1940 return this.failTimeout(); 1941 } 1942 1943 var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true;
vendor: 9,625 bytes, lines 1944-2264
1944 var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; 1945 1946 this.pTime = input.timeStamp; 1947 this.pCenter = input.center; 1948 1949 if (!validMultiTap || !validInterval) { 1950 this.count = 1; 1951 } else { 1952 this.count += 1; 1953 } 1954 1955 this._input = input; 1956 1957 // if tap count matches we have recognized it, 1958 // else it has began recognizing... 1959 var tapCount = this.count % options.taps; 1960 if (tapCount === 0) { 1961 // no failing requirements, immediately trigger the tap event 1962 // or wait as long as the multitap interval to trigger 1963 if (!this.hasRequireFailures()) { 1964 return STATE_RECOGNIZED; 1965 } else { 1966 this._timer = setTimeoutContext(function() { 1967 this.state = STATE_RECOGNIZED; 1968 this.tryEmit(); 1969 }, options.interval, this); 1970 return STATE_BEGAN; 1971 } 1972 } 1973 } 1974 return STATE_FAILED; 1975 }, 1976 1977 failTimeout: function() { 1978 this._timer = setTimeoutContext(function() { 1979 this.state = STATE_FAILED; 1980 }, this.options.interval, this); 1981 return STATE_FAILED; 1982 }, 1983 1984 reset: function() { 1985 clearTimeout(this._timer); 1986 }, 1987 1988 emit: function() { 1989 if (this.state == STATE_RECOGNIZED) { 1990 this._input.tapCount = this.count; 1991 this.manager.emit(this.options.event, this._input); 1992 } 1993 } 1994 }); 1995 1996 /** 1997 * Simple way to create an manager with a default set of recognizers. 1998 * @param {HTMLElement} element 1999 * @param {Object} [options] 2000 * @constructor 2001 */ 2002 function Hammer(element, options) { 2003 options = options || {}; 2004 options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset); 2005 return new Manager(element, options); 2006 } 2007 2008 /** 2009 * @const {string} 2010 */ 2011 Hammer.VERSION = '2.0.4'; 2012 2013 /** 2014 * default settings 2015 * @namespace 2016 */ 2017 Hammer.defaults = { 2018 /** 2019 * set if DOM events are being triggered. 2020 * But this is slower and unused by simple implementations, so disabled by default. 2021 * @type {Boolean} 2022 * @default false 2023 */ 2024 domEvents: false, 2025 2026 /** 2027 * The value for the touchAction property/fallback. 2028 * When set to `compute` it will magically set the correct value based on the added recognizers. 2029 * @type {String} 2030 * @default compute 2031 */ 2032 touchAction: TOUCH_ACTION_COMPUTE, 2033 2034 /** 2035 * @type {Boolean} 2036 * @default true 2037 */ 2038 enable: true, 2039 2040 /** 2041 * EXPERIMENTAL FEATURE -- can be removed/changed 2042 * Change the parent input target element. 2043 * If Null, then it is being set the to main element. 2044 * @type {Null|EventTarget} 2045 * @default null 2046 */ 2047 inputTarget: null, 2048 2049 /** 2050 * force an input class 2051 * @type {Null|Function} 2052 * @default null 2053 */ 2054 inputClass: null, 2055 2056 /** 2057 * Default recognizer setup when calling `Hammer()` 2058 * When creating a new Manager these will be skipped. 2059 * @type {Array} 2060 */ 2061 preset: [ 2062 // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] 2063 [RotateRecognizer, {enable: false}], 2064 [PinchRecognizer, {enable: false}, ['rotate']], 2065 [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], 2066 [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], 2067 [TapRecognizer], 2068 [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], 2069 [PressRecognizer] 2070 ], 2071 2072 /** 2073 * Some CSS properties can be used to improve the working of Hammer. 2074 * Add them to this method and they will be set when creating a new Manager. 2075 * @namespace 2076 */ 2077 cssProps: { 2078 /** 2079 * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. 2080 * @type {String} 2081 * @default 'none' 2082 */ 2083 userSelect: 'none', 2084 2085 /** 2086 * Disable the Windows Phone grippers when pressing an element. 2087 * @type {String} 2088 * @default 'none' 2089 */ 2090 touchSelect: 'none', 2091 2092 /** 2093 * Disables the default callout shown when you touch and hold a touch target. 2094 * On iOS, when you touch and hold a touch target such as a link, Safari displays 2095 * a callout containing information about the link. This property allows you to disable that callout. 2096 * @type {String} 2097 * @default 'none' 2098 */ 2099 touchCallout: 'none', 2100 2101 /** 2102 * Specifies whether zooming is enabled. Used by IE10> 2103 * @type {String} 2104 * @default 'none' 2105 */ 2106 contentZooming: 'none', 2107 2108 /** 2109 * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. 2110 * @type {String} 2111 * @default 'none' 2112 */ 2113 userDrag: 'none', 2114 2115 /** 2116 * Overrides the highlight color shown when the user taps a link or a JavaScript 2117 * clickable element in iOS. This property obeys the alpha value, if specified. 2118 * @type {String} 2119 * @default 'rgba(0,0,0,0)' 2120 */ 2121 tapHighlightColor: 'rgba(0,0,0,0)' 2122 } 2123 }; 2124 2125 var STOP = 1; 2126 var FORCED_STOP = 2; 2127 2128 /** 2129 * Manager 2130 * @param {HTMLElement} element 2131 * @param {Object} [options] 2132 * @constructor 2133 */ 2134 function Manager(element, options) { 2135 options = options || {}; 2136 2137 this.options = merge(options, Hammer.defaults); 2138 this.options.inputTarget = this.options.inputTarget || element; 2139 2140 this.handlers = {}; 2141 this.session = {}; 2142 this.recognizers = []; 2143 2144 this.element = element; 2145 this.input = createInputInstance(this); 2146 this.touchAction = new TouchAction(this, this.options.touchAction); 2147 2148 toggleCssProps(this, true); 2149 2150 each(options.recognizers, function(item) { 2151 var recognizer = this.add(new (item[0])(item[1])); 2152 item[2] && recognizer.recognizeWith(item[2]); 2153 item[3] && recognizer.requireFailure(item[3]); 2154 }, this); 2155 } 2156 2157 Manager.prototype = { 2158 /** 2159 * set options 2160 * @param {Object} options 2161 * @returns {Manager} 2162 */ 2163 set: function(options) { 2164 extend(this.options, options); 2165 2166 // Options that need a little more setup 2167 if (options.touchAction) { 2168 this.touchAction.update(); 2169 } 2170 if (options.inputTarget) { 2171 // Clean up existing event listeners and reinitialize 2172 this.input.destroy(); 2173 this.input.target = options.inputTarget; 2174 this.input.init(); 2175 } 2176 return this; 2177 }, 2178 2179 /** 2180 * stop recognizing for this session. 2181 * This session will be discarded, when a new [input]start event is fired. 2182 * When forced, the recognizer cycle is stopped immediately. 2183 * @param {Boolean} [force] 2184 */ 2185 stop: function(force) { 2186 this.session.stopped = force ? FORCED_STOP : STOP; 2187 }, 2188 2189 /** 2190 * run the recognizers! 2191 * called by the inputHandler function on every movement of the pointers (touches) 2192 * it walks through all the recognizers and tries to detect the gesture that is being made 2193 * @param {Object} inputData 2194 */ 2195 recognize: function(inputData) { 2196 var session = this.session; 2197 if (session.stopped) { 2198 return; 2199 } 2200 2201 // run the touch-action polyfill 2202 this.touchAction.preventDefaults(inputData); 2203 2204 var recognizer; 2205 var recognizers = this.recognizers; 2206 2207 // this holds the recognizer that is being recognized. 2208 // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED 2209 // if no recognizer is detecting a thing, it is set to `null` 2210 var curRecognizer = session.curRecognizer; 2211 2212 // reset when the last recognizer is recognized 2213 // or when we're in a new session 2214 if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { 2215 curRecognizer = session.curRecognizer = null; 2216 } 2217 2218 var i = 0; 2219 while (i < recognizers.length) { 2220 recognizer = recognizers[i]; 2221 2222 // find out if we are allowed try to recognize the input for this one. 2223 // 1. allow if the session is NOT forced stopped (see the .stop() method) 2224 // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one 2225 // that is being recognized. 2226 // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. 2227 // this can be setup with the `recognizeWith()` method on the recognizer. 2228 if (session.stopped !== FORCED_STOP && ( // 1 2229 !curRecognizer || recognizer == curRecognizer || // 2 2230 recognizer.canRecognizeWith(curRecognizer))) { // 3 2231 recognizer.recognize(inputData); 2232 } else { 2233 recognizer.reset(); 2234 } 2235 2236 // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the 2237 // current active recognizer. but only if we don't already have an active recognizer 2238 if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { 2239 curRecognizer = session.curRecognizer = recognizer; 2240 } 2241 i++; 2242 } 2243 }, 2244 2245 /** 2246 * get a recognizer by its event name. 2247 * @param {Recognizer|String} recognizer 2248 * @returns {Recognizer|Null} 2249 */ 2250 get: function(recognizer) { 2251 if (recognizer instanceof Recognizer) { 2252 return recognizer; 2253 } 2254 2255 var recognizers = this.recognizers; 2256 for (var i = 0; i < recognizers.length; i++) { 2257 if (recognizers[i].options.event == recognizer) { 2258 return recognizers[i]; 2259 } 2260 } 2261 return null; 2262 }, 2263 2264 /**
2265 * add a recognizer to the manager 2266 * existing recognizers with the same event name will be removed 2267 * @param {Recognizer} recognizer 2268 * @returns {Recognizer|Manager} 2269 */ 2270 add: function(recognizer) { 2271 if (invokeArrayArg(recognizer, 'add', this)) { 2272 return this; 2273 } 2274 2275 // remove existing 2276 var existing = this.get(recognizer.options.event); 2277 if (existing) { 2278 this.remove(existing); 2279 } 2280 2281 this.recognizers.push(recognizer); 2282 recognizer.manager = this; 2283 2284 this.touchAction.update(); 2285 return recognizer; 2286 }, 2287 2288 /** 2289 * remove a recognizer by name or instance 2290 * @param {Recognizer|String} recognizer 2291 * @returns {Manager} 2292 */ 2293 remove: function(recognizer) { 2294 if (invokeArrayArg(recognizer, 'remove', this)) { 2295 return this; 2296 } 2297 2298 var recognizers = this.recognizers; 2299 recognizer = this.get(recognizer); 2300 recognizers.splice(inArray(recognizers, recognizer), 1); 2301 2302 this.touchAction.update(); 2303 return this; 2304 }, 2305 2306 /** 2307 * bind event 2308 * @param {String} events 2309 * @param {Function} handler 2310 * @returns {EventEmitter} this 2311 */ 2312 on: function(events, handler) { 2313 var handlers = this.handlers; 2314 each(splitStr(events), function(event) { 2315 handlers[event] = handlers[event] || []; 2316 handlers[event].push(handler); 2317 }); 2318 return this; 2319 }, 2320 2321 /** 2322 * unbind event, leave emit blank to remove all handlers 2323 * @param {String} events 2324 * @param {Function} [handler] 2325 * @returns {EventEmitter} this 2326 */ 2327 off: function(events, handler) { 2328 var handlers = this.handlers; 2329 each(splitStr(events), function(event) { 2330 if (!handler) { 2331 delete handlers[event]; 2332 } else { 2333 handlers[event].splice(inArray(handlers[event], handler), 1); 2334 } 2335 }); 2336 return this; 2337 }, 2338 2339 /** 2340 * emit event to the listeners 2341 * @param {String} event 2342 * @param {Object} data 2343 */ 2344 emit: function(event, data) { 2345 // we also want to trigger dom events 2346 if (this.options.domEvents) { 2347 triggerDomEvent(event, data); 2348 } 2349 2350 // no handlers, so skip it all 2351 var handlers = this.handlers[event] && this.handlers[event].slice(); 2352 if (!handlers || !handlers.length) { 2353 return; 2354 } 2355 2356 data.type = event; 2357 data.preventDefault = function() { 2358 data.srcEvent.preventDefault(); 2359 }; 2360 2361 var i = 0; 2362 while (i < handlers.length) { 2363 handlers[i](data); 2364 i++; 2365 } 2366 }, 2367 2368 /** 2369 * destroy the manager and unbinds all events 2370 * it doesn't unbind dom events, that is the user own responsibility 2371 */ 2372 destroy: function() { 2373 this.element && toggleCssProps(this, false); 2374 2375 this.handlers = {}; 2376 this.session = {}; 2377 this.input.destroy(); 2378 this.element = null; 2379 } 2380 }; 2381 2382 /** 2383 * add/remove the css properties as defined in manager.options.cssProps 2384 * @param {Manager} manager 2385 * @param {Boolean} add 2386 */ 2387 function toggleCssProps(manager, add) { 2388 var element = manager.element; 2389 each(manager.options.cssProps, function(value, name) { 2390 element.style[prefixed(element.style, name)] = add ? value : ''; 2391 }); 2392 } 2393 2394 /** 2395 * trigger dom event 2396 * @param {String} event 2397 * @param {Object} data 2398 */ 2399 function triggerDomEvent(event, data) { 2400 var gestureEvent = document.createEvent('Event'); 2401 gestureEvent.initEvent(event, true, true); 2402 gestureEvent.gesture = data; 2403 data.target.dispatchEvent(gestureEvent); 2404 } 2405 2406 extend(Hammer, { 2407 INPUT_START: INPUT_START, 2408 INPUT_MOVE: INPUT_MOVE, 2409 INPUT_END: INPUT_END, 2410 INPUT_CANCEL: INPUT_CANCEL, 2411 2412 STATE_POSSIBLE: STATE_POSSIBLE, 2413 STATE_BEGAN: STATE_BEGAN, 2414 STATE_CHANGED: STATE_CHANGED, 2415 STATE_ENDED: STATE_ENDED, 2416 STATE_RECOGNIZED: STATE_RECOGNIZED, 2417 STATE_CANCELLED: STATE_CANCELLED, 2418 STATE_FAILED: STATE_FAILED, 2419 2420 DIRECTION_NONE: DIRECTION_NONE, 2421 DIRECTION_LEFT: DIRECTION_LEFT, 2422 DIRECTION_RIGHT: DIRECTION_RIGHT, 2423 DIRECTION_UP: DIRECTION_UP, 2424 DIRECTION_DOWN: DIRECTION_DOWN, 2425 DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, 2426 DIRECTION_VERTICAL: DIRECTION_VERTICAL, 2427 DIRECTION_ALL: DIRECTION_ALL, 2428 2429 Manager: Manager, 2430 Input: Input, 2431 TouchAction: TouchAction, 2432 2433 TouchInput: TouchInput, 2434 MouseInput: MouseInput, 2435 PointerEventInput: PointerEventInput, 2436 TouchMouseInput: TouchMouseInput, 2437 SingleTouchInput: SingleTouchInput, 2438 2439 Recognizer: Recognizer, 2440 AttrRecognizer: AttrRecognizer, 2441 Tap: TapRecognizer, 2442 Pan: PanRecognizer, 2443 Swipe: SwipeRecognizer, 2444 Pinch: PinchRecognizer, 2445 Rotate: RotateRecognizer, 2446 Press: PressRecognizer, 2447 2448 on: addEventListeners, 2449 off: removeEventListeners, 2450 each: each, 2451 merge: merge, 2452 extend: extend, 2453 inherit: inherit, 2454 bindFn: bindFn, 2455 prefixed: prefixed 2456 }); 2457 2458 if (typeof module != 'undefined' && module.exports) { 2459 module.exports = Hammer; 2460 } else { 2461 window[exportName] = Hammer; 2462 } 2463 2464})(window, document, 'Hammer'); 2465 2466// Generated by CoffeeScript 1.6.2 2467/*! 2468 jQuery Waypoints - v2.0.5 2469 Copyright (c) 2011-2014 Caleb Troughton 2470 Licensed under the MIT license. 2471 https://github.com/imakewebthings/jquery-waypoints/blob/master/licenses.txt 2472 */ 2473(function() { 2474 var t = [].indexOf || function(t) { 2475 for (var e = 0, n = this.length; e < n; e++) { 2476 if (e in this && this[e] === t) 2477 return e 2478 } 2479 return -1 2480 }, e = [].slice; 2481 (function(t, e) { 2482 return e(t.jQuery, t) 2483 })(window, function(n, r) { 2484 var i, o, l, s, f, u, c, a, h, d, p, y, v, w, g, m; 2485 i = n(r); 2486 a = t.call(r, "ontouchstart") >= 0; 2487 s = {horizontal: {}, vertical: {}}; 2488 f = 1; 2489 c = {}; 2490 u = "waypoints-context-id"; 2491 p = "resize.waypoints"; 2492 y = "scroll.waypoints"; 2493 v = 1; 2494 w = "waypoints-waypoint-ids";
vendor: 9,210 bytes, lines 2495-2790
2495 g = "waypoint"; 2496 m = "waypoints"; 2497 o = function() { 2498 function t(t) { 2499 var e = this; 2500 this.$element = t; 2501 this.element = t[0]; 2502 this.didResize = false; 2503 this.didScroll = false; 2504 this.id = "context" + f++; 2505 this.oldScroll = {x: t.scrollLeft(), y: t.scrollTop()}; 2506 this.waypoints = {horizontal: {}, vertical: {}}; 2507 this.element[u] = this.id; 2508 c[this.id] = this; 2509 t.bind(y, function() { 2510 var t; 2511 if (!(e.didScroll || a)) { 2512 e.didScroll = true; 2513 t = function() { 2514 e.doScroll(); 2515 return e.didScroll = false 2516 }; 2517 return r.setTimeout(t, n[m].settings.scrollThrottle) 2518 } 2519 }); 2520 t.bind(p, function() { 2521 var t; 2522 if (!e.didResize) { 2523 e.didResize = true; 2524 t = function() { 2525 n[m]("refresh"); 2526 return e.didResize = false 2527 }; 2528 return r.setTimeout(t, n[m].settings.resizeThrottle) 2529 } 2530 }) 2531 } 2532 2533 t.prototype.doScroll = function() { 2534 var t, e = this; 2535 t = { 2536 horizontal: { 2537 newScroll: this.$element.scrollLeft(), 2538 oldScroll: this.oldScroll.x, 2539 forward: "right", 2540 backward: "left" 2541 }, 2542 vertical: {newScroll: this.$element.scrollTop(), oldScroll: this.oldScroll.y, forward: "down", backward: "up"} 2543 }; 2544 if (a && (!t.vertical.oldScroll || !t.vertical.newScroll)) { 2545 n[m]("refresh") 2546 } 2547 n.each(t, function(t, r) { 2548 var i, o, l; 2549 l = []; 2550 o = r.newScroll > r.oldScroll; 2551 i = o ? r.forward : r.backward; 2552 n.each(e.waypoints[t], function(t, e) { 2553 var n, i; 2554 if (r.oldScroll < (n = e.offset) && n <= r.newScroll) { 2555 return l.push(e) 2556 } else if (r.newScroll < (i = e.offset) && i <= r.oldScroll) { 2557 return l.push(e) 2558 } 2559 }); 2560 l.sort(function(t, e) { 2561 return t.offset - e.offset 2562 }); 2563 if (!o) { 2564 l.reverse() 2565 } 2566 return n.each(l, function(t, e) { 2567 if (e.options.continuous || t === l.length - 1) { 2568 return e.trigger([i]) 2569 } 2570 }) 2571 }); 2572 return this.oldScroll = {x: t.horizontal.newScroll, y: t.vertical.newScroll} 2573 }; 2574 t.prototype.refresh = function() { 2575 var t, e, r, i = this; 2576 r = n.isWindow(this.element); 2577 e = this.$element.offset(); 2578 this.doScroll(); 2579 t = { 2580 horizontal: { 2581 contextOffset: r ? 0 : e.left, 2582 contextScroll: r ? 0 : this.oldScroll.x, 2583 contextDimension: this.$element.width(), 2584 oldScroll: this.oldScroll.x, 2585 forward: "right", 2586 backward: "left", 2587 offsetProp: "left" 2588 }, 2589 vertical: { 2590 contextOffset: r ? 0 : e.top, 2591 contextScroll: r ? 0 : this.oldScroll.y, 2592 contextDimension: r ? n[m]("viewportHeight") : this.$element.height(), 2593 oldScroll: this.oldScroll.y, 2594 forward: "down", 2595 backward: "up", 2596 offsetProp: "top" 2597 } 2598 }; 2599 return n.each(t, function(t, e) { 2600 return n.each(i.waypoints[t], function(t, r) { 2601 var i, o, l, s, f; 2602 i = r.options.offset; 2603 l = r.offset; 2604 o = n.isWindow(r.element) ? 0 : r.$element.offset()[e.offsetProp]; 2605 if (n.isFunction(i)) { 2606 i = i.apply(r.element) 2607 } else if (typeof i === "string") { 2608 i = parseFloat(i); 2609 if (r.options.offset.indexOf("%") > -1) { 2610 i = Math.ceil(e.contextDimension * i / 100) 2611 } 2612 } 2613 r.offset = o - e.contextOffset + e.contextScroll - i; 2614 if (r.options.onlyOnScroll && l != null || !r.enabled) { 2615 return 2616 } 2617 if (l !== null && l < (s = e.oldScroll) && s <= r.offset) { 2618 return r.trigger([e.backward]) 2619 } else if (l !== null && l > (f = e.oldScroll) && f >= r.offset) { 2620 return r.trigger([e.forward]) 2621 } else if (l === null && e.oldScroll >= r.offset) { 2622 return r.trigger([e.forward]) 2623 } 2624 }) 2625 }) 2626 }; 2627 t.prototype.checkEmpty = function() { 2628 if (n.isEmptyObject(this.waypoints.horizontal) && n.isEmptyObject(this.waypoints.vertical)) { 2629 this.$element.unbind([p, y].join(" ")); 2630 return delete c[this.id] 2631 } 2632 }; 2633 return t 2634 }(); 2635 l = function() { 2636 function t(t, e, r) { 2637 var i, o; 2638 if (r.offset === "bottom-in-view") { 2639 r.offset = function() { 2640 var t; 2641 t = n[m]("viewportHeight"); 2642 if (!n.isWindow(e.element)) { 2643 t = e.$element.height() 2644 } 2645 return t - n(this).outerHeight() 2646 } 2647 } 2648 this.$element = t; 2649 this.element = t[0]; 2650 this.axis = r.horizontal ? "horizontal" : "vertical"; 2651 this.callback = r.handler; 2652 this.context = e; 2653 this.enabled = r.enabled; 2654 this.id = "waypoints" + v++; 2655 this.offset = null; 2656 this.options = r; 2657 e.waypoints[this.axis][this.id] = this; 2658 s[this.axis][this.id] = this; 2659 i = (o = this.element[w]) != null ? o : []; 2660 i.push(this.id); 2661 this.element[w] = i 2662 } 2663 2664 t.prototype.trigger = function(t) { 2665 if (!this.enabled) { 2666 return 2667 } 2668 if (this.callback != null) { 2669 this.callback.apply(this.element, t) 2670 } 2671 if (this.options.triggerOnce) { 2672 return this.destroy() 2673 } 2674 }; 2675 t.prototype.disable = function() { 2676 return this.enabled = false 2677 }; 2678 t.prototype.enable = function() { 2679 this.context.refresh(); 2680 return this.enabled = true 2681 }; 2682 t.prototype.destroy = function() { 2683 delete s[this.axis][this.id]; 2684 delete this.context.waypoints[this.axis][this.id]; 2685 return this.context.checkEmpty() 2686 }; 2687 t.getWaypointsByElement = function(t) { 2688 var e, r; 2689 r = t[w]; 2690 if (!r) { 2691 return [] 2692 } 2693 e = n.extend({}, s.horizontal, s.vertical); 2694 return n.map(r, function(t) { 2695 return e[t] 2696 }) 2697 }; 2698 return t 2699 }(); 2700 d = { 2701 init: function(t, e) { 2702 var r; 2703 e = n.extend({}, n.fn[g].defaults, e); 2704 if ((r = e.handler) == null) { 2705 e.handler = t 2706 } 2707 this.each(function() { 2708 var t, r, i, s; 2709 t = n(this); 2710 i = (s = e.context) != null ? s : n.fn[g].defaults.context; 2711 if (!n.isWindow(i)) { 2712 i = t.closest(i) 2713 } 2714 i = n(i); 2715 r = c[i[0][u]]; 2716 if (!r) { 2717 r = new o(i) 2718 } 2719 return new l(t, r, e) 2720 }); 2721 n[m]("refresh"); 2722 return this 2723 }, disable: function() { 2724 return d._invoke.call(this, "disable") 2725 }, enable: function() { 2726 return d._invoke.call(this, "enable") 2727 }, destroy: function() { 2728 return d._invoke.call(this, "destroy") 2729 }, prev: function(t, e) { 2730 return d._traverse.call(this, t, e, function(t, e, n) { 2731 if (e > 0) { 2732 return t.push(n[e - 1]) 2733 } 2734 }) 2735 }, next: function(t, e) { 2736 return d._traverse.call(this, t, e, function(t, e, n) { 2737 if (e < n.length - 1) { 2738 return t.push(n[e + 1]) 2739 } 2740 }) 2741 }, _traverse: function(t, e, i) { 2742 var o, l; 2743 if (t == null) { 2744 t = "vertical" 2745 } 2746 if (e == null) { 2747 e = r 2748 } 2749 l = h.aggregate(e); 2750 o = []; 2751 this.each(function() { 2752 var e; 2753 e = n.inArray(this, l[t]); 2754 return i(o, e, l[t]) 2755 }); 2756 return this.pushStack(o) 2757 }, _invoke: function(t) { 2758 this.each(function() { 2759 var e; 2760 e = l.getWaypointsByElement(this); 2761 return n.each(e, function(e, n) { 2762 n[t](); 2763 return true 2764 }) 2765 }); 2766 return this 2767 } 2768 }; 2769 n.fn[g] = function() { 2770 var t, r; 2771 r = arguments[0], t = 2 <= arguments.length ? e.call(arguments, 1) : []; 2772 if (d[r]) { 2773 return d[r].apply(this, t) 2774 } else if (n.isFunction(r)) { 2775 return d.init.apply(this, arguments) 2776 } else if (n.isPlainObject(r)) { 2777 return d.init.apply(this, [null, r]) 2778 } else if (!r) { 2779 return n.error("jQuery Waypoints needs a callback function or handler option.") 2780 } else { 2781 return n.error("The " + r + " method does not exist in jQuery Waypoints.") 2782 } 2783 }; 2784 n.fn[g].defaults = {context: r, continuous: true, enabled: true, horizontal: false, offset: 0, triggerOnce: false}; 2785 h = { 2786 refresh: function() { 2787 return n.each(c, function(t, e) { 2788 return e.refresh() 2789 }) 2790 }
2790, viewportHeight: function() { 2791 var t; 2792 return (t = r.innerHeight) != null ? t : i.height() 2793 }, aggregate: function(t) { 2794 var e, r, i; 2795 e = s; 2796 if (t) { 2797 e = (i = c[n(t)[0][u]]) != null ? i.waypoints : void 0 2798 } 2799 if (!e) { 2800 return [] 2801 } 2802 r = {horizontal: [], vertical: []}; 2803 n.each(r, function(t, i) { 2804 n.each(e[t], function(t, e) { 2805 return i.push(e) 2806 }); 2807 i.sort(function(t, e) { 2808 return t.offset - e.offset 2809 }); 2810 r[t] = n.map(i, function(t) { 2811 return t.element 2812 }); 2813 return r[t] = n.unique(r[t]) 2814 }); 2815 return r 2816 }, above: function(t) { 2817 if (t == null) { 2818 t = r 2819 } 2820 return h._filter(t, "vertical", function(t, e) { 2821 return e.offset <= t.oldScroll.y 2822 }) 2823 }, below: function(t) { 2824 if (t == null) { 2825 t = r 2826 } 2827 return h._filter(t, "vertical", function(t, e) { 2828 return e.offset > t.oldScroll.y 2829 }) 2830 }, left: function(t) { 2831 if (t == null) { 2832 t = r 2833 } 2834 return h._filter(t, "horizontal", function(t, e) { 2835 return e.offset <= t.oldScroll.x 2836 }) 2837 }, right: function(t) { 2838 if (t == null) { 2839 t = r 2840 } 2841 return h._filter(t, "horizontal", function(t, e) { 2842 return e.offset > t.oldScroll.x 2843 }) 2844 }, enable: function() { 2845 return h._invoke("enable") 2846 }, disable: function() { 2847 return h._invoke("disable") 2848 }, destroy: function() { 2849 return h._invoke("destroy") 2850 }, extendFn: function(t, e) { 2851 return d[t] = e 2852 }, _invoke: function(t) { 2853 var e; 2854 e = n.extend({}, s.vertical, s.horizontal); 2855 return n.each(e, function(e, n) { 2856 n[t](); 2857 return true 2858 }) 2859 }, _filter: function(t, e, r) { 2860 var i, o; 2861 i = c[n(t)[0][u]]; 2862 if (!i) { 2863 return [] 2864 } 2865 o = []; 2866 n.each(i.waypoints[e], function(t, e) { 2867 if (r(i, e)) { 2868 return o.push(e) 2869 } 2870 }); 2871 o.sort(function(t, e) { 2872 return t.offset - e.offset 2873 }); 2874 return n.map(o, function(t) { 2875 return t.element 2876 }) 2877 } 2878 }; 2879 n[m] = function() { 2880 var t, n; 2881 n = arguments[0], t = 2 <= arguments.length ? e.call(arguments, 1) : []; 2882 if (h[n]) { 2883 return h[n].apply(null, t) 2884 } else { 2885 return h.aggregate.call(null, n) 2886 } 2887 }; 2888 n[m].settings = {resizeThrottle: 100, scrollThrottle: 30}; 2889 return i.on("load.waypoints", function() { 2890 return n[m]("refresh") 2891 }) 2892 }) 2893}).call(this); 2894 2895// Generated by CoffeeScript 1.6.2 2896/* 2897 Sticky Elements Shortcut for jQuery Waypoints - v2.0.5 2898 Copyright (c) 2011-2014 Caleb Troughton 2899 Licensed under the MIT license. 2900 https://github.com/imakewebthings/jquery-waypoints/blob/master/licenses.txt 2901 */ 2902(function() { 2903 (function(t, n) { 2904 return n(t.jQuery) 2905 })(window, function(t) { 2906 var n, i; 2907 n = {wrapper: '<div class="sticky-wrapper" />', stuckClass: "stuck", direction: "down right"}; 2908 i = function(t, n) { 2909 var i; 2910 t.wrap(n.wrapper); 2911 i = t.parent(); 2912 return i.data("isWaypointStickyWrapper", true) 2913 }; 2914 t.waypoints("extendFn", "sticky", function(r) { 2915 var e, a, s; 2916 a = t.extend({}, t.fn.waypoint.defaults, n, r); 2917 e = i(this, a); 2918 s = a.handler; 2919 a.handler = function(n) { 2920 var i, r; 2921 i = t(this).children(":first"); 2922 r = a.direction.indexOf(n) !== -1; 2923 i.toggleClass(a.stuckClass, r); 2924 e.height(r ? i.outerHeight() : ""); 2925 if (s != null) { 2926 return s.call(this, n) 2927 } 2928 }; 2929 e.waypoint(a); 2930 return this.data("stuckClass", a.stuckClass) 2931 }); 2932 return t.waypoints("extendFn", "unsticky", function() { 2933 var t; 2934 t = this.parent(); 2935 if (!t.data("isWaypointStickyWrapper")) { 2936 return this 2937 } 2938 t.waypoint("destroy"); 2939 this.unwrap(); 2940 return this.removeClass(this.data("stuckClass")) 2941 }) 2942 }) 2943}).call(this);
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.