]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/iscroll.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / iscroll.js
1 /**
2  * 
3  * Find more about the scrolling function at
4  * http://cubiq.org/iscroll
5  *
6  * Copyright (c) 2010 Matteo Spinelli, http://cubiq.org/
7  * Released under MIT license
8  * http://cubiq.org/dropbox/mit-license.txt
9  * 
10  * Version 3.7.1 - Last updated: 2010.10.08
11  * 
12  */
13
14 (function(){
15 function iScroll (el, options) {
16         var that = this, i;
17         that.element = typeof el == 'object' ? el : document.getElementById(el);
18         that.wrapper = that.element.parentNode;
19
20         that.element.style.webkitTransitionProperty = '-webkit-transform';
21         that.element.style.webkitTransitionTimingFunction = 'cubic-bezier(0,0,0.25,1)';
22         that.element.style.webkitTransitionDuration = '0';
23         that.element.style.webkitTransform = translateOpen + '0,0' + translateClose;
24
25         // Default options
26         that.options = {
27                 bounce: has3d,
28                 momentum: has3d,
29                 checkDOMChanges: true,
30                 topOnDOMChanges: false,
31                 hScrollbar: has3d,
32                 vScrollbar: has3d,
33                 fadeScrollbar: isIthing || !isTouch,
34                 shrinkScrollbar: isIthing || !isTouch,
35                 desktopCompatibility: false,
36                 overflow: 'auto',
37                 snap: false,
38                 bounceLock: false,
39                 scrollbarColor: 'rgba(0,0,0,0.5)',
40                 onScrollEnd: function () {}
41         };
42         
43         // User defined options
44         if (typeof options == 'object') {
45                 for (i in options) {
46                         that.options[i] = options[i];
47                 }
48         }
49
50         if (that.options.desktopCompatibility) {
51                 that.options.overflow = 'hidden';
52         }
53         
54         that.onScrollEnd = that.options.onScrollEnd;
55         delete that.options.onScrollEnd;
56         
57         that.wrapper.style.overflow = that.options.overflow;
58         
59         that.refresh();
60
61         window.addEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', that, false);
62
63         if (isTouch || that.options.desktopCompatibility) {
64                 that.element.addEventListener(START_EVENT, that, false);
65                 that.element.addEventListener(MOVE_EVENT, that, false);
66                 that.element.addEventListener(END_EVENT, that, false);
67         }
68         
69         if (that.options.checkDOMChanges) {
70                 that.element.addEventListener('DOMSubtreeModified', that, false);
71         }
72 }
73
74 iScroll.prototype = {
75         x: 0,
76         y: 0,
77         enabled: true,
78
79         handleEvent: function (e) {
80                 var that = this;
81                 
82                 switch (e.type) {
83                         case START_EVENT:
84                                 that.touchStart(e);
85                                 break;
86                         case MOVE_EVENT:
87                                 that.touchMove(e);
88                                 break;
89                         case END_EVENT:
90                                 that.touchEnd(e);
91                                 break;
92                         case 'webkitTransitionEnd':
93                                 that.transitionEnd();
94                                 break;
95                         case 'orientationchange':
96                         case 'resize':
97                                 that.refresh();
98                                 break;
99                         case 'DOMSubtreeModified':
100                                 that.onDOMModified(e);
101                                 break;
102                 }
103         },
104         
105         onDOMModified: function (e) {
106                 var that = this;
107
108                 // (Hopefully) execute onDOMModified only once
109                 if (e.target.parentNode != that.element) {
110                         return;
111                 }
112
113                 setTimeout(function () { that.refresh(); }, 0);
114
115                 if (that.options.topOnDOMChanges && (that.x!=0 || that.y!=0)) {
116                         that.scrollTo(0,0,'0');
117                 }
118         },
119
120         refresh: function () {
121                 var that = this,
122                         resetX = that.x, resetY = that.y,
123                         snap;
124                 
125                 that.scrollWidth = that.wrapper.clientWidth;
126                 that.scrollHeight = that.wrapper.clientHeight;
127                 that.scrollerWidth = that.element.offsetWidth;
128                 that.scrollerHeight = that.element.offsetHeight;
129                 that.maxScrollX = that.scrollWidth - that.scrollerWidth;
130                 that.maxScrollY = that.scrollHeight - that.scrollerHeight;
131                 that.directionX = 0;
132                 that.directionY = 0;
133
134                 if (that.scrollX) {
135                         if (that.maxScrollX >= 0) {
136                                 resetX = 0;
137                         } else if (that.x < that.maxScrollX) {
138                                 resetX = that.maxScrollX;
139                         }
140                 }
141                 if (that.scrollY) {
142                         if (that.maxScrollY >= 0) {
143                                 resetY = 0;
144                         } else if (that.y < that.maxScrollY) {
145                                 resetY = that.maxScrollY;
146                         }
147                 }
148
149                 // Snap
150                 if (that.options.snap) {
151                         that.maxPageX = -Math.floor(that.maxScrollX/that.scrollWidth);
152                         that.maxPageY = -Math.floor(that.maxScrollY/that.scrollHeight);
153
154                         snap = that.snap(resetX, resetY);
155                         resetX = snap.x;
156                         resetY = snap.y;
157                 }
158
159                 if (resetX!=that.x || resetY!=that.y) {
160                         that.setTransitionTime('0');
161                         that.setPosition(resetX, resetY, true);
162                 }
163                 
164                 that.scrollX = that.scrollerWidth > that.scrollWidth;
165                 that.scrollY = !that.options.bounceLock && !that.scrollX || that.scrollerHeight > that.scrollHeight;
166
167                 // Update horizontal scrollbar
168                 if (that.options.hScrollbar && that.scrollX) {
169                         that.scrollBarX = that.scrollBarX || new scrollbar('horizontal', that.wrapper, that.options.fadeScrollbar, that.options.shrinkScrollbar, that.options.scrollbarColor);
170                         that.scrollBarX.init(that.scrollWidth, that.scrollerWidth);
171                 } else if (that.scrollBarX) {
172                         that.scrollBarX = that.scrollBarX.remove();
173                 }
174
175                 // Update vertical scrollbar
176                 if (that.options.vScrollbar && that.scrollY && that.scrollerHeight > that.scrollHeight) {
177                         that.scrollBarY = that.scrollBarY || new scrollbar('vertical', that.wrapper, that.options.fadeScrollbar, that.options.shrinkScrollbar, that.options.scrollbarColor);
178                         that.scrollBarY.init(that.scrollHeight, that.scrollerHeight);
179                 } else if (that.scrollBarY) {
180                         that.scrollBarY = that.scrollBarY.remove();
181                 }
182         },
183
184         setPosition: function (x, y, hideScrollBars) {
185                 var that = this;
186                 
187                 that.x = x;
188                 that.y = y;
189
190                 that.element.style.webkitTransform = translateOpen + that.x + 'px,' + that.y + 'px' + translateClose;
191
192                 // Move the scrollbars
193                 if (!hideScrollBars) {
194                         if (that.scrollBarX) {
195                                 that.scrollBarX.setPosition(that.x);
196                         }
197                         if (that.scrollBarY) {
198                                 that.scrollBarY.setPosition(that.y);
199                         }
200                 }
201         },
202         
203         setTransitionTime: function(time) {
204                 var that = this;
205                 
206                 time = time || '0';
207                 that.element.style.webkitTransitionDuration = time;
208                 
209                 if (that.scrollBarX) {
210                         that.scrollBarX.bar.style.webkitTransitionDuration = time;
211                         that.scrollBarX.wrapper.style.webkitTransitionDuration = has3d && that.options.fadeScrollbar ? '300ms' : '0';
212                 }
213                 if (that.scrollBarY) {
214                         that.scrollBarY.bar.style.webkitTransitionDuration = time;
215                         that.scrollBarY.wrapper.style.webkitTransitionDuration = has3d && that.options.fadeScrollbar ? '300ms' : '0';
216                 }
217         },
218                 
219         touchStart: function(e) {
220                 var that = this,
221                         matrix;
222                 
223                 if (!that.enabled) {
224                         return;
225                 }
226
227                 e.preventDefault();
228                 e.stopPropagation();
229                 
230                 that.scrolling = true;          // This is probably not needed, but may be useful if iScroll is used in conjuction with other frameworks
231
232                 that.moved = false;
233                 that.distX = 0;
234                 that.distY = 0;
235
236                 that.setTransitionTime('0');
237
238                 // Check if the scroller is really where it should be
239                 if (that.options.momentum || that.options.snap) {
240                         matrix = new WebKitCSSMatrix(window.getComputedStyle(that.element).webkitTransform);
241                         if (matrix.e != that.x || matrix.f != that.y) {
242                                 document.removeEventListener('webkitTransitionEnd', that, false);
243                                 that.setPosition(matrix.e, matrix.f);
244                                 that.moved = true;
245                         }
246                 }
247
248                 that.touchStartX = isTouch ? e.changedTouches[0].pageX : e.pageX;
249                 that.scrollStartX = that.x;
250
251                 that.touchStartY = isTouch ? e.changedTouches[0].pageY : e.pageY;
252                 that.scrollStartY = that.y;
253
254                 that.scrollStartTime = e.timeStamp;
255
256                 that.directionX = 0;
257                 that.directionY = 0;
258         },
259         
260         touchMove: function(e) {
261                 if (!this.scrolling) {
262                         return;
263                 }
264
265                 var that = this,
266                         pageX = isTouch ? e.changedTouches[0].pageX : e.pageX,
267                         pageY = isTouch ? e.changedTouches[0].pageY : e.pageY,
268                         leftDelta = that.scrollX ? pageX - that.touchStartX : 0,
269                         topDelta = that.scrollY ? pageY - that.touchStartY : 0,
270                         newX = that.x + leftDelta,
271                         newY = that.y + topDelta;
272
273                 //e.preventDefault();
274                 e.stopPropagation();    // Stopping propagation just saves some cpu cycles (I presume)
275
276                 that.touchStartX = pageX;
277                 that.touchStartY = pageY;
278
279                 // Slow down if outside of the boundaries
280                 if (newX >= 0 || newX < that.maxScrollX) {
281                         newX = that.options.bounce ? Math.round(that.x + leftDelta / 3) : (newX >= 0 || that.maxScrollX>=0) ? 0 : that.maxScrollX;
282                 }
283                 if (newY >= 0 || newY < that.maxScrollY) { 
284                         newY = that.options.bounce ? Math.round(that.y + topDelta / 3) : (newY >= 0 || that.maxScrollY>=0) ? 0 : that.maxScrollY;
285                 }
286
287                 if (that.distX + that.distY > 5) {                      // 5 pixels threshold
288
289                         // Lock scroll direction
290                         if (that.distX-3 > that.distY) {
291                                 newY = that.y;
292                                 topDelta = 0;
293                         } else if (that.distY-3 > that.distX) {
294                                 newX = that.x;
295                                 leftDelta = 0;
296                         }
297
298                         that.setPosition(newX, newY);
299                         that.moved = true;
300                         that.directionX = leftDelta > 0 ? -1 : 1;
301                         that.directionY = topDelta > 0 ? -1 : 1;
302                 } else {
303                         that.distX+= Math.abs(leftDelta);
304                         that.distY+= Math.abs(topDelta);
305 //                      that.dist+= Math.abs(leftDelta) + Math.abs(topDelta);
306                 }
307         },
308         
309         touchEnd: function(e) {
310                 if (!this.scrolling) {
311                         return;
312                 }
313
314                 var that = this,
315                         time = e.timeStamp - that.scrollStartTime,
316                         point = isTouch ? e.changedTouches[0] : e,
317                         target, ev,
318                         momentumX, momentumY,
319                         newDuration = 0,
320                         newPositionX = that.x, newPositionY = that.y,
321                         snap;
322
323                 that.scrolling = false;
324
325                 if (!that.moved) {
326                         that.resetPosition();
327
328                         if (isTouch) {
329                                 // Find the last touched element
330                                 target = point.target;
331                                 while (target.nodeType != 1) {
332                                         target = target.parentNode;
333                                 }
334
335                                 // Create the fake event
336                                 ev = document.createEvent('MouseEvents');
337                                 ev.initMouseEvent('click', true, true, e.view, 1,
338                                         point.screenX, point.screenY, point.clientX, point.clientY,
339                                         e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
340                                         0, null);
341                                 ev._fake = true;
342                                 target.dispatchEvent(ev);
343                         }
344
345                         return;
346                 }
347
348                 if (!that.options.snap && time > 250) {                 // Prevent slingshot effect
349                         that.resetPosition();
350                         return;
351                 }
352
353                 if (that.options.momentum) {
354                         momentumX = that.scrollX === true
355                                 ? that.momentum(that.x - that.scrollStartX,
356                                                                 time,
357                                                                 that.options.bounce ? -that.x + that.scrollWidth/5 : -that.x,
358                                                                 that.options.bounce ? that.x + that.scrollerWidth - that.scrollWidth + that.scrollWidth/5 : that.x + that.scrollerWidth - that.scrollWidth)
359                                 : { dist: 0, time: 0 };
360
361                         momentumY = that.scrollY === true
362                                 ? that.momentum(that.y - that.scrollStartY,
363                                                                 time,
364                                                                 that.options.bounce ? -that.y + that.scrollHeight/5 : -that.y,
365                                                                 that.options.bounce ? (that.maxScrollY < 0 ? that.y + that.scrollerHeight - that.scrollHeight : 0) + that.scrollHeight/5 : that.y + that.scrollerHeight - that.scrollHeight)
366                                 : { dist: 0, time: 0 };
367
368                         newDuration = Math.max(Math.max(momentumX.time, momentumY.time), 1);            // The minimum animation length must be 1ms
369                         newPositionX = that.x + momentumX.dist;
370                         newPositionY = that.y + momentumY.dist;
371                 }
372
373                 if (that.options.snap) {
374                         snap = that.snap(newPositionX, newPositionY);
375                         newPositionX = snap.x;
376                         newPositionY = snap.y;
377                         newDuration = Math.max(snap.time, newDuration);
378                 }
379
380                 that.scrollTo(newPositionX, newPositionY, newDuration + 'ms');
381         },
382
383         transitionEnd: function () {
384                 var that = this;
385                 document.removeEventListener('webkitTransitionEnd', that, false);
386                 that.resetPosition();
387         },
388
389         resetPosition: function () {
390                 var that = this,
391                         resetX = that.x,
392                         resetY = that.y;
393
394                 if (that.x >= 0) {
395                         resetX = 0;
396                 } else if (that.x < that.maxScrollX) {
397                         resetX = that.maxScrollX;
398                 }
399
400                 if (that.y >= 0 || that.maxScrollY > 0) {
401                         resetY = 0;
402                 } else if (that.y < that.maxScrollY) {
403                         resetY = that.maxScrollY;
404                 }
405                 
406                 if (resetX != that.x || resetY != that.y) {
407                         that.scrollTo(resetX, resetY);
408                 } else {
409                         if (that.moved) {
410                                 that.onScrollEnd();             // Execute custom code on scroll end
411                                 that.moved = false;
412                         }
413
414                         // Hide the scrollbars
415                         if (that.scrollBarX) {
416                                 that.scrollBarX.hide();
417                         }
418                         if (that.scrollBarY) {
419                                 that.scrollBarY.hide();
420                         }
421                 }
422         },
423         
424         snap: function (x, y) {
425                 var that = this, time;
426
427                 if (that.directionX > 0) {
428                         x = Math.floor(x/that.scrollWidth);
429                 } else if (that.directionX < 0) {
430                         x = Math.ceil(x/that.scrollWidth);
431                 } else {
432                         x = Math.round(x/that.scrollWidth);
433                 }
434                 that.pageX = -x;
435                 x = x * that.scrollWidth;
436                 if (x > 0) {
437                         x = that.pageX = 0;
438                 } else if (x < that.maxScrollX) {
439                         that.pageX = that.maxPageX;
440                         x = that.maxScrollX;
441                 }
442
443                 if (that.directionY > 0) {
444                         y = Math.floor(y/that.scrollHeight);
445                 } else if (that.directionY < 0) {
446                         y = Math.ceil(y/that.scrollHeight);
447                 } else {
448                         y = Math.round(y/that.scrollHeight);
449                 }
450                 that.pageY = -y;
451                 y = y * that.scrollHeight;
452                 if (y > 0) {
453                         y = that.pageY = 0;
454                 } else if (y < that.maxScrollY) {
455                         that.pageY = that.maxPageY;
456                         y = that.maxScrollY;
457                 }
458
459                 // Snap with constant speed (proportional duration)
460                 time = Math.round(Math.max(
461                                 Math.abs(that.x - x) / that.scrollWidth * 500,
462                                 Math.abs(that.y - y) / that.scrollHeight * 500
463                         ));
464                         
465                 return { x: x, y: y, time: time };
466         },
467
468         scrollTo: function (destX, destY, runtime) {
469                 var that = this;
470
471                 if (that.x == destX && that.y == destY) {
472                         that.resetPosition();
473                         return;
474                 }
475
476                 that.moved = true;
477                 that.setTransitionTime(runtime || '350ms');
478                 that.setPosition(destX, destY);
479
480                 if (runtime==='0' || runtime=='0s' || runtime=='0ms') {
481                         that.resetPosition();
482                 } else {
483                         document.addEventListener('webkitTransitionEnd', that, false);  // At the end of the transition check if we are still inside of the boundaries
484                 }
485         },
486         
487         scrollToPage: function (pageX, pageY, runtime) {
488                 var that = this, snap;
489
490                 if (!that.options.snap) {
491                         that.pageX = -Math.round(that.x / that.scrollWidth);
492                         that.pageY = -Math.round(that.y / that.scrollHeight);
493                 }
494
495                 if (pageX == 'next') {
496                         pageX = ++that.pageX;
497                 } else if (pageX == 'prev') {
498                         pageX = --that.pageX;
499                 }
500
501                 if (pageY == 'next') {
502                         pageY = ++that.pageY;
503                 } else if (pageY == 'prev') {
504                         pageY = --that.pageY;
505                 }
506
507                 pageX = -pageX*that.scrollWidth;
508                 pageY = -pageY*that.scrollHeight;
509
510                 snap = that.snap(pageX, pageY);
511                 pageX = snap.x;
512                 pageY = snap.y;
513
514                 that.scrollTo(pageX, pageY, runtime || '500ms');
515         },
516
517         scrollToElement: function (el, runtime) {
518                 el = typeof el == 'object' ? el : this.element.querySelector(el);
519
520                 if (!el) {
521                         return;
522                 }
523
524                 var that = this,
525                         x = that.scrollX ? -el.offsetLeft : 0,
526                         y = that.scrollY ? -el.offsetTop : 0;
527
528                 if (x >= 0) {
529                         x = 0;
530                 } else if (x < that.maxScrollX) {
531                         x = that.maxScrollX;
532                 }
533
534                 if (y >= 0) {
535                         y = 0;
536                 } else if (y < that.maxScrollY) {
537                         y = that.maxScrollY;
538                 }
539
540                 that.scrollTo(x, y, runtime);
541         },
542
543         momentum: function (dist, time, maxDistUpper, maxDistLower) {
544                 var friction = 2.5,
545                         deceleration = 1.2,
546                         speed = Math.abs(dist) / time * 1000,
547                         newDist = speed * speed / friction / 1000,
548                         newTime = 0;
549
550                 // Proportinally reduce speed if we are outside of the boundaries 
551                 if (dist > 0 && newDist > maxDistUpper) {
552                         speed = speed * maxDistUpper / newDist / friction;
553                         newDist = maxDistUpper;
554                 } else if (dist < 0 && newDist > maxDistLower) {
555                         speed = speed * maxDistLower / newDist / friction;
556                         newDist = maxDistLower;
557                 }
558                 
559                 newDist = newDist * (dist < 0 ? -1 : 1);
560                 newTime = speed / deceleration;
561
562                 return { dist: Math.round(newDist), time: Math.round(newTime) };
563         },
564         
565         destroy: function (full) {
566                 var that = this;
567
568                 window.removeEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', that, false);              
569                 that.element.removeEventListener(START_EVENT, that, false);
570                 that.element.removeEventListener(MOVE_EVENT, that, false);
571                 that.element.removeEventListener(END_EVENT, that, false);
572                 document.removeEventListener('webkitTransitionEnd', that, false);
573
574                 if (that.options.checkDOMChanges) {
575                         that.element.removeEventListener('DOMSubtreeModified', that, false);
576                 }
577
578                 if (that.scrollBarX) {
579                         that.scrollBarX = that.scrollBarX.remove();
580                 }
581
582                 if (that.scrollBarY) {
583                         that.scrollBarY = that.scrollBarY.remove();
584                 }
585                 
586                 if (full) {
587                         that.wrapper.parentNode.removeChild(that.wrapper);
588                 }
589                 
590                 return null;
591         }
592 };
593
594 function scrollbar (dir, wrapper, fade, shrink, color) {
595         var that = this,
596                 doc = document;
597         
598         that.dir = dir;
599         that.fade = fade;
600         that.shrink = shrink;
601         that.uid = ++uid;
602
603         // Create main scrollbar
604         that.bar = doc.createElement('div');
605
606         that.bar.style.cssText = 'position:absolute;top:0;left:0;-webkit-transition-timing-function:cubic-bezier(0,0,0.25,1);pointer-events:none;-webkit-transition-duration:0;-webkit-transition-delay:0;-webkit-transition-property:-webkit-transform;z-index:10;background:' + color + ';' +
607                 '-webkit-transform:' + translateOpen + '0,0' + translateClose + ';' +
608                 (dir == 'horizontal' ? '-webkit-border-radius:3px 2px;min-width:6px;min-height:5px' : '-webkit-border-radius:2px 3px;min-width:5px;min-height:6px');
609
610         // Create scrollbar wrapper
611         that.wrapper = doc.createElement('div');
612         that.wrapper.style.cssText = '-webkit-mask:-webkit-canvas(scrollbar' + that.uid + that.dir + ');position:absolute;z-index:10;pointer-events:none;overflow:hidden;opacity:0;-webkit-transition-duration:' + (fade ? '300ms' : '0') + ';-webkit-transition-delay:0;-webkit-transition-property:opacity;' +
613                 (that.dir == 'horizontal' ? 'bottom:2px;left:2px;right:7px;height:5px' : 'top:2px;right:2px;bottom:7px;width:5px;');
614
615         // Add scrollbar to the DOM
616         that.wrapper.appendChild(that.bar);
617         wrapper.appendChild(that.wrapper);
618 }
619
620 scrollbar.prototype = {
621         init: function (scroll, size) {
622                 var that = this,
623                         doc = document,
624                         pi = Math.PI,
625                         ctx;
626
627                 // Create scrollbar mask
628                 if (that.dir == 'horizontal') {
629                         if (that.maxSize != that.wrapper.offsetWidth) {
630                                 that.maxSize = that.wrapper.offsetWidth;
631                                 ctx = doc.getCSSCanvasContext("2d", "scrollbar" + that.uid + that.dir, that.maxSize, 5);
632                                 ctx.fillStyle = "rgb(0,0,0)";
633                                 ctx.beginPath();
634                                 ctx.arc(2.5, 2.5, 2.5, pi/2, -pi/2, false);
635                                 ctx.lineTo(that.maxSize-2.5, 0);
636                                 ctx.arc(that.maxSize-2.5, 2.5, 2.5, -pi/2, pi/2, false);
637                                 ctx.closePath();
638                                 ctx.fill();
639                         }
640                 } else {
641                         if (that.maxSize != that.wrapper.offsetHeight) {
642                                 that.maxSize = that.wrapper.offsetHeight;
643                                 ctx = doc.getCSSCanvasContext("2d", "scrollbar" + that.uid + that.dir, 5, that.maxSize);
644                                 ctx.fillStyle = "rgb(0,0,0)";
645                                 ctx.beginPath();
646                                 ctx.arc(2.5, 2.5, 2.5, pi, 0, false);
647                                 ctx.lineTo(5, that.maxSize-2.5);
648                                 ctx.arc(2.5, that.maxSize-2.5, 2.5, 0, pi, false);
649                                 ctx.closePath();
650                                 ctx.fill();
651                         }
652                 }
653
654                 that.size = Math.max(Math.round(that.maxSize * that.maxSize / size), 6);
655                 that.maxScroll = that.maxSize - that.size;
656                 that.toWrapperProp = that.maxScroll / (scroll - size);
657                 that.bar.style[that.dir == 'horizontal' ? 'width' : 'height'] = that.size + 'px';
658         },
659         
660         setPosition: function (pos) {
661                 var that = this;
662                 
663                 if (that.wrapper.style.opacity != '1') {
664                         that.show();
665                 }
666
667                 pos = Math.round(that.toWrapperProp * pos);
668
669                 if (pos < 0) {
670                         pos = that.shrink ? pos + pos*3 : 0;
671                         if (that.size + pos < 7) {
672                                 pos = -that.size + 6;
673                         }
674                 } else if (pos > that.maxScroll) {
675                         pos = that.shrink ? pos + (pos-that.maxScroll)*3 : that.maxScroll;
676                         if (that.size + that.maxScroll - pos < 7) {
677                                 pos = that.size + that.maxScroll - 6;
678                         }
679                 }
680
681                 pos = that.dir == 'horizontal'
682                         ? translateOpen + pos + 'px,0' + translateClose
683                         : translateOpen + '0,' + pos + 'px' + translateClose;
684
685                 that.bar.style.webkitTransform = pos;
686         },
687
688         show: function () {
689                 if (has3d) {
690                         this.wrapper.style.webkitTransitionDelay = '0';
691                 }
692                 this.wrapper.style.opacity = '1';
693         },
694
695         hide: function () {
696                 if (has3d) {
697                         this.wrapper.style.webkitTransitionDelay = '350ms';
698                 }
699                 this.wrapper.style.opacity = '0';
700         },
701         
702         remove: function () {
703                 this.wrapper.parentNode.removeChild(this.wrapper);
704                 return null;
705         }
706 };
707
708 // Is translate3d compatible?
709 var has3d = ('WebKitCSSMatrix' in window && 'm11' in new WebKitCSSMatrix()),
710         // Device sniffing
711         isIthing = (/iphone|ipad/gi).test(navigator.appVersion),
712         isTouch = ('ontouchstart' in window),
713         // Event sniffing
714         START_EVENT = isTouch ? 'touchstart' : 'mousedown',
715         MOVE_EVENT = isTouch ? 'touchmove' : 'mousemove',
716         END_EVENT = isTouch ? 'touchend' : 'mouseup',
717         // Translate3d helper
718         translateOpen = 'translate' + (has3d ? '3d(' : '('),
719         translateClose = has3d ? ',0)' : ')',
720         // Unique ID
721         uid = 0;
722
723 // Expose iScroll to the world
724 window.iScroll = iScroll;
725 })();