1+ import Base from './Base.mjs' ;
2+ import DomEvents from '../../DomEvents.mjs' ;
3+
4+ let preventScrolling = false ;
5+
6+ // WebKit requires cancelable touchmove events to be added as early as possible
7+ window . addEventListener ( 'touchmove' , event => {
8+ if ( ! preventScrolling ) {
9+ return ;
10+ }
11+
12+ // Prevent scrolling
13+ if ( event . cancelable ) {
14+ event . preventDefault ( ) ;
15+ }
16+ } , { cancelable : true , passive : false } ) ;
17+
18+ /**
19+ * @class Neo.main.draggable.sensor.Touch
20+ * @extends Neo.main.draggable.sensor.Base
21+ */
22+ class Touch extends Base {
23+ static getConfig ( ) { return {
24+ /**
25+ * @member {String} className='Neo.main.draggable.sensor.Touch'
26+ * @protected
27+ */
28+ className : 'Neo.main.draggable.sensor.Touch' ,
29+ /**
30+ * @member {Number} delay=200
31+ */
32+ delay : 200 ,
33+ /**
34+ * @member {Number} minDistance=0
35+ */
36+ minDistance : 0 ,
37+ /**
38+ * @member {Number|null} pageX=null
39+ * @protected
40+ */
41+ pageX : null ,
42+ /**
43+ * @member {Number|null} pageY=null
44+ * @protected
45+ */
46+ pageY : null ,
47+ /**
48+ * @member {Number|null} tapTimeout=null
49+ */
50+ tapTimeout : null ,
51+ /**
52+ * @member {Number} touchStartTime=0
53+ */
54+ touchStartTime : 0
55+ } }
56+
57+ /**
58+ *
59+ * @param config
60+ */
61+ constructor ( config ) {
62+ super ( config ) ;
63+ Neo . bindMethods ( this , [ 'onDistanceChange' , 'onTouchEnd' , 'onTouchMove' , 'onTouchStart' , 'startDrag' ] ) ;
64+ }
65+
66+ /**
67+ * Attaches sensors event listeners to the DOM
68+ */
69+ attach ( ) {
70+ document . addEventListener ( 'touchstart' , this . onTouchStart ) ;
71+ }
72+
73+ /**
74+ * Detaches sensors event listeners from the DOM
75+ */
76+ detach ( ) {
77+ document . removeEventListener ( 'touchstart' , this . onTouchStart ) ;
78+ }
79+
80+ /**
81+ * Detect change in distance, starting drag when both delay and distance requirements are met
82+ * @param {TouchEvent|Object } event - Object in case it does get trigger via the tapTimeout
83+ */
84+ onDistanceChange ( event ) {
85+ let me = this ;
86+
87+ if ( me . currentElement ) {
88+ const { pageX, pageY} = DomEvents . getTouchCoords ( event ) ,
89+ start = DomEvents . getTouchCoords ( me . startEvent ) ,
90+ timeElapsed = Date . now ( ) - me . touchStartTime ,
91+ distanceTravelled = DomEvents . getDistance ( start . pageX , start . pageY , pageX , pageY ) || 0 ;
92+
93+ Object . assign ( me , { pageX, pageY} ) ;
94+
95+ if ( timeElapsed >= me . delay && distanceTravelled >= me . minDistance ) {
96+ clearTimeout ( me . tapTimeout ) ;
97+ document . removeEventListener ( 'touchmove' , me . onDistanceChange ) ;
98+ me . startDrag ( ) ;
99+ }
100+ }
101+ }
102+
103+ /**
104+ *
105+ * @param {TouchEvent } event
106+ */
107+ onTouchEnd ( event ) {
108+ preventScrolling = false ;
109+
110+ let me = this ;
111+
112+ clearTimeout ( me . tapTimeout ) ;
113+
114+ document . removeEventListener ( 'dragstart' , stopEvent ) ;
115+ document . removeEventListener ( 'touchcancel' , me . onTouchEnd ) ;
116+ document . removeEventListener ( 'touchend' , me . onTouchEnd ) ;
117+ document . removeEventListener ( 'touchmove' , me . onDistanceChange ) ;
118+
119+ if ( me . dragging ) {
120+ const { pageX, pageY} = DomEvents . getTouchCoords ( event ) ;
121+
122+ let element = me . currentElement ,
123+ target = document . elementFromPoint ( pageX - window . scrollX , pageY - window . scrollY ) ;
124+
125+ event . preventDefault ( ) ;
126+
127+ me . trigger ( element , {
128+ clientX : pageX ,
129+ clientY : pageY ,
130+ element,
131+ originalEvent : event ,
132+ path : me . startEvent . path || me . startEvent . composedPath ( ) ,
133+ target,
134+ type : 'drag:end'
135+ } ) ;
136+
137+ document . removeEventListener ( 'contextmenu' , stopEvent , true ) ;
138+ document . removeEventListener ( 'touchmove' , me . onTouchMove ) ;
139+
140+ Object . assign ( me , {
141+ currentElement : null ,
142+ dragging : false ,
143+ startEvent : null
144+ } ) ;
145+ }
146+
147+ me . dragging = false ;
148+ }
149+
150+ /**
151+ *
152+ * @param {TouchEvent } event
153+ */
154+ onTouchMove ( event ) {
155+ let me = this ;
156+
157+ if ( me . dragging ) {
158+ const { pageX, pageY} = DomEvents . getTouchCoords ( event ) ;
159+
160+ let element = me . currentElement ,
161+ target = document . elementFromPoint ( pageX - window . scrollX , pageY - window . scrollY ) ;
162+
163+ me . trigger ( element , {
164+ clientX : pageX ,
165+ clientY : pageY ,
166+ element,
167+ originalEvent : event ,
168+ path : me . startEvent . path || me . startEvent . composedPath ( ) ,
169+ target,
170+ type : 'drag:move'
171+ } ) ;
172+ }
173+ }
174+
175+ /**
176+ *
177+ * @param {TouchEvent } event
178+ */
179+ onTouchStart ( event ) {
180+ let me = this ,
181+ target = DomEvents . testPathInclusion ( event , me . dragTargetClasses ) ;
182+
183+ if ( target ) {
184+ const { pageX, pageY} = DomEvents . getTouchCoords ( event ) ;
185+
186+ Object . assign ( me , {
187+ currentElement : target . node ,
188+ pageX : pageX ,
189+ pageY : pageY ,
190+ startEvent : event ,
191+ touchStartTime : Date . now ( )
192+ } ) ;
193+
194+ document . addEventListener ( 'dragstart' , stopEvent ) ;
195+ document . addEventListener ( 'touchcancel' , me . onTouchEnd ) ;
196+ document . addEventListener ( 'touchend' , me . onTouchEnd ) ;
197+ document . addEventListener ( 'touchmove' , me . onDistanceChange , { cancelable : true } ) ;
198+
199+ me . tapTimeout = setTimeout ( ( ) => {
200+ me . onDistanceChange ( { touches : [ { pageX : me . pageX , pageY : me . pageY } ] } ) ;
201+ } , me . delay ) ;
202+ }
203+ }
204+
205+ /**
206+ *
207+ */
208+ startDrag ( ) {
209+ let me = this ,
210+ element = me . currentElement ,
211+ startEvent = me . startEvent ,
212+ touch = DomEvents . getTouchCoords ( me . startEvent ) ;
213+
214+ me . trigger ( element , {
215+ clientX : touch . pageX ,
216+ clientY : touch . pageY ,
217+ element,
218+ originalEvent : startEvent ,
219+ path : startEvent . path || startEvent . composedPath ( ) ,
220+ target : startEvent . target ,
221+ type : 'drag:start'
222+ } ) ;
223+
224+ me . dragging = true ; // todo
225+
226+ if ( me . dragging ) {
227+ document . addEventListener ( 'contextmenu' , stopEvent , true ) ;
228+ document . addEventListener ( 'touchmove' , me . onTouchMove ) ;
229+ }
230+
231+ preventScrolling = me . dragging ;
232+ }
233+ }
234+
235+ function stopEvent ( event ) {
236+ event . stopEvent ( ) ;
237+ }
238+
239+ Neo . applyClassConfig ( Touch ) ;
240+
241+ export { Touch as default } ;
0 commit comments