/*! * jQuery UI Touch Punch Improved 0.3.1 * * * Copyright 2013, Chris Hutchinson * Original jquery-ui-touch-punch Copyright 2011, Dave Furfero * Dual licensed under the MIT or GPL Version 2 licenses. * * Depends: * jquery.ui.widget.js * jquery.ui.mouse.js */ (function ($) { var pointerEnabled = window.navigator.pointerEnabled || window.navigator.msPointerEnabled; // Detect touch support $.support.touch = 'ontouchend' in document || pointerEnabled; // Ignore browsers without touch support or mouse support if (!$.support.touch || !$.ui.mouse) { return; } var mouseProto = $.ui.mouse.prototype, _mouseInit = mouseProto._mouseInit, touchHandled; // see http://stackoverflow.com/a/12714084/220825 function fixTouch(touch) { var winPageX = window.pageXOffset, winPageY = window.pageYOffset, x = touch.clientX, y = touch.clientY; if (touch.pageY === 0 && Math.floor(y) > Math.floor(touch.pageY) || touch.pageX === 0 && Math.floor(x) > Math.floor(touch.pageX)) { // iOS4 clientX/clientY have the value that should have been // in pageX/pageY. While pageX/page/ have the value 0 x = x - winPageX; y = y - winPageY; } else if (y < (touch.pageY - winPageY) || x < (touch.pageX - winPageX)) { // Some Android browsers have totally bogus values for clientX/Y // when scrolling/zooming a page. Detectable since clientX/clientY // should never be smaller than pageX/pageY minus page scroll x = touch.pageX - winPageX; y = touch.pageY - winPageY; } return { clientX: x, clientY: y }; } /** * Simulate a mouse event based on a corresponding touch event * @param {Object} event A touch event * @param {String} simulatedType The corresponding mouse event */ function simulateMouseEvent (event, simulatedType) { // Ignore multi-touch events if ((!pointerEnabled && event.originalEvent.touches.length > 1) || (pointerEnabled && !event.isPrimary)) { return; } var touch = pointerEnabled ? event.originalEvent : event.originalEvent.changedTouches[0], simulatedEvent = document.createEvent('MouseEvents'), coord = fixTouch(touch); // Check if element is an input or a textarea if ($(touch.target).is("input") || $(touch.target).is("textarea")) { event.stopPropagation(); } else { event.preventDefault(); } // Initialize the simulated mouse event using the touch event's coordinates simulatedEvent.initMouseEvent( simulatedType, // type true, // bubbles true, // cancelable window, // view 1, // detail event.screenX || touch.screenX, // screenX event.screenY || touch.screenY, // screenY event.clientX || coord.clientX, // clientX event.clientY || coord.clientY, // clientY false, // ctrlKey false, // altKey false, // shiftKey false, // metaKey 0, // button null // relatedTarget ); // Dispatch the simulated event to the target element event.target.dispatchEvent(simulatedEvent); } /** * Handle the jQuery UI widget's touchstart events * @param {Object} event The widget element's touchstart event */ mouseProto._touchStart = function (event) { var self = this; // Ignore the event if another widget is already being handled if (touchHandled || (!pointerEnabled && !self._mouseCapture(event.originalEvent.changedTouches[0]))) { return; } // Set the flag to prevent other widgets from inheriting the touch event touchHandled = true; // Track movement to determine if interaction was a click self._touchMoved = false; // Simulate the mouseover event simulateMouseEvent(event, 'mouseover'); // Simulate the mousemove event simulateMouseEvent(event, 'mousemove'); // Simulate the mousedown event simulateMouseEvent(event, 'mousedown'); }; /** * Handle the jQuery UI widget's touchmove events * @param {Object} event The document's touchmove event */ mouseProto._touchMove = function (event) { // Ignore event if not handled if (!touchHandled) { return; } // Interaction was not a click this._touchMoved = true; // Simulate the mousemove event simulateMouseEvent(event, 'mousemove'); }; /** * Handle the jQuery UI widget's touchend events * @param {Object} event The document's touchend event */ mouseProto._touchEnd = function (event) { // Ignore event if not handled if (!touchHandled) { return; } // Simulate the mouseup event simulateMouseEvent(event, 'mouseup'); // Simulate the mouseout event simulateMouseEvent(event, 'mouseout'); // If the touch interaction did not move, it should trigger a click if (!this._touchMoved) { // Simulate the click event simulateMouseEvent(event, 'click'); } // Unset the flag to allow other widgets to inherit the touch event touchHandled = false; }; /** * A duck punch of the $.ui.mouse _mouseInit method to support touch events. * This method extends the widget with bound touch event handlers that * translate touch events to mouse events and pass them to the widget's * original mouse event handling methods. */ mouseProto._mouseInit = function () { var self = this; self.element.on({ 'touchstart': $.proxy(self, '_touchStart'), 'touchmove': $.proxy(self, '_touchMove'), 'touchend': $.proxy(self, '_touchEnd'), 'pointerDown': $.proxy(self, '_touchStart'), 'pointerMove': $.proxy(self, '_touchMove'), 'pointerUp': $.proxy(self, '_touchEnd'), 'MSPointerDown': $.proxy(self, '_touchStart'), 'MSPointerMove': $.proxy(self, '_touchMove'), 'MSPointerUp': $.proxy(self, '_touchEnd') }); // Call the original $.ui.mouse init method _mouseInit.call(self); }; })(jQuery);