/**
 * jQuery wipe plugin - Adds wipe gesture handling to a DOM node
 *
 * Author: Johannes Wüller
 * Created On: 20.01.2011
 *
 * This plugin should work for Apples iDevices, Google Android and other mobile
 * mobile browsers for touch devices. It has no side effects if touch events are
 * not supported by the target device.
 *
 * Options:
 *    threshold   minimum movement distance for wipe activation (in pixels, not
 *                radial, positive integer)
 *    top         handler function for top-wipes
 *    left        handler function for left-wipes
 *    right       handler function for right-wipes
 *    bottom      handler function for bottom-wipes
 *    global      handler function that is called for all wipe detections, gets
 *                passed ΔX and ΔY as arguments; can be used to implement your
 *                own direction detection mechanisms
 */

// closure for jQuery shorthand notation
(function ($) {
   var
      // jQuerified document
      doc = $(document);

   $.fn.wipe = function (options) {
      // Merge with default options.
      options = $.extend({
         threshold:      20,
         preventDefault: true,
         top:            null,
         left:           null,
         right:          null,
         bottom:         null,
         global:         null
      }, options || {});

      $(this).each(function () {
         var
            // jQuerified this
            element = $(this),

            // initial touch position on x-axis
            startX = null,

            // initial touch position on y-axis
            startY = null,

            // functions
            cancel,
            move;

         /**
          * Removes the touch handler and resets everything.
          */
         cancel = function (e) {
            // Reset everything.
            doc.unbind('touchmove.wipe', move);
            startX = startY = null;
         };

         /**
          * Handles touch movement.
          */
         move = function (e) {
            var touches = e.originalEvent.touches,
               deltaX,
               deltaY,
               isLeft,
               isRight,
               isTop,
               isBottom;

            if (options.preventDefault) {
               e.preventDefault();
            }

            if (startX !== null && startY !== null && touches) {
               deltaX = startX - touches[0].pageX;
               deltaY = startY - touches[0].pageY;
               isLeft = deltaX >= options.threshold;
               isRight = deltaX <= -options.threshold;
               isTop = deltaY >= options.threshold;
               isBottom = deltaY <= -options.threshold;

               if (isLeft || isRight || isTop || isBottom) {
                  cancel();

                  if (options.global !== null) {
                     options.global.call(this, deltaX, deltaY);
                  }

                  if (isLeft && options.left !== null) {
                     options.left.call(this);
                  } else if (isRight && options.right !== null) {
                     options.right.call(this);
                  }

                  if (isTop && options.top !== null) {
                     options.top.call(this);
                  } else if (isBottom && options.bottom !== null) {
                     options.bottom.call(this);
                  }
               }
            }
         };

         // Bind local events.
         element.bind('touchstart.wipe', function (e) {
            var touches = e.originalEvent.touches;

            if (options.preventDefault) {
               e.preventDefault();
            }

            if (touches && touches.length == 1) {
               startX = touches[0].pageX;
               startY = touches[0].pageY;
               doc.bind('touchmove.wipe', move);
            }
         });

         // Bind global handlers for cancel actions.
         doc.bind('touchend.wipe touchcancel.wipe', function (e) {
            if (options.preventDefault) {
               e.preventDefault();
            }

            cancel();
         });
      });

      return this; // chaining
   };
}(jQuery));
