remove debugging code
[roojs1] / ux / Tscroll.js
1 /**
2  *
3  * usage:
4  *
5  * new Roo.ux.TouchScroll( { el : id| dom element | roo element } )
6  *
7  *
8  */
9
10
11 Roo.ux.TouchScroll = function (cfg) {
12     Roo.apply(this,cfg);
13     this.el = Roo.get(this.el);
14     this.init();
15 }
16     
17 Roo.apply(Roo.ux.TouchScroll.prototype, {
18     
19         // Define default scroll settings
20          
21         y: 0,
22         scrollHeight: 0,
23         elastic: !navigator.userAgent.match(/android/i),
24         momentum: true,
25         elasticDamp: 0.6,
26         elasticTime: 50,
27         reboundTime: 400,
28         momentumDamp: 0.9,
29         momentumTime: 300,
30         iPadMomentumDamp: 0.95,
31         iPadMomentumTime: 1200,
32         touchTags: ['select', 'input', 'textarea'],
33         
34         _init : false,
35         
36         init: function( ) {
37                 
38                 
39               // Prevent double-init, just update instead
40             if (this._init) {
41                 return this.update();
42             }
43             
44             this._init = true;
45                         
46             // Define element variables
47             var 
48                 scrollY = -this.el.getY(),
49                 touchY = 0,
50                 movedY = 0,
51                 pollY = 0,
52                 height = 0,
53                 maxHeight = 0,
54                 scrollHeight = 0,
55                 scrolling = false,
56                 bouncing = false,
57                 moved = false,
58                 timeoutID,
59                 isiPad = navigator.platform.match(/ipad/i) ? true : false,
60                 hasMatrix = 'WebKitCSSMatrix' in window,
61                 has3d = hasMatrix && 'm11' in new WebKitCSSMatrix();
62                         
63                 // Keep bottom of scroll area at the bottom on resize
64                 var update = this.update = function() {
65                         height = el.getHeight();
66                         if (el.dom.scrollHeight) {
67                                 scrollHeight = el.dom.scrollHeight;
68                         } else {
69                                 scrollHeight = el.dom.getAttribute('scrollHeight');
70                         }
71                         if (scrollHeight < height) {
72                                 scrollHeight = height;
73                         }
74                         maxHeight = height - scrollHeight;
75                         clearTimeout(timeoutID);
76                         clampScroll(false);
77                 };
78                         
79                 // Set up initial variables
80                 update();
81                 
82                         // Set up transform CSS
83                 this.el.setStyle({
84                     '-webkit-transition-property': '-webkit-transform',
85                     '-webkit-transition-timing-function': 'cubic-bezier(0,0,0.2,1)',
86                     '-webkit-transition-duration': '0',
87                     '-webkit-transform': cssTranslate(scrollY)
88                 });
89                         
90                         // Listen for screen size change event
91                 window.addEventListener('onorientationchange' in window ?
92                                 'orientationchange' : 'resize', update, false);
93                         
94                 // Listen for touch events
95                 this.el.on('touchstart.touchScroll', touchStart);
96                 this.el.on('touchmove.touchScroll', touchMove);
97                 this.el.on('touchend.touchScroll touchcancel.touchScroll', touchEnd);
98                 this.el.on('webkitTransitionEnd.touchScroll', transitionEnd);
99                         
100                         // Set the position of the scroll area using transform CSS
101                 var setPosition = this.setPosition = function(y) {
102                         scrollY = y;
103                         this.el.setStyle('-webkit-transform', cssTranslate(scrollY));
104                 };
105                 
106                         // Transform using a 3D translate if available
107                 function cssTranslate(y) {
108                         return 'translate' + (has3d ? '3d(0,' : '(0,') + y + 'px' + (has3d ? ',0)' : ')');
109                 }
110                         
111                         // Set CSS transition time
112                 function setTransitionTime(time) {
113                         time = time || '0';
114                         this.el.setStyle('-webkit-transition-duration', time + 'ms');
115                 }
116     
117                             // Get the actual pixel position made by transform CSS
118                 function getPosition() {
119                         if (hasMatrix) {
120                                 var transform = window.getComputedStyle(this.el.dom).webkitTransform;
121                                 if (!!transform && transform !== 'none') {
122                                         var matrix = new WebKitCSSMatrix(transform);
123                                         return matrix.f;
124                                 }
125                         }
126                         return scrollY;
127                 }
128                             
129                     // Expose getPosition API
130                 this.getPosition = function() {
131                         return getPosition();
132                 };
133
134                 // Bounce back to the bounds after momentum scrolling
135                 function reboundScroll() {
136                         if (scrollY > 0) {
137                                 scrollTo(0, o.reboundTime);
138                         } else if (scrollY < maxHeight) {
139                                 scrollTo(maxHeight, o.reboundTime);
140                         }
141                 }
142
143                 // Stop everything once the CSS transition in complete
144                 function transitionEnd() {
145                         if (bouncing) {
146                                 bouncing = false;
147                                 reboundScroll();
148                         }
149
150                         clearTimeout(timeoutID);
151                 }
152         
153                 // Limit the scrolling to within the bounds
154                 function clampScroll(poll) {
155                         if (!hasMatrix || bouncing) {
156                                 return;
157                         }
158     
159                         var oldY = pollY;
160                         pollY = getPosition();
161                         
162                         if (pollY > 0) {
163                                 if (o.elastic) {
164                                         // Slow down outside top bound
165                                         bouncing = true;
166                                         scrollY = 0;
167                                         momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
168                                 } else {
169                                         // Stop outside top bound
170                                         setTransitionTime(0);
171                                         setPosition(0);
172                                 }
173                         } else if (pollY < maxHeight) {
174                                 if (o.elastic) {
175                                         // Slow down outside bottom bound
176                                         bouncing = true;
177                                         scrollY = maxHeight;
178                                         momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
179                                 } else {
180                                         // Stop outside bottom bound
181                                         setTransitionTime(0);
182                                         setPosition(maxHeight);
183                                 }
184                         } else if (poll) {
185                                 // Poll the computed position to check if element is out of bounds
186                                 timeoutID = setTimeout(clampScroll, 20, true);
187                         }
188                 }
189         
190         // Animate to a position using CSS
191                 function scrollTo(destY, time) {
192                         if (destY === scrollY) {
193                                 return;
194                         }
195                         
196                         moved = true;
197                         setTransitionTime(time);
198                         setPosition(destY);
199                 }
200         
201                 // Perform a momentum-based scroll using CSS
202                 function momentumScroll(d, k, minDist, maxDist, t) {
203                         var ad = Math.abs(d),
204                                 dy = 0;
205                         
206                         // Calculate the total distance
207                         while (ad > 0.1) {
208                                 ad *= k;
209                                 dy += ad;
210                         }
211                         
212                         // Limit to within min and max distances
213                         if (dy > maxDist) {
214                                 dy = maxDist;
215                         }
216                         if (dy > minDist) {
217                                 if (d < 0) {
218                                         dy = -dy;
219                                 }
220                                 
221                                 dy += scrollY;
222                                 
223                                 // If outside the bounds, don't go too far
224                                 if (height > 0) {
225                                         if (dy > height * 2) {
226                                                 var ody = dy;
227                                                 dy = height * 2;
228                                         } else if (dy < maxHeight - height * 2) {
229                                                 dy = maxHeight - height * 2;
230                                         }
231                                 }
232                         
233                                 // Perform scroll
234                                 scrollTo(Math.round(dy), t);
235                         }
236                         
237                         clampScroll(true);
238                 }
239         
240                 // Get the touch points from this event
241                 function getTouches(e) {
242                         if (e.originalEvent) {
243                                 if (e.originalEvent.touches && e.originalEvent.touches.length) {
244                                         return e.originalEvent.touches;
245                                 } else if (e.originalEvent.changedTouches && e.originalEvent.changedTouches.length) {
246                                         return e.originalEvent.changedTouches;
247                                 }
248                         }
249                         return e.touches;
250                 }
251                 
252                 // Dispatches a fake mouse event from a touch event
253                 function dispatchMouseEvent(name, touch, target) {
254                         var e = document.createEvent('MouseEvent');
255                         e.initMouseEvent(name, true, true, touch.view, 1,
256                                         touch.screenX, touch.screenY,
257                                         touch.clientX, touch.clientY,
258                                         false, false, false, false, 0, null);
259                         target.dispatchEvent(e);
260                 }
261         
262                 // Find the root node of this target
263                 function getRootNode(target) {
264                         while (target.nodeType !== 1) {
265                                 target = target.parentNode;
266                         }
267                         return target;
268                 }
269                 
270                 // Perform a touch start event
271                 function touchStart(e) {
272                     // Allow certain HTML tags to receive touch events
273                     if (o.touchTags.indexOf(e.target.tagName.toLowerCase()) !== -1) {
274                             return;
275                     }
276                     
277                     // Stop the default touches
278                     e.preventDefault();
279                     e.stopPropagation();
280                     
281                     var touch = getTouches(e)[0];
282                     
283                     // Dispatch a fake mouse down event         
284                     dispatchMouseEvent('mousedown', touch, getRootNode(touch.target));
285                     
286                     scrolling = true;
287                     moved = false;
288                     movedY = 0;
289                     
290                     clearTimeout(timeoutID);
291                     setTransitionTime(0);
292                     
293                     // Check scroll position
294                     if (o.momentum) {
295                             var y = getPosition();
296                             if (y !== scrollY) {
297                                     setPosition(y);
298                                     moved = true;
299                             }
300                     }
301
302                     touchY = touch.pageY - scrollY;
303                 }
304                         
305                 // Perform a touch move event
306                 function touchMove(e) {
307                         if (!scrolling) {
308                                 return;
309                         }
310                         
311                         var dy = getTouches(e)[0].pageY - touchY;
312                         
313                         // Elastic-drag or stop when moving outside of boundaries
314                         if (dy > 0) {
315                                 if (o.elastic) {
316                                         dy /= 2;
317                                 } else {
318                                         dy = 0;
319                                 }
320                         } else if (dy < maxHeight) {
321                                 if (o.elastic) {
322                                         dy = (dy + maxHeight) / 2;
323                                 } else {
324                                         dy = maxHeight;
325                                 }
326                         }
327                         
328                         movedY = dy - scrollY;
329                         moved = true;
330                         setPosition(dy);
331                 }
332                 
333                         // Perform a touch end event
334                         function touchEnd(e) {
335                                 if (!scrolling) {
336                                         return;
337                                 }
338                                 
339                                 scrolling = false;
340                                 
341                                 if (moved) {
342                                         // Ease back to within boundaries
343                                         if (scrollY > 0 || scrollY < maxHeight) {
344                                                 reboundScroll();
345                                         } else if (o.momentum) {
346                                                 // Free scroll with momentum
347                                                 momentumScroll(movedY, isiPad ? o.iPadMomentumDamp : o.momentumDamp, 40, 2000, isiPad ? o.iPadMomentumTime : o.momentumTime);
348                                         }                       
349                                 } else {
350                                         var touch = getTouches(e)[0],
351                                                 target = getRootNode(touch.target);
352                                         
353                                         // Dispatch fake mouse up and click events if this touch event did not move
354                                         dispatchMouseEvent('mouseup', touch, target);
355                                         dispatchMouseEvent('click', touch, target);
356                                 }
357                         }
358                 
359                 });
360         },
361         
362         update: function() {
363                 return this.each(function() {
364                         this.update();
365                 });
366         },
367         
368         getPosition: function() {
369                 var a = [];
370                 this.each(function() {
371                         a.push(-this.getPosition());
372                 });
373                 return a;
374         },
375         
376         setPosition: function(y) {
377                 return this.each(function() {
378                         this.setPosition(-y);
379                 });
380         }
381         
382 };
383         
384         // Public method for touchScroll
385         $.fn.touchScroll = function(method) {
386             if (methods[method]) {
387                         return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
388                 } else if (typeof method === 'object' || !method) {
389                         return methods.init.apply(this, arguments);
390                 } else {
391                         $.error('Method ' +  method + ' does not exist on jQuery.touchScroll');
392                 }
393         };
394
395 })(jQuery);