|
1 L.Map.mergeOptions({ |
|
2 touchExtend: true |
|
3 }); |
|
4 |
|
5 /** |
|
6 * @class L.Map.TouchExtend |
|
7 * @aka TouchExtend |
|
8 */ |
|
9 L.Map.TouchExtend = L.Handler.extend({ |
|
10 |
|
11 // @method initialize(): void |
|
12 // Sets TouchExtend private accessor variables |
|
13 initialize: function (map) { |
|
14 this._map = map; |
|
15 this._container = map._container; |
|
16 this._pane = map._panes.overlayPane; |
|
17 }, |
|
18 |
|
19 // @method addHooks(): void |
|
20 // Adds dom listener events to the map container |
|
21 addHooks: function () { |
|
22 L.DomEvent.on(this._container, 'touchstart', this._onTouchStart, this); |
|
23 L.DomEvent.on(this._container, 'touchend', this._onTouchEnd, this); |
|
24 L.DomEvent.on(this._container, 'touchmove', this._onTouchMove, this); |
|
25 if (this._detectIE()) { |
|
26 L.DomEvent.on(this._container, 'MSPointerDown', this._onTouchStart, this); |
|
27 L.DomEvent.on(this._container, 'MSPointerUp', this._onTouchEnd, this); |
|
28 L.DomEvent.on(this._container, 'MSPointerMove', this._onTouchMove, this); |
|
29 L.DomEvent.on(this._container, 'MSPointerCancel', this._onTouchCancel, this); |
|
30 |
|
31 } else { |
|
32 L.DomEvent.on(this._container, 'touchcancel', this._onTouchCancel, this); |
|
33 L.DomEvent.on(this._container, 'touchleave', this._onTouchLeave, this); |
|
34 } |
|
35 }, |
|
36 |
|
37 // @method removeHooks(): void |
|
38 // Removes dom listener events from the map container |
|
39 removeHooks: function () { |
|
40 L.DomEvent.off(this._container, 'touchstart', this._onTouchStart); |
|
41 L.DomEvent.off(this._container, 'touchend', this._onTouchEnd); |
|
42 L.DomEvent.off(this._container, 'touchmove', this._onTouchMove); |
|
43 if (this._detectIE()) { |
|
44 L.DomEvent.off(this._container, 'MSPointerDowm', this._onTouchStart); |
|
45 L.DomEvent.off(this._container, 'MSPointerUp', this._onTouchEnd); |
|
46 L.DomEvent.off(this._container, 'MSPointerMove', this._onTouchMove); |
|
47 L.DomEvent.off(this._container, 'MSPointerCancel', this._onTouchCancel); |
|
48 } else { |
|
49 L.DomEvent.off(this._container, 'touchcancel', this._onTouchCancel); |
|
50 L.DomEvent.off(this._container, 'touchleave', this._onTouchLeave); |
|
51 } |
|
52 }, |
|
53 |
|
54 _touchEvent: function (e, type) { |
|
55 // #TODO: fix the pageX error that is do a bug in Android where a single touch triggers two click events |
|
56 // _filterClick is what leaflet uses as a workaround. |
|
57 // This is a problem with more things than just android. Another problem is touchEnd has no touches in |
|
58 // its touch list. |
|
59 var touchEvent = {}; |
|
60 if (typeof e.touches !== 'undefined') { |
|
61 if (!e.touches.length) { |
|
62 return; |
|
63 } |
|
64 touchEvent = e.touches[0]; |
|
65 } else if (e.pointerType === 'touch') { |
|
66 touchEvent = e; |
|
67 if (!this._filterClick(e)) { |
|
68 return; |
|
69 } |
|
70 } else { |
|
71 return; |
|
72 } |
|
73 |
|
74 var containerPoint = this._map.mouseEventToContainerPoint(touchEvent), |
|
75 layerPoint = this._map.mouseEventToLayerPoint(touchEvent), |
|
76 latlng = this._map.layerPointToLatLng(layerPoint); |
|
77 |
|
78 this._map.fire(type, { |
|
79 latlng: latlng, |
|
80 layerPoint: layerPoint, |
|
81 containerPoint: containerPoint, |
|
82 pageX: touchEvent.pageX, |
|
83 pageY: touchEvent.pageY, |
|
84 originalEvent: e |
|
85 }); |
|
86 }, |
|
87 |
|
88 /** Borrowed from Leaflet and modified for bool ops **/ |
|
89 _filterClick: function (e) { |
|
90 var timeStamp = (e.timeStamp || e.originalEvent.timeStamp), |
|
91 elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); |
|
92 |
|
93 // are they closer together than 500ms yet more than 100ms? |
|
94 // Android typically triggers them ~300ms apart while multiple listeners |
|
95 // on the same event should be triggered far faster; |
|
96 // or check if click is simulated on the element, and if it is, reject any non-simulated events |
|
97 if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { |
|
98 L.DomEvent.stop(e); |
|
99 return false; |
|
100 } |
|
101 L.DomEvent._lastClick = timeStamp; |
|
102 return true; |
|
103 }, |
|
104 |
|
105 _onTouchStart: function (e) { |
|
106 if (!this._map._loaded) { |
|
107 return; |
|
108 } |
|
109 |
|
110 var type = 'touchstart'; |
|
111 this._touchEvent(e, type); |
|
112 |
|
113 }, |
|
114 |
|
115 _onTouchEnd: function (e) { |
|
116 if (!this._map._loaded) { |
|
117 return; |
|
118 } |
|
119 |
|
120 var type = 'touchend'; |
|
121 this._touchEvent(e, type); |
|
122 }, |
|
123 |
|
124 _onTouchCancel: function (e) { |
|
125 if (!this._map._loaded) { |
|
126 return; |
|
127 } |
|
128 |
|
129 var type = 'touchcancel'; |
|
130 if (this._detectIE()) { |
|
131 type = 'pointercancel'; |
|
132 } |
|
133 this._touchEvent(e, type); |
|
134 }, |
|
135 |
|
136 _onTouchLeave: function (e) { |
|
137 if (!this._map._loaded) { |
|
138 return; |
|
139 } |
|
140 |
|
141 var type = 'touchleave'; |
|
142 this._touchEvent(e, type); |
|
143 }, |
|
144 |
|
145 _onTouchMove: function (e) { |
|
146 if (!this._map._loaded) { |
|
147 return; |
|
148 } |
|
149 |
|
150 var type = 'touchmove'; |
|
151 this._touchEvent(e, type); |
|
152 }, |
|
153 |
|
154 _detectIE: function () { |
|
155 var ua = window.navigator.userAgent; |
|
156 |
|
157 var msie = ua.indexOf('MSIE '); |
|
158 if (msie > 0) { |
|
159 // IE 10 or older => return version number |
|
160 return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); |
|
161 } |
|
162 |
|
163 var trident = ua.indexOf('Trident/'); |
|
164 if (trident > 0) { |
|
165 // IE 11 => return version number |
|
166 var rv = ua.indexOf('rv:'); |
|
167 return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); |
|
168 } |
|
169 |
|
170 var edge = ua.indexOf('Edge/'); |
|
171 if (edge > 0) { |
|
172 // IE 12 => return version number |
|
173 return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); |
|
174 } |
|
175 |
|
176 // other browser |
|
177 return false; |
|
178 } |
|
179 }); |
|
180 |
|
181 L.Map.addInitHook('addHandler', 'touchExtend', L.Map.TouchExtend); |
|
182 |
|
183 |
|
184 /** |
|
185 * @class L.Marker.Touch |
|
186 * @aka Marker.Touch |
|
187 * |
|
188 * This isn't full Touch support. This is just to get markers to also support dom touch events after creation |
|
189 * #TODO: find a better way of getting markers to support touch. |
|
190 */ |
|
191 L.Marker.Touch = L.Marker.extend({ |
|
192 |
|
193 _initInteraction: function () { |
|
194 if (!this.addInteractiveTarget) { |
|
195 // 0.7.x support |
|
196 return this._initInteractionLegacy(); |
|
197 } |
|
198 // TODO this may need be updated to re-add touch events for 1.0+ |
|
199 return L.Marker.prototype._initInteraction.apply(this); |
|
200 }, |
|
201 |
|
202 // This is an exact copy of https://github.com/Leaflet/Leaflet/blob/v0.7/src/layer/marker/Marker.js |
|
203 // with the addition of the touch events |
|
204 _initInteractionLegacy: function () { |
|
205 |
|
206 if (!this.options.clickable) { |
|
207 return; |
|
208 } |
|
209 |
|
210 // TODO refactor into something shared with Map/Path/etc. to DRY it up |
|
211 |
|
212 var icon = this._icon, |
|
213 events = ['dblclick', |
|
214 'mousedown', |
|
215 'mouseover', |
|
216 'mouseout', |
|
217 'contextmenu', |
|
218 'touchstart', |
|
219 'touchend', |
|
220 'touchmove']; |
|
221 if (this._detectIE) { |
|
222 events.concat(['MSPointerDown', |
|
223 'MSPointerUp', |
|
224 'MSPointerMove', |
|
225 'MSPointerCancel']); |
|
226 } else { |
|
227 events.concat(['touchcancel']); |
|
228 } |
|
229 |
|
230 L.DomUtil.addClass(icon, 'leaflet-clickable'); |
|
231 L.DomEvent.on(icon, 'click', this._onMouseClick, this); |
|
232 L.DomEvent.on(icon, 'keypress', this._onKeyPress, this); |
|
233 |
|
234 for (var i = 0; i < events.length; i++) { |
|
235 L.DomEvent.on(icon, events[i], this._fireMouseEvent, this); |
|
236 } |
|
237 |
|
238 if (L.Handler.MarkerDrag) { |
|
239 this.dragging = new L.Handler.MarkerDrag(this); |
|
240 |
|
241 if (this.options.draggable) { |
|
242 this.dragging.enable(); |
|
243 } |
|
244 } |
|
245 }, |
|
246 |
|
247 _detectIE: function () { |
|
248 var ua = window.navigator.userAgent; |
|
249 |
|
250 var msie = ua.indexOf('MSIE '); |
|
251 if (msie > 0) { |
|
252 // IE 10 or older => return version number |
|
253 return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); |
|
254 } |
|
255 |
|
256 var trident = ua.indexOf('Trident/'); |
|
257 if (trident > 0) { |
|
258 // IE 11 => return version number |
|
259 var rv = ua.indexOf('rv:'); |
|
260 return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); |
|
261 } |
|
262 |
|
263 var edge = ua.indexOf('Edge/'); |
|
264 if (edge > 0) { |
|
265 // IE 12 => return version number |
|
266 return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); |
|
267 } |
|
268 |
|
269 // other browser |
|
270 return false; |
|
271 } |
|
272 }); |