(function ($) {
    $.extend($.support, { touch: 'ontouchstart' in document });
    $.fn.enableTouch = function () {
        var lastTap = null,
            tapValid = false,
            tapTimeout = null,
            rightClickPending = false,
            rightClickTouch = null,
            holdTimeout = null,
            cancelMouseUp = false;

        if ($.support.touch) {
            this.each(function (i, e) {
                var element = $(e);
                element.bind('touchstart', touchHandler);
                element.bind('touchmove', touchHandler);
                element.bind('touchend', touchHandler);
                element.bind('touchcancel', touchHandler);
            });
        }

        return this;

        function simulateMouseEvent(touch, type, button, offsetX, offsetY) {
            if (!offsetX) {
                offsetX = 0;
            }
            if (!offsetY) {
                offsetY = 0;
            }
            var simulatedEvent = document.createEvent('MouseEvent');
            simulatedEvent.initMouseEvent(type, true, true, window, 1,
                touch.screenX + offsetX, touch.screenY + offsetY,
                touch.clientX + offsetX, touch.clientY + offsetY,
                false, false, false, false, button, null);
            touch.target.dispatchEvent(simulatedEvent);
        }

        function touchHandler(e) {
            var originalEvent = e.originalEvent,
                type = "",
                touch = originalEvent.changedTouches[0];

            if (originalEvent.touches.length > 1) {
                return;
            }

            switch (e.type) {
                case 'touchstart':
                    if ($(e.target).is('select')) {
                        return;
                    }
                    startTouch(touch);
                    e.preventDefault();
                    return false;
                case 'touchmove':
                    cancelHold();
                    type = 'mousemove';
                    e.preventDefault();
                    break;
                case 'touchend':
                    if (cancelMouseUp) {
                        cancelMouseUp = false;
                        e.preventDefault();
                        return false;
                    }
                    cancelHold();
                    type = 'mouseup';
                    break;
                default:
                    return;
            }

            simulateMouseEvent(touch, type, 0);

            if (type === 'mouseup' && tapValid && touch.target === lastTap) {
                simulateMouseEvent(touch, 'click', 0);
            }
        }

        function startTouch(touch) {
            simulateMouseEvent(touch, 'mouseover', 0);
            simulateMouseEvent(touch, 'mousedown', 0);

            if (!tapValid) {
                lastTap = touch.target;
                tapValid = true;
                tapTimeout = setTimeout(cancelTap, 600);
                startHold(touch);
            } else {
                clearTimeout(tapTimeout);

                if (touch.target === lastTap) {
                    lastTap = null;
                    tapValid = false;
                    simulateMouseEvent(touch, 'click', 0);
                    simulateMouseEvent(touch, 'dblclick', 0);
                } else {
                    lastTap = touch.target;
                    tapValid = true;
                    tapTimeout = setTimeout(cancelTap, 600);
                    startHold(event);
                }
            }
        }

        function startHold(touch) {
            if (rightClickPending) {
                return;
            }
            rightClickPending = true;
            rightClickTouch = touch;
            holdTimeout = setTimeout(rightClick, 800);
        }

        function cancelHold() {
            if (rightClickPending) {
                clearTimeout(holdTimeout);
                rightClickPending = false;
                rightClickTouch = null;
            }
        }

        function cancelTap() {
            tapValid = false;
        }

        function rightClick() {
            rightClickPending = false;
            simulateMouseEvent(rightClickTouch, 'mouseup', 0);
            simulateMouseEvent(rightClickTouch, 'mousedown', 2);
            simulateMouseEvent(rightClickTouch, 'contextmenu', 2, 50, 5);
            cancelMouseUp = true;
            rightClickTouch = null;
        }
    };
})(jQuery);