Path: blob/master/sandbox/RFinance2014/libraries/frameworks/io2012/js/hammer.js
1436 views
/*1* Hammer.JS2* version 0.43* author: Eight Media4* https://github.com/EightMedia/hammer.js5*/6function Hammer(element, options, undefined)7{8var self = this;910var defaults = {11// prevent the default event or not... might be buggy when false12prevent_default : false,13css_hacks : true,1415drag : true,16drag_vertical : true,17drag_horizontal : true,18// minimum distance before the drag event starts19drag_min_distance : 20, // pixels2021// pinch zoom and rotation22transform : true,23scale_treshold : 0.1,24rotation_treshold : 15, // degrees2526tap : true,27tap_double : true,28tap_max_interval : 300,29tap_double_distance: 20,3031hold : true,32hold_timeout : 50033};34options = mergeObject(defaults, options);3536// some css hacks37(function() {38if(!options.css_hacks) {39return false;40}4142var vendors = ['webkit','moz','ms','o',''];43var css_props = {44"userSelect": "none",45"touchCallout": "none",46"userDrag": "none",47"tapHighlightColor": "rgba(0,0,0,0)"48};4950var prop = '';51for(var i = 0; i < vendors.length; i++) {52for(var p in css_props) {53prop = p;54if(vendors[i]) {55prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);56}57element.style[ prop ] = css_props[p];58}59}60})();6162// holds the distance that has been moved63var _distance = 0;6465// holds the exact angle that has been moved66var _angle = 0;6768// holds the diraction that has been moved69var _direction = 0;7071// holds position movement for sliding72var _pos = { };7374// how many fingers are on the screen75var _fingers = 0;7677var _first = false;7879var _gesture = null;80var _prev_gesture = null;8182var _touch_start_time = null;83var _prev_tap_pos = {x: 0, y: 0};84var _prev_tap_end_time = null;8586var _hold_timer = null;8788var _offset = {};8990// keep track of the mouse status91var _mousedown = false;9293var _event_start;94var _event_move;95var _event_end;969798/**99* angle to direction define100* @param float angle101* @return string direction102*/103this.getDirectionFromAngle = function( angle )104{105var directions = {106down: angle >= 45 && angle < 135, //90107left: angle >= 135 || angle <= -135, //180108up: angle < -45 && angle > -135, //270109right: angle >= -45 && angle <= 45 //0110};111112var direction, key;113for(key in directions){114if(directions[key]){115direction = key;116break;117}118}119return direction;120};121122123/**124* count the number of fingers in the event125* when no fingers are detected, one finger is returned (mouse pointer)126* @param event127* @return int fingers128*/129function countFingers( event )130{131// there is a bug on android (until v4?) that touches is always 1,132// so no multitouch is supported, e.g. no, zoom and rotation...133return event.touches ? event.touches.length : 1;134}135136137/**138* get the x and y positions from the event object139* @param event140* @return array [{ x: int, y: int }]141*/142function getXYfromEvent( event )143{144event = event || window.event;145146// no touches, use the event pageX and pageY147if(!event.touches) {148var doc = document,149body = doc.body;150151return [{152x: event.pageX || event.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && doc.clientLeft || 0 ),153y: event.pageY || event.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && doc.clientTop || 0 )154}];155}156// multitouch, return array with positions157else {158var pos = [], src;159for(var t=0, len=event.touches.length; t<len; t++) {160src = event.touches[t];161pos.push({ x: src.pageX, y: src.pageY });162}163return pos;164}165}166167168/**169* calculate the angle between two points170* @param object pos1 { x: int, y: int }171* @param object pos2 { x: int, y: int }172*/173function getAngle( pos1, pos2 )174{175return Math.atan2(pos2.y - pos1.y, pos2.x - pos1.x) * 180 / Math.PI;176}177178/**179* trigger an event/callback by name with params180* @param string name181* @param array params182*/183function triggerEvent( eventName, params )184{185// return touches object186params.touches = getXYfromEvent(params.originalEvent);187params.type = eventName;188189// trigger callback190if(isFunction(self["on"+ eventName])) {191self["on"+ eventName].call(self, params);192}193}194195196/**197* cancel event198* @param object event199* @return void200*/201202function cancelEvent(event){203event = event || window.event;204if(event.preventDefault){205event.preventDefault();206}else{207event.returnValue = false;208event.cancelBubble = true;209}210}211212213/**214* reset the internal vars to the start values215*/216function reset()217{218_pos = {};219_first = false;220_fingers = 0;221_distance = 0;222_angle = 0;223_gesture = null;224}225226227var gestures = {228// hold gesture229// fired on touchstart230hold : function(event)231{232// only when one finger is on the screen233if(options.hold) {234_gesture = 'hold';235clearTimeout(_hold_timer);236237_hold_timer = setTimeout(function() {238if(_gesture == 'hold') {239triggerEvent("hold", {240originalEvent : event,241position : _pos.start242});243}244}, options.hold_timeout);245}246},247248249// drag gesture250// fired on mousemove251drag : function(event)252{253// get the distance we moved254var _distance_x = _pos.move[0].x - _pos.start[0].x;255var _distance_y = _pos.move[0].y - _pos.start[0].y;256_distance = Math.sqrt(_distance_x * _distance_x + _distance_y * _distance_y);257258// drag259// minimal movement required260if(options.drag && (_distance > options.drag_min_distance) || _gesture == 'drag') {261// calculate the angle262_angle = getAngle(_pos.start[0], _pos.move[0]);263_direction = self.getDirectionFromAngle(_angle);264265// check the movement and stop if we go in the wrong direction266var is_vertical = (_direction == 'up' || _direction == 'down');267if(((is_vertical && !options.drag_vertical) || (!is_vertical && !options.drag_horizontal))268&& (_distance > options.drag_min_distance)) {269return;270}271272_gesture = 'drag';273274var position = { x: _pos.move[0].x - _offset.left,275y: _pos.move[0].y - _offset.top };276277var event_obj = {278originalEvent : event,279position : position,280direction : _direction,281distance : _distance,282distanceX : _distance_x,283distanceY : _distance_y,284angle : _angle285};286287// on the first time trigger the start event288if(_first) {289triggerEvent("dragstart", event_obj);290291_first = false;292}293294// normal slide event295triggerEvent("drag", event_obj);296297cancelEvent(event);298}299},300301302// transform gesture303// fired on touchmove304transform : function(event)305{306if(options.transform) {307var scale = event.scale || 1;308var rotation = event.rotation || 0;309310if(countFingers(event) != 2) {311return false;312}313314if(_gesture != 'drag' &&315(_gesture == 'transform' || Math.abs(1-scale) > options.scale_treshold316|| Math.abs(rotation) > options.rotation_treshold)) {317_gesture = 'transform';318319_pos.center = { x: ((_pos.move[0].x + _pos.move[1].x) / 2) - _offset.left,320y: ((_pos.move[0].y + _pos.move[1].y) / 2) - _offset.top };321322var event_obj = {323originalEvent : event,324position : _pos.center,325scale : scale,326rotation : rotation327};328329// on the first time trigger the start event330if(_first) {331triggerEvent("transformstart", event_obj);332_first = false;333}334335triggerEvent("transform", event_obj);336337cancelEvent(event);338339return true;340}341}342343return false;344},345346347// tap and double tap gesture348// fired on touchend349tap : function(event)350{351// compare the kind of gesture by time352var now = new Date().getTime();353var touch_time = now - _touch_start_time;354355// dont fire when hold is fired356if(options.hold && !(options.hold && options.hold_timeout > touch_time)) {357return;358}359360// when previous event was tap and the tap was max_interval ms ago361var is_double_tap = (function(){362if (_prev_tap_pos && options.tap_double && _prev_gesture == 'tap' && (_touch_start_time - _prev_tap_end_time) < options.tap_max_interval) {363var x_distance = Math.abs(_prev_tap_pos[0].x - _pos.start[0].x);364var y_distance = Math.abs(_prev_tap_pos[0].y - _pos.start[0].y);365return (_prev_tap_pos && _pos.start && Math.max(x_distance, y_distance) < options.tap_double_distance);366367}368return false;369})();370371if(is_double_tap) {372_gesture = 'double_tap';373_prev_tap_end_time = null;374375triggerEvent("doubletap", {376originalEvent : event,377position : _pos.start378});379cancelEvent(event);380}381382// single tap is single touch383else {384_gesture = 'tap';385_prev_tap_end_time = now;386_prev_tap_pos = _pos.start;387388if(options.tap) {389triggerEvent("tap", {390originalEvent : event,391position : _pos.start392});393cancelEvent(event);394}395}396397}398399};400401402function handleEvents(event)403{404switch(event.type)405{406case 'mousedown':407case 'touchstart':408_pos.start = getXYfromEvent(event);409_touch_start_time = new Date().getTime();410_fingers = countFingers(event);411_first = true;412_event_start = event;413414// borrowed from jquery offset https://github.com/jquery/jquery/blob/master/src/offset.js415var box = element.getBoundingClientRect();416var clientTop = element.clientTop || document.body.clientTop || 0;417var clientLeft = element.clientLeft || document.body.clientLeft || 0;418var scrollTop = window.pageYOffset || element.scrollTop || document.body.scrollTop;419var scrollLeft = window.pageXOffset || element.scrollLeft || document.body.scrollLeft;420421_offset = {422top: box.top + scrollTop - clientTop,423left: box.left + scrollLeft - clientLeft424};425426_mousedown = true;427428// hold gesture429gestures.hold(event);430431if(options.prevent_default) {432cancelEvent(event);433}434break;435436case 'mousemove':437case 'touchmove':438if(!_mousedown) {439return false;440}441_event_move = event;442_pos.move = getXYfromEvent(event);443444if(!gestures.transform(event)) {445gestures.drag(event);446}447break;448449case 'mouseup':450case 'mouseout':451case 'touchcancel':452case 'touchend':453if(!_mousedown || (_gesture != 'transform' && event.touches && event.touches.length > 0)) {454return false;455}456457_mousedown = false;458_event_end = event;459460// drag gesture461// dragstart is triggered, so dragend is possible462if(_gesture == 'drag') {463triggerEvent("dragend", {464originalEvent : event,465direction : _direction,466distance : _distance,467angle : _angle468});469}470471// transform472// transformstart is triggered, so transformed is possible473else if(_gesture == 'transform') {474triggerEvent("transformend", {475originalEvent : event,476position : _pos.center,477scale : event.scale,478rotation : event.rotation479});480}481else {482gestures.tap(_event_start);483}484485_prev_gesture = _gesture;486487// reset vars488reset();489break;490}491}492493494// bind events for touch devices495// except for windows phone 7.5, it doesnt support touch events..!496if('ontouchstart' in window) {497element.addEventListener("touchstart", handleEvents, false);498element.addEventListener("touchmove", handleEvents, false);499element.addEventListener("touchend", handleEvents, false);500element.addEventListener("touchcancel", handleEvents, false);501}502// for non-touch503else {504505if(element.addEventListener){ // prevent old IE errors506element.addEventListener("mouseout", function(event) {507if(!isInsideHammer(element, event.relatedTarget)) {508handleEvents(event);509}510}, false);511element.addEventListener("mouseup", handleEvents, false);512element.addEventListener("mousedown", handleEvents, false);513element.addEventListener("mousemove", handleEvents, false);514515// events for older IE516}else if(document.attachEvent){517element.attachEvent("onmouseout", function(event) {518if(!isInsideHammer(element, event.relatedTarget)) {519handleEvents(event);520}521}, false);522element.attachEvent("onmouseup", handleEvents);523element.attachEvent("onmousedown", handleEvents);524element.attachEvent("onmousemove", handleEvents);525}526}527528529/**530* find if element is (inside) given parent element531* @param object element532* @param object parent533* @return bool inside534*/535function isInsideHammer(parent, child) {536// get related target for IE537if(!child && window.event && window.event.toElement){538child = window.event.toElement;539}540541if(parent === child){542return true;543}544545// loop over parentNodes of child until we find hammer element546if(child){547var node = child.parentNode;548while(node !== null){549if(node === parent){550return true;551};552node = node.parentNode;553}554}555return false;556}557558559/**560* merge 2 objects into a new object561* @param object obj1562* @param object obj2563* @return object merged object564*/565function mergeObject(obj1, obj2) {566var output = {};567568if(!obj2) {569return obj1;570}571572for (var prop in obj1) {573if (prop in obj2) {574output[prop] = obj2[prop];575} else {576output[prop] = obj1[prop];577}578}579return output;580}581582function isFunction( obj ){583return Object.prototype.toString.call( obj ) == "[object Function]";584}585}586587