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