Source: composer/swipe-composer.js

/**
 * @module montage/composer/swipe-composer
 * @requires montage
 * @requires montage/composer/composer
 */
var Montage = require("../core/core").Montage,
    Composer = require("./composer").Composer;

/**
 * @class SwipeComposer
 * @classdesc `Composer` for detecting swipe gestures.
 * @extends Composer
 */
exports.SwipeComposer = Composer.specialize( /** @lends SwipeComposer# */ {

    load: {
        value: function () {
            document.addEventListener("touchstart", this, true);
        }
    },

    unload: {
        value: function () {
            document.removeEventListener("touchstart", this, true);
        }
    },

    _startX: {
        enumerable: false,
        value: 0
    },

    _startY: {
        enumerable: false,
        value: 0
    },

    _deltaX: {
        enumerable: false,
        value: 0
    },

    _deltaY: {
        enumerable: false,
        value: 0
    },

    /**
     * The number of pixels a gesture must continue to be recognized as a
     * swipe.
     * @type {number}
     */
    _threshold: {
        enumerable: false,
        value: 50
    },

    /**
     * The maximum angle (in degrees) away from horizontal or vertical
     * for a gesture to be recognized as a swipe.
     * @type {number}
     */
    _thresholdSwipeAngle: {
        enumerable: false,
        value: 20
    },

    _startTimestamp: {
        enumerable: false,
        value: 0
    },

    captureTouchstart: {
        value: function (event) {
            this._reset();
            var touches = event.touches,
                touch = touches[0];
            this._startX = touch.clientX;
            this._startY = touch.clientY;
            this._startTimestamp = event.timeStamp;
            document.addEventListener("touchmove", this, true);
            document.addEventListener("touchend", this, true);
            document.addEventListener("touchcancel", this, true);
        }
    },

    _reset: {
        enumerable: false,
        value: function () {
            this._startX = 0;
            this._startY = 0;
            this._deltaX = 0;
            this._deltaY = 0;
            this._startSwipeAngle = null;
        }
    },

    _startSwipeAngle: {
        enumerable: false,
        value: null
    },

    captureTouchcancel: {
        value: function (event) {
            document.removeEventListener("touchmove", this, true);
            document.removeEventListener("touchend", this, true);
            document.removeEventListener("touchcancel", this, true);
        }
    },

    captureTouchmove: {
        value: function (event) {
            event.preventDefault();
            var touches = event.changedTouches[0], swipeEvent, direction;

            this._deltaX = touches.clientX - this._startX;
            this._deltaY = touches.clientY - this._startY;

            var dX = this._deltaX,
                dY = this._deltaY,
                threshold = this._threshold,
                swipeAngle = this._findSwipeAngle(dX, dY);

            if (
                this._startSwipeAngle !== null && 
                    Math.abs(this._startSwipeAngle - swipeAngle) > this._thresholdSwipeAngle
            ) {
                //Direction changed; Abort touch
                //this.captureTouchcancel();
                this._startSwipeAngle = null;
            }

            if (this._startSwipeAngle === null) {
                this._startSwipeAngle = swipeAngle;
                this._startX = touches.clientX;
                this._startY = touches.clientY;
                this._deltaX = 0;
                this._deltaY = 0;
            }

            if (dX > threshold && dY > threshold) {
                direction = "DIAGONAL";
            } else if (dX > threshold && dY < threshold) {
                if (this._deltaX > 0) {
                    direction = "RIGHT";
                } else {
                    direction = "LEFT";
                }
            } else if (dX < threshold && dY > threshold) {
                if (this._deltaY > 0) {
                    direction = "DOWN";
                } else {
                    direction = "UP";
                }
            }

            if (dX !== 0 || dY !== 0) {
                swipeEvent = document.createEvent("CustomEvent");
                swipeEvent.initCustomEvent("swipemove", true, false, null);
                swipeEvent.direction = direction;
                swipeEvent.angle = this._startSwipeAngle;
                swipeEvent.velocity = this._findVelocity((event.timeStamp - this._startTimestamp));
                swipeEvent.startX = this._startX;
                swipeEvent.startY = this._startY;
                swipeEvent.dX = this._deltaX;
                swipeEvent.dY = this._deltaY;

                this.dispatchEvent(swipeEvent);
            }
        }
    },

    _findSwipeAngle: {
        enumerable: false,
        value: function (dX, dY) {
            var swipeAngle = -1 * (Math.atan2(dY, dX) * 180 / 3.14);
            return swipeAngle.toFixed(2);
        }
    },

    captureTouchend: {
        value: function (event) {

            if (!event) {
                return;
            }

            var deltaX = Math.abs(this._deltaX),
                deltaY = Math.abs(this._deltaY),
                threshold = this._threshold,
                direction,
                swipeEvent;

            if (deltaX < threshold && deltaY < threshold) {
                this.captureTouchcancel();
                return;
            }

            document.removeEventListener("touchmove", this, true);

            if (deltaX > threshold && deltaY > threshold) {
                direction = "DIAGONAL";
            } else if (deltaX > threshold && deltaY < threshold) {
                if (this._deltaX > 0) {
                    direction = "RIGHT";
                } else {
                    direction = "LEFT";
                }
            } else if (deltaX < threshold && deltaY > threshold) {
                if (this._deltaY > 0) {
                    direction = "DOWN";
                } else {
                    direction = "UP";
                }
            }

            swipeEvent = document.createEvent("CustomEvent");
            swipeEvent.initCustomEvent("swipe", true, false, null);
            swipeEvent.direction = direction;
            swipeEvent.angle = this._startSwipeAngle;
            swipeEvent.velocity = this._findVelocity((event.timeStamp - this._startTimestamp));
            swipeEvent.startX = this._startX;
            swipeEvent.startY = this._startY;
            swipeEvent.dX = this._deltaX;
            swipeEvent.dY = this._deltaY;

            this.dispatchEvent(swipeEvent);
        }
    },

    _findVelocity: {
        enumerable: false,
        value: function (deltaTime) {
            return (Math.sqrt(/*xSquare*/(this._deltaX * this._deltaX) + /*ySquare*/(this._deltaY * this._deltaY)) / /*deltaTime*/(deltaTime));

        }
    }

});