/*! Hammer.JS - v1.0.3 - 2013-03-02

* http://eightmedia.github.com/hammer.js
*
* Copyright (c) 2013 Jorik Tangelder <j.tangelder@gmail.com>;
* Licensed under the MIT license */

(function(window) {

'use strict';

/**

* Hammer
* use this to create instances
* @param   {HTMLElement}   element
* @param   {Object}        options
* @returns {Hammer.Instance}
* @constructor
*/

var Hammer = function(element, options) {

return new Hammer.Instance(element, options || {});

};

// default settings Hammer.defaults = {

// add styles and attributes to the element to prevent the browser from doing
// its native behavior. this doesnt prevent the scrolling, but cancels
// the contextmenu, tap highlighting etc
// set to false to disable this
stop_browser_behavior: {
    userSelect: 'none', // this also triggers onselectstart=false for IE
    touchCallout: 'none',
    touchAction: 'none',
    contentZooming: 'none',
    userDrag: 'none',
    tapHighlightColor: 'rgba(0,0,0,0)'
}

// more settings are defined per gesture at gestures.js

};

// detect touchevents Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled; Hammer.HAS_TOUCHEVENTS = (‘ontouchstart’ in window);

// eventtypes per touchevent (start, move, end) // are filled by Hammer.event.determineEventTypes on setup Hammer.EVENT_TYPES = {};

// direction defines Hammer.DIRECTION_DOWN = ‘down’; Hammer.DIRECTION_LEFT = ‘left’; Hammer.DIRECTION_UP = ‘up’; Hammer.DIRECTION_RIGHT = ‘right’;

// pointer type Hammer.POINTER_MOUSE = ‘mouse’; Hammer.POINTER_TOUCH = ‘touch’; Hammer.POINTER_PEN = ‘pen’;

// touch event defines Hammer.EVENT_START = ‘start’; Hammer.EVENT_MOVE = ‘move’; Hammer.EVENT_END = ‘end’;

// plugins namespace Hammer.plugins = {};

// if the window events are set… Hammer.READY = false;

/**

* setup events to detect gestures on the document
*/

function setup() {

if(Hammer.READY) {
    return;
}

// find what eventtypes we add listeners to
Hammer.event.determineEventTypes();

// Register all gestures inside Hammer.gestures
for(var name in Hammer.gestures) {
    if(Hammer.gestures.hasOwnProperty(name)) {
        Hammer.detection.register(Hammer.gestures[name]);
    }
}

// Add touch events on the document
Hammer.event.onTouch(document, Hammer.EVENT_MOVE, Hammer.detection.detect);
Hammer.event.onTouch(document, Hammer.EVENT_END, Hammer.detection.endDetect);

// Hammer is ready...!
Hammer.READY = true;

}

/**

* create new hammer instance
* all methods should return the instance itself, so it is chainable.
* @param   {HTMLElement}       element
* @param   {Object}            [options={}]
* @returns {Hammer.Instance}
* @constructor
*/

Hammer.Instance = function(element, options) {

var self = this;

// setup HammerJS window events and register all gestures
// this also sets up the default options
setup();

this.element = element;

// start/stop detection option
this.enabled = true;

// merge options
this.options = Hammer.utils.extend(
    Hammer.utils.extend({}, Hammer.defaults),
    options || {});

// add some css to the element to prevent the browser from doing its native behavoir
if(this.options.stop_browser_behavior) {
    Hammer.utils.stopDefaultBrowserBehavior(this.element, this.options.stop_browser_behavior);
}

// start detection on touchstart
Hammer.event.onTouch(element, Hammer.EVENT_START, function(ev) {
    if(self.enabled) {
        Hammer.detection.startDetect(self, ev);
    }
});

// return instance
return this;

};

Hammer.Instance.prototype = {

/**
 * bind events to the instance
 * @param   {String}      gesture
 * @param   {Function}    handler
 * @returns {Hammer.Instance}
 */
on: function onEvent(gesture, handler){
    var gestures = gesture.split(' ');
    for(var t=0; t<gestures.length; t++) {
        this.element.addEventListener(gestures[t], handler, false);
    }
    return this;
},

/**
 * unbind events to the instance
 * @param   {String}      gesture
 * @param   {Function}    handler
 * @returns {Hammer.Instance}
 */
off: function offEvent(gesture, handler){
    var gestures = gesture.split(' ');
    for(var t=0; t<gestures.length; t++) {
        this.element.removeEventListener(gestures[t], handler, false);
    }
    return this;
},

/**
 * trigger gesture event
 * @param   {String}      gesture
 * @param   {Object}      eventData
 * @returns {Hammer.Instance}
 */
trigger: function triggerEvent(gesture, eventData){
    // trigger DOM event
    var event = document.createEvent('Event');
event.initEvent(gesture, true, true);
event.gesture = eventData;
this.element.dispatchEvent(event);
    return this;
},

/**
 * enable of disable hammer.js detection
 * @param   {Boolean}   state
 * @returns {Hammer.Instance}
 */
enable: function enable(state) {
    this.enabled = state;
    return this;
}

};

/**

* this holds the last move event,
* used to fix empty touchend issue
* see the onTouch event for an explanation
* @type {Object}
*/

var last_move_event = null;

/**

* when the mouse is hold down, this is true
* @type {Boolean}
*/

var enable_detect = false;

/**

* when touch events have been fired, this is true
* @type {Boolean}
*/

var touch_triggered = false;

Hammer.event = {

/**
 * simple addEventListener
 * @param   {HTMLElement}   element
 * @param   {String}        type
 * @param   {Function}      handler
 */
bindDom: function(element, type, handler) {
    var types = type.split(' ');
    for(var t=0; t<types.length; t++) {
        element.addEventListener(types[t], handler, false);
    }
},

/**
 * touch events with mouse fallback
 * @param   {HTMLElement}   element
 * @param   {String}        eventType        like Hammer.EVENT_MOVE
 * @param   {Function}      handler
 */
onTouch: function onTouch(element, eventType, handler) {
var self = this;
    this.bindDom(element, Hammer.EVENT_TYPES[eventType], function(ev) {
        var sourceEventType = ev.type.toLowerCase();

        // onmouseup, but when touchend has been fired we do nothing.
        // this is for touchdevices which also fire a mouseup on touchend
        if(sourceEventType.match(/mouseup/) && touch_triggered) {
            touch_triggered = false;
            return;
        }

        // mousebutton must be down or a touch event
        if(sourceEventType.match(/touch/) ||   // touch events are always on screen
            (sourceEventType.match(/mouse/) && ev.which === 1) ||   // mousedown
            (Hammer.HAS_POINTEREVENTS && sourceEventType.match(/down/))  // pointerevents touch
        ){
            enable_detect = true;
        }

        // we are in a touch event, set the touch triggered bool to true,
        // this for the conflicts that may occur on ios and android
        if(sourceEventType.match(/touch|pointer/)) {
            touch_triggered = true;
        }

        // when touch has been triggered in this detection session
        // and we are now handling a mouse event, we stop that to prevent conflicts
        if(enable_detect && !(touch_triggered && sourceEventType.match(/mouse/))) {
            // update pointer
            if(Hammer.HAS_POINTEREVENTS && eventType != Hammer.EVENT_END) {
                Hammer.PointerEvent.updatePointer(eventType, ev);
            }

            // because touchend has no touches, and we often want to use these in our gestures,
            // we send the last move event as our eventData in touchend
            if(eventType === Hammer.EVENT_END && last_move_event !== null) {
                ev = last_move_event;
            }
            // store the last move event
            else {
                last_move_event = ev;
            }
            // trigger the handler
            handler.call(Hammer.detection, self.collectEventData(element, eventType, ev));

            // remove pointer after the handler is done
            if(Hammer.HAS_POINTEREVENTS && eventType == Hammer.EVENT_END) {
                Hammer.PointerEvent.updatePointer(eventType, ev);
            }
        }

        // on the end we reset everything
        if(sourceEventType.match(/up|cancel|end/)) {
            enable_detect = false;
            last_move_event = null;
            Hammer.PointerEvent.reset();
        }
    });
},

/**
 * we have different events for each device/browser
 * determine what we need and set them in the Hammer.EVENT_TYPES constant
 */
determineEventTypes: function determineEventTypes() {
    // determine the eventtype we want to set
    var types;
    if(Hammer.HAS_POINTEREVENTS) {
        types = Hammer.PointerEvent.getEvents();
    }
    // for non pointer events browsers
    else {
        types = [
            'touchstart mousedown',
            'touchmove mousemove',
            'touchend touchcancel mouseup'];
    }

    Hammer.EVENT_TYPES[Hammer.EVENT_START]  = types[0];
    Hammer.EVENT_TYPES[Hammer.EVENT_MOVE]   = types[1];
    Hammer.EVENT_TYPES[Hammer.EVENT_END]    = types[2];
},

/**
 * create touchlist depending on the event
 * @param   {Object}    ev
 * @param   {String}    eventType   used by the fakemultitouch plugin
 */
getTouchList: function getTouchList(ev/*, eventType*/) {
    // get the fake pointerEvent touchlist
    if(Hammer.HAS_POINTEREVENTS) {
        return Hammer.PointerEvent.getTouchList();
    }
    // get the touchlist
    else if(ev.touches) {
        return ev.touches;
    }
    // make fake touchlist from mouse position
    else {
        return [{
            identifier: 1,
            pageX: ev.pageX,
            pageY: ev.pageY,
            target: ev.target
        }];
    }
},

/**
 * collect event data for Hammer js
 * @param   {HTMLElement}   element
 * @param   {String}        eventType        like Hammer.EVENT_MOVE
 * @param   {Object}        eventData
 */
collectEventData: function collectEventData(element, eventType, ev) {
    var touches = this.getTouchList(ev, eventType);

    // find out pointerType
    var pointerType = Hammer.POINTER_TOUCH;
    if(ev.type.match(/mouse/) || Hammer.PointerEvent.matchType(Hammer.POINTER_MOUSE, ev)) {
        pointerType = Hammer.POINTER_MOUSE;
    }

    return {
        center      : Hammer.utils.getCenter(touches),
        timestamp   : ev.timestamp || new Date().getTime(), // for IE
        target      : ev.target,
        touches     : touches,
        eventType   : eventType,
        pointerType : pointerType,
        srcEvent    : ev,

        /**
         * prevent the browser default actions
         * mostly used to disable scrolling of the browser
         */
        preventDefault: function() {
            if(this.srcEvent.preventManipulation) {
                this.srcEvent.preventManipulation();
            }

            if(this.srcEvent.preventDefault) {
                this.srcEvent.preventDefault();
            }
        },

        /**
         * stop bubbling the event up to its parents
         */
        stopPropagation: function() {
            this.srcEvent.stopPropagation();
        },

        /**
         * immediately stop gesture detection
         * might be useful after a swipe was detected
         * @return {*}
         */
        stopDetect: function() {
            return Hammer.detection.stopDetect();
        }
    };
}

};

Hammer.PointerEvent = {

/**
 * holds all pointers
 * @type {Object}
 */
pointers: {},

/**
 * get a list of pointers
 * @returns {Array}     touchlist
 */
getTouchList: function() {
    var pointers = this.pointers;
    var touchlist = [];

    // we can use forEach since pointerEvents only is in IE10
    Object.keys(pointers).sort().forEach(function(id) {
        touchlist.push(pointers[id]);
    });
    return touchlist;
},

/**
 * update the position of a pointer
 * @param   {String}   type             Hammer.EVENT_END
 * @param   {Object}   pointerEvent
 */
updatePointer: function(type, pointerEvent) {
    if(type == Hammer.EVENT_END) {
        delete this.pointers[pointerEvent.pointerId];
    }
    else {
        pointerEvent.identifier = pointerEvent.pointerId;
        this.pointers[pointerEvent.pointerId] = pointerEvent;
    }
},

/**
 * check if ev matches pointertype
 * @param   {String}        pointerType     Hammer.POINTER_MOUSE
 * @param   {PointerEvent}  ev
 */
matchType: function(pointerType, ev) {
    if(!ev.pointerType) {
        return false;
    }

    var types = {};
    types[Hammer.POINTER_MOUSE] = (ev.pointerType == ev.MSPOINTER_TYPE_MOUSE || ev.pointerType == Hammer.POINTER_MOUSE);
    types[Hammer.POINTER_TOUCH] = (ev.pointerType == ev.MSPOINTER_TYPE_TOUCH || ev.pointerType == Hammer.POINTER_TOUCH);
    types[Hammer.POINTER_PEN] = (ev.pointerType == ev.MSPOINTER_TYPE_PEN || ev.pointerType == Hammer.POINTER_PEN);
    return types[pointerType];
},

/**
 * get events
 */
getEvents: function() {
    return [
        'pointerdown MSPointerDown',
        'pointermove MSPointerMove',
        'pointerup pointercancel MSPointerUp MSPointerCancel'
    ];
},

/**
 * reset the list
 */
reset: function() {
    this.pointers = {};
}

};

Hammer.utils = {

/**
 * extend method,
 * also used for cloning when dest is an empty object
 * @param   {Object}    dest
 * @param   {Object}    src
 * @returns {Object}    dest
 */
extend: function extend(dest, src) {
    for (var key in src) {
        dest[key] = src[key];
    }

    return dest;
},

/**
 * get the center of all the touches
 * @param   {Array}     touches
 * @returns {Object}    center
 */
getCenter: function getCenter(touches) {
    var valuesX = [], valuesY = [];

    for(var t= 0,len=touches.length; t<len; t++) {
        valuesX.push(touches[t].pageX);
        valuesY.push(touches[t].pageY);
    }

    return {
        pageX: ((Math.min.apply(Math, valuesX) + Math.max.apply(Math, valuesX)) / 2),
        pageY: ((Math.min.apply(Math, valuesY) + Math.max.apply(Math, valuesY)) / 2)
    };
},

/**
 * calculate the velocity between two points
 * @param   {Number}    delta_time
 * @param   {Number}    delta_x
 * @param   {Number}    delta_y
 * @returns {Object}    velocity
 */
getVelocity: function getSimpleDistance(delta_time, delta_x, delta_y) {
    return {
        x: Math.abs(delta_x / delta_time) || 0,
        y: Math.abs(delta_y / delta_time) || 0
    };
},

/**
 * calculate the angle between two coordinates
 * @param   {Touch}     touch1
 * @param   {Touch}     touch2
 * @returns {Number}    angle
 */
getAngle: function getAngle(touch1, touch2) {
    var y = touch2.pageY - touch1.pageY,
        x = touch2.pageX - touch1.pageX;
    return Math.atan2(y, x) * 180 / Math.PI;
},

/**
 * angle to direction define
 * @param   {Touch}     touch1
 * @param   {Touch}     touch2
 * @returns {String}    direction constant, like Hammer.DIRECTION_LEFT
 */
getDirection: function getDirection(touch1, touch2) {
    var x = Math.abs(touch1.pageX - touch2.pageX),
        y = Math.abs(touch1.pageY - touch2.pageY);

    if(x >= y) {
        return touch1.pageX - touch2.pageX > 0 ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
    }
    else {
        return touch1.pageY - touch2.pageY > 0 ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
    }
},

/**
 * calculate the distance between two touches
 * @param   {Touch}     touch1
 * @param   {Touch}     touch2
 * @returns {Number}    distance
 */
getDistance: function getDistance(touch1, touch2) {
    var x = touch2.pageX - touch1.pageX,
        y = touch2.pageY - touch1.pageY;
    return Math.sqrt((x*x) + (y*y));
},

/**
 * calculate the scale factor between two touchLists (fingers)
 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
 * @param   {Array}     start
 * @param   {Array}     end
 * @returns {Number}    scale
 */
getScale: function getScale(start, end) {
    // need two fingers...
    if(start.length >= 2 && end.length >= 2) {
        return this.getDistance(end[0], end[1]) /
            this.getDistance(start[0], start[1]);
    }
    return 1;
},

/**
 * calculate the rotation degrees between two touchLists (fingers)
 * @param   {Array}     start
 * @param   {Array}     end
 * @returns {Number}    rotation
 */
getRotation: function getRotation(start, end) {
    // need two fingers
    if(start.length >= 2 && end.length >= 2) {
        return this.getAngle(end[1], end[0]) -
            this.getAngle(start[1], start[0]);
    }
    return 0;
},

/**
 * boolean if the direction is vertical
 * @param    {String}    direction
 * @returns  {Boolean}   is_vertical
 */
isVertical: function isVertical(direction) {
    return (direction == Hammer.DIRECTION_UP || direction == Hammer.DIRECTION_DOWN);
},

/**
 * stop browser default behavior with css props
 * @param   {HtmlElement}   element
 * @param   {Object}        css_props
 */
stopDefaultBrowserBehavior: function stopDefaultBrowserBehavior(element, css_props) {
    var prop,
        vendors = ['webkit','khtml','moz','ms','o',''];

    if(!css_props || !element.style) {
        return;
    }

    // with css properties for modern browsers
    for(var i = 0; i < vendors.length; i++) {
        for(var p in css_props) {
            if(css_props.hasOwnProperty(p)) {
                prop = p;

                // vender prefix at the property
                if(vendors[i]) {
                    prop = vendors[i] + prop.substring(0, 1).toUpperCase() + prop.substring(1);
                }

                // set the style
                element.style[prop] = css_props[p];
            }
        }
    }

    // also the disable onselectstart
    if(css_props.userSelect == 'none') {
        element.onselectstart = function() {
            return false;
        };
    }
}

};

Hammer.detection = {

// contains all registred Hammer.gestures in the correct order
gestures: [],

// data of the current Hammer.gesture detection session
current: null,

// the previous Hammer.gesture session data
// is a full clone of the previous gesture.current object
previous: null,

// when this becomes true, no gestures are fired
stopped: false,

/**
 * start Hammer.gesture detection
 * @param   {Hammer.Instance}   inst
 * @param   {Object}            eventData
 */
startDetect: function startDetect(inst, eventData) {
    // already busy with a Hammer.gesture detection on an element
    if(this.current) {
        return;
    }

    this.stopped = false;

    this.current = {
        inst        : inst, // reference to HammerInstance we're working for
        startEvent  : Hammer.utils.extend({}, eventData), // start eventData for distances, timing etc
        lastEvent   : false, // last eventData
        name        : '' // current gesture we're in/detected, can be 'tap', 'hold' etc
    };

    this.detect(eventData);
},

/**
 * Hammer.gesture detection
 * @param   {Object}    eventData
 */
detect: function detect(eventData) {
    if(!this.current || this.stopped) {
        return;
    }

    // extend event data with calculations about scale, distance etc
    eventData = this.extendEventData(eventData);

    // instance options
    var inst_options = this.current.inst.options;

    // call Hammer.gesture handlers
    for(var g=0,len=this.gestures.length; g<len; g++) {
        var gesture = this.gestures[g];

        // only when the instance options have enabled this gesture
        if(!this.stopped && inst_options[gesture.name] !== false) {
            // if a handler returns false, we stop with the detection
            if(gesture.handler.call(gesture, eventData, this.current.inst) === false) {
                this.stopDetect();
                break;
            }
        }
    }

    // store as previous event event
    if(this.current) {
        this.current.lastEvent = eventData;
    }
},

/**
 * end Hammer.gesture detection
 * @param   {Object}    eventData
 */
endDetect: function endDetect(eventData) {
    this.detect(eventData);
    this.stopDetect();
},

/**
 * clear the Hammer.gesture vars
 * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected
 * to stop other Hammer.gestures from being fired
 */
stopDetect: function stopDetect() {
    // clone current data to the store as the previous gesture
    // used for the double tap gesture, since this is an other gesture detect session
    this.previous = Hammer.utils.extend({}, this.current);

    // reset the current
    this.current = null;

    // stopped!
    this.stopped = true;
},

/**
 * extend eventData for Hammer.gestures
 * @param   {Object}   ev
 * @returns {Object}   ev
 */
extendEventData: function extendEventData(ev) {
    var startEv = this.current.startEvent;

    // if the touches change, set the new touches over the startEvent touches
    // this because touchevents don't have all the touches on touchstart, or the
    // user must place his fingers at the EXACT same time on the screen, which is not realistic
    // but, sometimes it happens that both fingers are touching at the EXACT same time
    if(startEv && (ev.touches.length != startEv.touches.length || ev.touches === startEv.touches)) {
        // extend 1 level deep to get the touchlist with the touch objects
        startEv.touches = [];
        for(var i=0,len=ev.touches.length; i<len; i++) {
            startEv.touches.push(Hammer.utils.extend({}, ev.touches[i]));
        }
    }

    var delta_time = ev.timestamp - startEv.timestamp,
        delta_x = ev.center.pageX - startEv.center.pageX,
        delta_y = ev.center.pageY - startEv.center.pageY,
        velocity = Hammer.utils.getVelocity(delta_time, delta_x, delta_y);

    Hammer.utils.extend(ev, {
        deltaTime   : delta_time,

        deltaX      : delta_x,
        deltaY      : delta_y,

        velocityX   : velocity.x,
        velocityY   : velocity.y,

        distance    : Hammer.utils.getDistance(startEv.center, ev.center),
        angle       : Hammer.utils.getAngle(startEv.center, ev.center),
        direction   : Hammer.utils.getDirection(startEv.center, ev.center),

        scale       : Hammer.utils.getScale(startEv.touches, ev.touches),
        rotation    : Hammer.utils.getRotation(startEv.touches, ev.touches),

        startEvent  : startEv
    });

    return ev;
},

/**
 * register new gesture
 * @param   {Object}    gesture object, see gestures.js for documentation
 * @returns {Array}     gestures
 */
register: function register(gesture) {
    // add an enable gesture options if there is no given
    var options = gesture.defaults || {};
    if(typeof(options[gesture.name]) == 'undefined') {
        options[gesture.name] = true;
    }

    // extend Hammer default options with the Hammer.gesture options
    Hammer.utils.extend(Hammer.defaults, options);

    // set its index
    gesture.index = gesture.index || 1000;

    // add Hammer.gesture to the list
    this.gestures.push(gesture);

    // sort the list by index
    this.gestures.sort(function(a, b) {
        if (a.index < b.index) {
            return -1;
        }
        if (a.index > b.index) {
            return 1;
        }
        return 0;
    });

    return this.gestures;
}

};

Hammer.gestures = Hammer.gestures || {};

/**

* Custom gestures
* ==============================
*
* Gesture object
* --------------------
* The object structure of a gesture:
*
* { name: 'mygesture',
*   index: 1337,
*   defaults: {
*     mygesture_option: true
*   }
*   handler: function(type, ev, inst) {
*     // trigger gesture event
*     inst.trigger(this.name, ev);
*   }
* }

* @param   {String}    name
* this should be the name of the gesture, lowercase
* it is also being used to disable/enable the gesture per instance config.
*
* @param   {Number}    [index=1000]
* the index of the gesture, where it is going to be in the stack of gestures detection
* like when you build an gesture that depends on the drag gesture, it is a good
* idea to place it after the index of the drag gesture.
*
* @param   {Object}    [defaults={}]
* the default settings of the gesture. these are added to the instance settings,
* and can be overruled per instance. you can also add the name of the gesture,
* but this is also added by default (and set to true).
*
* @param   {Function}  handler
* this handles the gesture detection of your custom gesture and receives the
* following arguments:
*
*      @param  {Object}    eventData
*      event data containing the following properties:
*          timestamp   {Number}        time the event occurred
*          target      {HTMLElement}   target element
*          touches     {Array}         touches (fingers, pointers, mouse) on the screen
*          pointerType {String}        kind of pointer that was used. matches Hammer.POINTER_MOUSE|TOUCH
*          center      {Object}        center position of the touches. contains pageX and pageY
*          deltaTime   {Number}        the total time of the touches in the screen
*          deltaX      {Number}        the delta on x axis we haved moved
*          deltaY      {Number}        the delta on y axis we haved moved
*          velocityX   {Number}        the velocity on the x
*          velocityY   {Number}        the velocity on y
*          angle       {Number}        the angle we are moving
*          direction   {String}        the direction we are moving. matches Hammer.DIRECTION_UP|DOWN|LEFT|RIGHT
*          distance    {Number}        the distance we haved moved
*          scale       {Number}        scaling of the touches, needs 2 touches
*          rotation    {Number}        rotation of the touches, needs 2 touches *
*          eventType   {String}        matches Hammer.EVENT_START|MOVE|END
*          srcEvent    {Object}        the source event, like TouchStart or MouseDown *
*          startEvent  {Object}        contains the same properties as above,
*                                      but from the first touch. this is used to calculate
*                                      distances, deltaTime, scaling etc
*
*      @param  {Hammer.Instance}    inst
*      the instance we are doing the detection for. you can get the options from
*      the inst.options object and trigger the gesture event by calling inst.trigger
*
*
* Handle gestures
* --------------------
* inside the handler you can get/set Hammer.detection.current. This is the current
* detection session. It has the following properties
*      @param  {String}    name
*      contains the name of the gesture we have detected. it has not a real function,
*      only to check in other gestures if something is detected.
*      like in the drag gesture we set it to 'drag' and in the swipe gesture we can
*      check if the current gesture is 'drag' by accessing Hammer.detection.current.name
*
*      @readonly
*      @param  {Hammer.Instance}    inst
*      the instance we do the detection for
*
*      @readonly
*      @param  {Object}    startEvent
*      contains the properties of the first gesture detection in this session.
*      Used for calculations about timing, distance, etc.
*
*      @readonly
*      @param  {Object}    lastEvent
*      contains all the properties of the last gesture detect in this session.
*
* after the gesture detection session has been completed (user has released the screen)
* the Hammer.detection.current object is copied into Hammer.detection.previous,
* this is usefull for gestures like doubletap, where you need to know if the
* previous gesture was a tap
*
* options that have been set by the instance can be received by calling inst.options
*
* You can trigger a gesture event by calling inst.trigger("mygesture", event).
* The first param is the name of your gesture, the second the event argument
*
*
* Register gestures
* --------------------
* When an gesture is added to the Hammer.gestures object, it is auto registered
* at the setup of the first Hammer instance. You can also call Hammer.detection.register
* manually and pass your gesture object as a param
*
*/

/**

* Hold
* Touch stays at the same place for x time
* @events  hold
*/

Hammer.gestures.Hold = {

name: 'hold',
index: 10,
defaults: {
    hold_timeout: 500,
    hold_threshold: 1
},
timer: null,
handler: function holdGesture(ev, inst) {
    switch(ev.eventType) {
        case Hammer.EVENT_START:
            // clear any running timers
            clearTimeout(this.timer);

            // set the gesture so we can check in the timeout if it still is
            Hammer.detection.current.name = this.name;

            // set timer and if after the timeout it still is hold,
            // we trigger the hold event
            this.timer = setTimeout(function() {
                if(Hammer.detection.current.name == 'hold') {
                    inst.trigger('hold', ev);
                }
            }, inst.options.hold_timeout);
            break;

        // when you move or end we clear the timer
        case Hammer.EVENT_MOVE:
            if(ev.distance > inst.options.hold_threshold) {
                clearTimeout(this.timer);
            }
            break;

        case Hammer.EVENT_END:
            clearTimeout(this.timer);
            break;
    }
}

};

/**

* Tap/DoubleTap
* Quick touch at a place or double at the same place
* @events  tap, doubletap
*/

Hammer.gestures.Tap = {

name: 'tap',
index: 100,
defaults: {
    tap_max_touchtime  : 250,
    tap_max_distance   : 10,
    doubletap_distance : 20,
    doubletap_interval : 300
},
handler: function tapGesture(ev, inst) {
    if(ev.eventType == Hammer.EVENT_END) {
        // previous gesture, for the double tap since these are two different gesture detections
        var prev = Hammer.detection.previous;

        // when the touchtime is higher then the max touch time
        // or when the moving distance is too much
        if(ev.deltaTime > inst.options.tap_max_touchtime ||
            ev.distance > inst.options.tap_max_distance) {
            return;
        }

        // check if double tap
        if(prev && prev.name == 'tap' &&
            (ev.timestamp - prev.lastEvent.timestamp) < inst.options.doubletap_interval &&
            ev.distance < inst.options.doubletap_distance) {
            Hammer.detection.current.name = 'doubletap';
        }
        else {
            Hammer.detection.current.name = 'tap';
        }

        inst.trigger(Hammer.detection.current.name, ev);
    }
}

};

/**

* Swipe
* triggers swipe events when the end velocity is above the threshold
* @events  swipe, swipeleft, swiperight, swipeup, swipedown
*/

Hammer.gestures.Swipe = {

name: 'swipe',
index: 40,
defaults: {
    // set 0 for unlimited, but this can conflict with transform
    swipe_max_touches  : 1,
    swipe_velocity     : 0.7
},
handler: function swipeGesture(ev, inst) {
    if(ev.eventType == Hammer.EVENT_END) {
        // max touches
        if(inst.options.swipe_max_touches > 0 &&
            ev.touches.length > inst.options.swipe_max_touches) {
            return;
        }

        // when the distance we moved is too small we skip this gesture
        // or we can be already in dragging
        if(ev.velocityX > inst.options.swipe_velocity ||
            ev.velocityY > inst.options.swipe_velocity) {
            // trigger swipe events
            inst.trigger(this.name, ev);
            inst.trigger(this.name + ev.direction, ev);
        }
    }
}

};

/**

* Drag
* Move with x fingers (default 1) around on the page. Blocking the scrolling when
* moving left and right is a good practice. When all the drag events are blocking
* you disable scrolling on that area.
* @events  drag, drapleft, dragright, dragup, dragdown
*/

Hammer.gestures.Drag = {

name: 'drag',
index: 50,
defaults: {
    drag_min_distance : 10,
    // set 0 for unlimited, but this can conflict with transform
    drag_max_touches  : 1,
    // prevent default browser behavior when dragging occurs
    // be careful with it, it makes the element a blocking element
    // when you are using the drag gesture, it is a good practice to set this true
    drag_block_horizontal   : false,
    drag_block_vertical     : false,
    // drag_lock_to_axis keeps the drag gesture on the axis that it started on,
    // It disallows vertical directions if the initial direction was horizontal, and vice versa.
    drag_lock_to_axis       : false
},
triggered: false,
handler: function dragGesture(ev, inst) {
    // current gesture isnt drag, but dragged is true
    // this means an other gesture is busy. now call dragend
    if(Hammer.detection.current.name != this.name && this.triggered) {
        inst.trigger(this.name +'end', ev);
        this.triggered = false;
        return;
    }

    // max touches
    if(inst.options.drag_max_touches > 0 &&
        ev.touches.length > inst.options.drag_max_touches) {
        return;
    }

    switch(ev.eventType) {
        case Hammer.EVENT_START:
            this.triggered = false;
            break;

        case Hammer.EVENT_MOVE:
            // when the distance we moved is too small we skip this gesture
            // or we can be already in dragging
            if(ev.distance < inst.options.drag_min_distance &&
                Hammer.detection.current.name != this.name) {
                return;
            }

            // we are dragging!
            Hammer.detection.current.name = this.name;

            // lock drag to axis?
            var last_direction = Hammer.detection.current.lastEvent.direction;
            if(inst.options.drag_lock_to_axis && last_direction !== ev.direction) {
                // keep direction on the axis that the drag gesture started on
                if(Hammer.utils.isVertical(last_direction)) {
                    ev.direction = (ev.deltaY < 0) ? Hammer.DIRECTION_UP : Hammer.DIRECTION_DOWN;
                }
                else {
                    ev.direction = (ev.deltaX < 0) ? Hammer.DIRECTION_LEFT : Hammer.DIRECTION_RIGHT;
                }
            }

            // first time, trigger dragstart event
            if(!this.triggered) {
                inst.trigger(this.name +'start', ev);
                this.triggered = true;
            }

            // trigger normal event
            inst.trigger(this.name, ev);

            // direction event, like dragdown
            inst.trigger(this.name + ev.direction, ev);

            // block the browser events
            if( (inst.options.drag_block_vertical && Hammer.utils.isVertical(ev.direction)) ||
                (inst.options.drag_block_horizontal && !Hammer.utils.isVertical(ev.direction))) {
                ev.preventDefault();
            }
            break;

        case Hammer.EVENT_END:
            // trigger dragend
            if(this.triggered) {
                inst.trigger(this.name +'end', ev);
            }

            this.triggered = false;
            break;
    }
}

};

/**

* Transform
* User want to scale or rotate with 2 fingers
* @events  transform, pinch, pinchin, pinchout, rotate
*/

Hammer.gestures.Transform = {

name: 'transform',
index: 45,
defaults: {
    // factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
    transform_min_scale     : 0.01,
    // rotation in degrees
    transform_min_rotation  : 1,
    // prevent default browser behavior when two touches are on the screen
    // but it makes the element a blocking element
    // when you are using the transform gesture, it is a good practice to set this true
    transform_always_block  : false
},
triggered: false,
handler: function transformGesture(ev, inst) {
    // current gesture isnt drag, but dragged is true
    // this means an other gesture is busy. now call dragend
    if(Hammer.detection.current.name != this.name && this.triggered) {
        inst.trigger(this.name +'end', ev);
        this.triggered = false;
        return;
    }

    // atleast multitouch
    if(ev.touches.length < 2) {
        return;
    }

    // prevent default when two fingers are on the screen
    if(inst.options.transform_always_block) {
        ev.preventDefault();
    }

    switch(ev.eventType) {
        case Hammer.EVENT_START:
            this.triggered = false;
            break;

        case Hammer.EVENT_MOVE:
            var scale_threshold = Math.abs(1-ev.scale);
            var rotation_threshold = Math.abs(ev.rotation);

            // when the distance we moved is too small we skip this gesture
            // or we can be already in dragging
            if(scale_threshold < inst.options.transform_min_scale &&
                rotation_threshold < inst.options.transform_min_rotation) {
                return;
            }

            // we are transforming!
            Hammer.detection.current.name = this.name;

            // first time, trigger dragstart event
            if(!this.triggered) {
                inst.trigger(this.name +'start', ev);
                this.triggered = true;
            }

            inst.trigger(this.name, ev); // basic transform event

            // trigger rotate event
            if(rotation_threshold > inst.options.transform_min_rotation) {
                inst.trigger('rotate', ev);
            }

            // trigger pinch event
            if(scale_threshold > inst.options.transform_min_scale) {
                inst.trigger('pinch', ev);
                inst.trigger('pinch'+ ((ev.scale < 1) ? 'in' : 'out'), ev);
            }
            break;

        case Hammer.EVENT_END:
            // trigger dragend
            if(this.triggered) {
                inst.trigger(this.name +'end', ev);
            }

            this.triggered = false;
            break;
    }
}

};

/**

* Touch
* Called as first, tells the user has touched the screen
* @events  touch
*/

Hammer.gestures.Touch = {

name: 'touch',
index: -Infinity,
defaults: {
    // call preventDefault at touchstart, and makes the element blocking by
    // disabling the scrolling of the page, but it improves gestures like
    // transforming and dragging.
    // be careful with using this, it can be very annoying for users to be stuck
    // on the page
    prevent_default: false
},
handler: function touchGesture(ev, inst) {
    if(inst.options.prevent_default) {
        ev.preventDefault();
    }

    if(ev.eventType ==  Hammer.EVENT_START) {
        inst.trigger(this.name, ev);
    }
}

};

/**

* Release
* Called as last, tells the user has released the screen
* @events  release
*/

Hammer.gestures.Release = {

name: 'release',
index: Infinity,
handler: function releaseGesture(ev, inst) {
    if(ev.eventType ==  Hammer.EVENT_END) {
        inst.trigger(this.name, ev);
    }
}

};

// node export if(typeof module === ‘object’ && typeof module.exports === ‘object’){

module.exports = Hammer;

} // just window export else {

window.Hammer = Hammer;

// requireJS module definition
if(typeof window.define === 'function' && window.define.amd) {
    window.define('hammer', [], function() {
        return Hammer;
    });
}

} })(this);