1/*!
2 * jQuery UI Touch Punch 0.2.3
3 *
4 * Copyright 2011–2014, Dave Furfero
5 * Dual licensed under the MIT or GPL Version 2 licenses.
6 *
7 * Depends:
8 *  jquery.ui.widget.js
9 *  jquery.ui.mouse.js
10 */
11(function ($) {
12
13  // Detect touch support
14  $.support.touch = 'ontouchend' in document;
15
16  // Ignore browsers without touch support
17  if (!$.support.touch) {
18    return;
19  }
20
21  var mouseProto = $.ui.mouse.prototype,
22      _mouseInit = mouseProto._mouseInit,
23      _mouseDestroy = mouseProto._mouseDestroy,
24      touchHandled;
25
26  /**
27   * Simulate a mouse event based on a corresponding touch event
28   * @param {Object} event A touch event
29   * @param {String} simulatedType The corresponding mouse event
30   */
31  function simulateMouseEvent (event, simulatedType) {
32
33    // Ignore multi-touch events
34    if (event.originalEvent.touches.length > 1) {
35      return;
36    }
37
38    event.preventDefault();
39
40    var touch = event.originalEvent.changedTouches[0],
41        simulatedEvent = document.createEvent('MouseEvents');
42
43    // Initialize the simulated mouse event using the touch event's coordinates
44    simulatedEvent.initMouseEvent(
45      simulatedType,    // type
46      true,             // bubbles
47      true,             // cancelable
48      window,           // view
49      1,                // detail
50      touch.screenX,    // screenX
51      touch.screenY,    // screenY
52      touch.clientX,    // clientX
53      touch.clientY,    // clientY
54      false,            // ctrlKey
55      false,            // altKey
56      false,            // shiftKey
57      false,            // metaKey
58      0,                // button
59      null              // relatedTarget
60    );
61
62    // Dispatch the simulated event to the target element
63    event.target.dispatchEvent(simulatedEvent);
64  }
65
66  /**
67   * Handle the jQuery UI widget's touchstart events
68   * @param {Object} event The widget element's touchstart event
69   */
70  mouseProto._touchStart = function (event) {
71
72    var self = this;
73
74    // Ignore the event if another widget is already being handled
75    if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
76      return;
77    }
78
79    // Set the flag to prevent other widgets from inheriting the touch event
80    touchHandled = true;
81
82    // Track movement to determine if interaction was a click
83    self._touchMoved = false;
84
85    // Simulate the mouseover event
86    simulateMouseEvent(event, 'mouseover');
87
88    // Simulate the mousemove event
89    simulateMouseEvent(event, 'mousemove');
90
91    // Simulate the mousedown event
92    simulateMouseEvent(event, 'mousedown');
93  };
94
95  /**
96   * Handle the jQuery UI widget's touchmove events
97   * @param {Object} event The document's touchmove event
98   */
99  mouseProto._touchMove = function (event) {
100
101    // Ignore event if not handled
102    if (!touchHandled) {
103      return;
104    }
105
106    // Interaction was not a click
107    this._touchMoved = true;
108
109    // Simulate the mousemove event
110    simulateMouseEvent(event, 'mousemove');
111  };
112
113  /**
114   * Handle the jQuery UI widget's touchend events
115   * @param {Object} event The document's touchend event
116   */
117  mouseProto._touchEnd = function (event) {
118
119    // Ignore event if not handled
120    if (!touchHandled) {
121      return;
122    }
123
124    // Simulate the mouseup event
125    simulateMouseEvent(event, 'mouseup');
126
127    // Simulate the mouseout event
128    simulateMouseEvent(event, 'mouseout');
129
130    // If the touch interaction did not move, it should trigger a click
131    if (!this._touchMoved) {
132
133      // Simulate the click event
134      simulateMouseEvent(event, 'click');
135    }
136
137    // Unset the flag to allow other widgets to inherit the touch event
138    touchHandled = false;
139  };
140
141  /**
142   * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
143   * This method extends the widget with bound touch event handlers that
144   * translate touch events to mouse events and pass them to the widget's
145   * original mouse event handling methods.
146   */
147  mouseProto._mouseInit = function () {
148
149    var self = this;
150
151    // Delegate the touch handlers to the widget's element
152    self.element.bind({
153      touchstart: $.proxy(self, '_touchStart'),
154      touchmove: $.proxy(self, '_touchMove'),
155      touchend: $.proxy(self, '_touchEnd')
156    });
157
158    // Call the original $.ui.mouse init method
159    _mouseInit.call(self);
160  };
161
162  /**
163   * Remove the touch event handlers
164   */
165  mouseProto._mouseDestroy = function () {
166
167    var self = this;
168
169    // Delegate the touch handlers to the widget's element
170    self.element.unbind({
171      touchstart: $.proxy(self, '_touchStart'),
172      touchmove: $.proxy(self, '_touchMove'),
173      touchend: $.proxy(self, '_touchEnd')
174    });
175
176    // Call the original $.ui.mouse destroy method
177    _mouseDestroy.call(self);
178  };
179
180})(jQuery);