Source: main/webapp/modules/Touch.js

  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. var Guacamole = Guacamole || {};
  20. /**
  21. * Provides cross-browser multi-touch events for a given element. The events of
  22. * the given element are automatically populated with handlers that translate
  23. * touch events into a non-browser-specific event provided by the
  24. * Guacamole.Touch instance.
  25. *
  26. * @constructor
  27. * @augments Guacamole.Event.Target
  28. * @param {!Element} element
  29. * The Element to use to provide touch events.
  30. */
  31. Guacamole.Touch = function Touch(element) {
  32. Guacamole.Event.Target.call(this);
  33. /**
  34. * Reference to this Guacamole.Touch.
  35. *
  36. * @private
  37. * @type {!Guacamole.Touch}
  38. */
  39. var guacTouch = this;
  40. /**
  41. * The default X/Y radius of each touch if the device or browser does not
  42. * expose the size of the contact area.
  43. *
  44. * @private
  45. * @constant
  46. * @type {!number}
  47. */
  48. var DEFAULT_CONTACT_RADIUS = Math.floor(16 * window.devicePixelRatio);
  49. /**
  50. * The set of all active touches, stored by their unique identifiers.
  51. *
  52. * @type {!Object.<Number, Guacamole.Touch.State>}
  53. */
  54. this.touches = {};
  55. /**
  56. * The number of active touches currently stored within
  57. * {@link Guacamole.Touch#touches touches}.
  58. */
  59. this.activeTouches = 0;
  60. /**
  61. * Fired whenever a new touch contact is initiated on the element
  62. * associated with this Guacamole.Touch.
  63. *
  64. * @event Guacamole.Touch#touchstart
  65. * @param {!Guacamole.Touch.Event} event
  66. * A {@link Guacamole.Touch.Event} object representing the "touchstart"
  67. * event.
  68. */
  69. /**
  70. * Fired whenever an established touch contact moves within the element
  71. * associated with this Guacamole.Touch.
  72. *
  73. * @event Guacamole.Touch#touchmove
  74. * @param {!Guacamole.Touch.Event} event
  75. * A {@link Guacamole.Touch.Event} object representing the "touchmove"
  76. * event.
  77. */
  78. /**
  79. * Fired whenever an established touch contact is lifted from the element
  80. * associated with this Guacamole.Touch.
  81. *
  82. * @event Guacamole.Touch#touchend
  83. * @param {!Guacamole.Touch.Event} event
  84. * A {@link Guacamole.Touch.Event} object representing the "touchend"
  85. * event.
  86. */
  87. element.addEventListener('touchstart', function touchstart(e) {
  88. // Fire "ontouchstart" events for all new touches
  89. for (var i = 0; i < e.changedTouches.length; i++) {
  90. var changedTouch = e.changedTouches[i];
  91. var identifier = changedTouch.identifier;
  92. // Ignore duplicated touches
  93. if (guacTouch.touches[identifier])
  94. continue;
  95. var touch = guacTouch.touches[identifier] = new Guacamole.Touch.State({
  96. id : identifier,
  97. radiusX : changedTouch.radiusX || DEFAULT_CONTACT_RADIUS,
  98. radiusY : changedTouch.radiusY || DEFAULT_CONTACT_RADIUS,
  99. angle : changedTouch.angle || 0.0,
  100. force : changedTouch.force || 1.0 /* Within JavaScript changedTouch events, a force of 0.0 indicates the device does not support reporting changedTouch force */
  101. });
  102. guacTouch.activeTouches++;
  103. touch.fromClientPosition(element, changedTouch.clientX, changedTouch.clientY);
  104. guacTouch.dispatch(new Guacamole.Touch.Event('touchmove', e, touch));
  105. }
  106. }, false);
  107. element.addEventListener('touchmove', function touchstart(e) {
  108. // Fire "ontouchmove" events for all updated touches
  109. for (var i = 0; i < e.changedTouches.length; i++) {
  110. var changedTouch = e.changedTouches[i];
  111. var identifier = changedTouch.identifier;
  112. // Ignore any unrecognized touches
  113. var touch = guacTouch.touches[identifier];
  114. if (!touch)
  115. continue;
  116. // Update force only if supported by browser (otherwise, assume
  117. // force is unchanged)
  118. if (changedTouch.force)
  119. touch.force = changedTouch.force;
  120. // Update touch area, if supported by browser and device
  121. touch.angle = changedTouch.angle || 0.0;
  122. touch.radiusX = changedTouch.radiusX || DEFAULT_CONTACT_RADIUS;
  123. touch.radiusY = changedTouch.radiusY || DEFAULT_CONTACT_RADIUS;
  124. // Update with any change in position
  125. touch.fromClientPosition(element, changedTouch.clientX, changedTouch.clientY);
  126. guacTouch.dispatch(new Guacamole.Touch.Event('touchmove', e, touch));
  127. }
  128. }, false);
  129. element.addEventListener('touchend', function touchstart(e) {
  130. // Fire "ontouchend" events for all updated touches
  131. for (var i = 0; i < e.changedTouches.length; i++) {
  132. var changedTouch = e.changedTouches[i];
  133. var identifier = changedTouch.identifier;
  134. // Ignore any unrecognized touches
  135. var touch = guacTouch.touches[identifier];
  136. if (!touch)
  137. continue;
  138. // Stop tracking this particular touch
  139. delete guacTouch.touches[identifier];
  140. guacTouch.activeTouches--;
  141. // Touch has ended
  142. touch.force = 0.0;
  143. // Update with final position
  144. touch.fromClientPosition(element, changedTouch.clientX, changedTouch.clientY);
  145. guacTouch.dispatch(new Guacamole.Touch.Event('touchend', e, touch));
  146. }
  147. }, false);
  148. };
  149. /**
  150. * The current state of a touch contact.
  151. *
  152. * @constructor
  153. * @augments Guacamole.Position
  154. * @param {Guacamole.Touch.State|object} [template={}]
  155. * The object whose properties should be copied within the new
  156. * Guacamole.Touch.State.
  157. */
  158. Guacamole.Touch.State = function State(template) {
  159. template = template || {};
  160. Guacamole.Position.call(this, template);
  161. /**
  162. * An arbitrary integer ID which uniquely identifies this contact relative
  163. * to other active contacts.
  164. *
  165. * @type {!number}
  166. * @default 0
  167. */
  168. this.id = template.id || 0;
  169. /**
  170. * The Y radius of the ellipse covering the general area of the touch
  171. * contact, in pixels.
  172. *
  173. * @type {!number}
  174. * @default 0
  175. */
  176. this.radiusX = template.radiusX || 0;
  177. /**
  178. * The X radius of the ellipse covering the general area of the touch
  179. * contact, in pixels.
  180. *
  181. * @type {!number}
  182. * @default 0
  183. */
  184. this.radiusY = template.radiusY || 0;
  185. /**
  186. * The rough angle of clockwise rotation of the general area of the touch
  187. * contact, in degrees.
  188. *
  189. * @type {!number}
  190. * @default 0.0
  191. */
  192. this.angle = template.angle || 0.0;
  193. /**
  194. * The relative force exerted by the touch contact, where 0 is no force
  195. * (the touch has been lifted) and 1 is maximum force (the maximum amount
  196. * of force representable by the device).
  197. *
  198. * @type {!number}
  199. * @default 1.0
  200. */
  201. this.force = template.force || 1.0;
  202. };
  203. /**
  204. * An event which represents a change in state of a single touch contact,
  205. * including the creation or removal of that contact. If multiple contacts are
  206. * involved in a touch interaction, each contact will be associated with its
  207. * own event.
  208. *
  209. * @constructor
  210. * @augments Guacamole.Event.DOMEvent
  211. * @param {!string} type
  212. * The name of the touch event type. Possible values are "touchstart",
  213. * "touchmove", and "touchend".
  214. *
  215. * @param {!TouchEvent} event
  216. * The DOM touch event that produced this Guacamole.Touch.Event.
  217. *
  218. * @param {!Guacamole.Touch.State} state
  219. * The state of the touch contact associated with this event.
  220. */
  221. Guacamole.Touch.Event = function TouchEvent(type, event, state) {
  222. Guacamole.Event.DOMEvent.call(this, type, [ event ]);
  223. /**
  224. * The state of the touch contact associated with this event.
  225. *
  226. * @type {!Guacamole.Touch.State}
  227. */
  228. this.state = state;
  229. };