/** * Copyright (c) 2011 Zauber S.A. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @author Guido Marucci Blas - guido@zaubersoftware.com * @description Adds a handler for a custom event 'taphold' that handles a * tap and hold on touch interfaces. */ (function($) { var TAP_AND_HOLD_TRIGGER_TIMER = 1000; var MAX_DISTANCE_ALLOWED_IN_TAP_AND_HOLD_EVENT = 40; var TOUCHSTART = "touchstart"; var TOUCHEND = "touchend"; var TOUCHMOVE = "touchmove"; // For debugging only // var TOUCHSTART = "mousedown"; // var TOUCHEND = "mouseup"; // var TOUCHMOVE = "mousemove"; var tapAndHoldTimer = null; function calculateEuclideanDistance(x1, y1, x2, y2) { var diffX = (x2 - x1); var diffY = (y2 - y1); return Math.sqrt((diffX * diffX) + (diffY * diffY)); }; function onTouchStart(event) { var e = event.originalEvent; // Only start detector if and only if one finger is over the widget if (!e.touches || (e.targetTouches.length === 1 && e.touches.length === 1)) { startTapAndHoldDetector.call(this, event) var element = $(this); element.bind(TOUCHMOVE, onTouchMove); element.bind(TOUCHEND, onTouchEnd); } else { stopTapAndHoldDetector.call(this); } }; function onTouchMove(event) { if (tapAndHoldTimer == null) { return; } var e = event.originalEvent; var x = (e.changedTouches) ? e.changedTouches[0].pageX: e.pageX; var y = (e.changedTouches) ? e.changedTouches[0].pageY: e.pageY; var tapAndHoldPoint = $(this).data("taphold.point"); var euclideanDistance = calculateEuclideanDistance(tapAndHoldPoint.x, tapAndHoldPoint.y, x, y); if (euclideanDistance > MAX_DISTANCE_ALLOWED_IN_TAP_AND_HOLD_EVENT) { stopTapAndHoldDetector.call(this); } }; function onTouchEnd(event) { stopTapAndHoldDetector.call(this); }; function onTapAndHold(event) { clear.call(this); $(this).data("taphold.handler").call(this, event); }; function clear() { tapAndHoldTimer = null; $(this).unbind(TOUCHMOVE, onTouchMove); $(this).unbind(TOUCHEND, onTouchEnd); }; function startTapAndHoldDetector(event) { if (tapAndHoldTimer != null) { return; } var self = this; tapAndHoldTimer = setTimeout(function(){ onTapAndHold.call(self, event) }, TAP_AND_HOLD_TRIGGER_TIMER); // Stores tap x & y var e = event.originalEvent; var tapAndHoldPoint = {}; tapAndHoldPoint.x = (e.changedTouches) ? e.changedTouches[0].pageX: e.pageX; tapAndHoldPoint.y = (e.changedTouches) ? e.changedTouches[0].pageY: e.pageY; $(this).data("taphold.point", tapAndHoldPoint); }; function stopTapAndHoldDetector() { clearTimeout(tapAndHoldTimer); clear.call(this); }; $.event.special["taphold"] = { setup: function() { }, add: function(handleObj) { $(this).data("taphold.handler", handleObj.handler); if (handleObj.data) { $(this).bind(TOUCHSTART, handleObj.data, onTouchStart); } else { $(this).bind(TOUCHSTART, onTouchStart); } }, remove: function(handleObj) { stopTapAndHoldDetector.call(this); if (handleObj.data) { $(this).unbind(TOUCHSTART, handleObj.data, onTouchStart); } else { $(this).unbind(TOUCHSTART, onTouchStart); } }, teardown: function() { } }; })(jQuery);