iscroll.js 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  1. /*!
  2. * iScroll v4.2.5 ~ Copyright (c) 2012 Matteo Spinelli, http://cubiq.org
  3. * Released under MIT license, http://cubiq.org/license
  4. */
  5. (function(window, doc){
  6. var m = Math,
  7. dummyStyle = doc.createElement('div').style,
  8. vendor = (function () {
  9. var vendors = 't,webkitT,MozT,msT,OT'.split(','),
  10. t,
  11. i = 0,
  12. l = vendors.length;
  13. for ( ; i < l; i++ ) {
  14. t = vendors[i] + 'ransform';
  15. if ( t in dummyStyle ) {
  16. return vendors[i].substr(0, vendors[i].length - 1);
  17. }
  18. }
  19. return false;
  20. })(),
  21. cssVendor = vendor ? '-' + vendor.toLowerCase() + '-' : '',
  22. // Style properties
  23. transform = prefixStyle('transform'),
  24. transitionProperty = prefixStyle('transitionProperty'),
  25. transitionDuration = prefixStyle('transitionDuration'),
  26. transformOrigin = prefixStyle('transformOrigin'),
  27. transitionTimingFunction = prefixStyle('transitionTimingFunction'),
  28. transitionDelay = prefixStyle('transitionDelay'),
  29. // Browser capabilities
  30. isAndroid = (/android/gi).test(navigator.appVersion),
  31. isIDevice = (/iphone|ipad/gi).test(navigator.appVersion),
  32. isTouchPad = (/hp-tablet/gi).test(navigator.appVersion),
  33. has3d = prefixStyle('perspective') in dummyStyle,
  34. hasTouch = 'ontouchstart' in window && !isTouchPad,
  35. hasTransform = vendor !== false,
  36. hasTransitionEnd = prefixStyle('transition') in dummyStyle,
  37. RESIZE_EV = 'onorientationchange' in window ? 'orientationchange' : 'resize',
  38. START_EV = hasTouch ? 'touchstart' : 'mousedown',
  39. MOVE_EV = hasTouch ? 'touchmove' : 'mousemove',
  40. END_EV = hasTouch ? 'touchend' : 'mouseup',
  41. CANCEL_EV = hasTouch ? 'touchcancel' : 'mouseup',
  42. TRNEND_EV = (function () {
  43. if ( vendor === false ) return false;
  44. var transitionEnd = {
  45. '' : 'transitionend',
  46. 'webkit' : 'webkitTransitionEnd',
  47. 'Moz' : 'transitionend',
  48. 'O' : 'otransitionend',
  49. 'ms' : 'MSTransitionEnd'
  50. };
  51. return transitionEnd[vendor];
  52. })(),
  53. nextFrame = (function() {
  54. return window.requestAnimationFrame ||
  55. window.webkitRequestAnimationFrame ||
  56. window.mozRequestAnimationFrame ||
  57. window.oRequestAnimationFrame ||
  58. window.msRequestAnimationFrame ||
  59. function(callback) { return setTimeout(callback, 1); };
  60. })(),
  61. cancelFrame = (function () {
  62. return window.cancelRequestAnimationFrame ||
  63. window.webkitCancelAnimationFrame ||
  64. window.webkitCancelRequestAnimationFrame ||
  65. window.mozCancelRequestAnimationFrame ||
  66. window.oCancelRequestAnimationFrame ||
  67. window.msCancelRequestAnimationFrame ||
  68. clearTimeout;
  69. })(),
  70. // Helpers
  71. translateZ = has3d ? ' translateZ(0)' : '',
  72. // Constructor
  73. iScroll = function (el, options) {
  74. var that = this,
  75. i;
  76. that.wrapper = typeof el == 'object' ? el : doc.getElementById(el);
  77. that.wrapper.style.overflow = 'hidden';
  78. that.scroller = that.wrapper.children[0];
  79. if(options.top){
  80. that.wrapper.style.top = options.top+"px";
  81. }
  82. if(options.bottom){
  83. that.wrapper.style.bottom = options.bottom+"px";
  84. }
  85. var max = 50;
  86. that.Max = {X:max,Y:max};
  87. // Default options
  88. that.options = {
  89. hScroll: true,
  90. vScroll: true,
  91. x: 0,
  92. y: 0,
  93. bounce: true,
  94. bounceLock: false,
  95. momentum: true,
  96. lockDirection: true,
  97. useTransform: true,
  98. useTransition: false,
  99. topOffset: 0,
  100. checkDOMChanges: false, // Experimental
  101. handleClick: true,
  102. // Scrollbar
  103. hScrollbar: true,
  104. vScrollbar: true,
  105. fixedScrollbar: isAndroid,
  106. hideScrollbar: isIDevice,
  107. fadeScrollbar: isIDevice && has3d,
  108. scrollbarClass: '',
  109. // Zoom
  110. zoom: false,
  111. zoomMin: 1,
  112. zoomMax: 4,
  113. doubleTapZoom: 2,
  114. wheelAction: 'scroll',
  115. // Snap
  116. snap: false,
  117. snapThreshold: 1,
  118. // Events
  119. onRefresh: null,
  120. onBeforeScrollStart: function (e) {
  121. var target = e.target;
  122. while (target.nodeType != 1) {
  123. target = target.parentNode;
  124. }
  125. if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {
  126. e.preventDefault();
  127. }
  128. },
  129. onScrollStart: function(e){
  130. that.startPageX = getPage(e, 'pageX');
  131. that.startPageY = getPage(e, 'pageY');
  132. },
  133. onBeforeScrollMove: null,
  134. onScrollMove: function(e){
  135. var pageX = getPage(e, 'pageX'),
  136. pageY = getPage(e, 'pageY'),
  137. deltaX = pageX - that.startPageX,
  138. deltaY = pageY - that.startPageY;
  139. if(Math.abs(deltaX)> Math.abs(deltaY) && deltaX<-that.Max.X)
  140. controlSwap(e,"l");
  141. if(Math.abs(deltaX)> Math.abs(deltaY) && deltaX>that.Max.X)
  142. controlSwap(e,"r");
  143. if(Math.abs(deltaX)< Math.abs(deltaY) && deltaY<-that.Max.Y)
  144. controlSwap(e,"u");
  145. if(Math.abs(deltaX)< Math.abs(deltaY) && deltaY>that.Max.Y)
  146. controlSwap(e,"d");
  147. },
  148. onBeforeScrollEnd: null,
  149. onScrollEnd: null,
  150. onTouchEnd: null,
  151. onDestroy: null,
  152. onZoomStart: null,
  153. onZoom: null,
  154. onZoomEnd: null
  155. };
  156. var controlSwap = function(e,dir){
  157. var target = e.target;
  158. HT.findPreElm(target,"subTable", 2, function(o){
  159. if(o&&(dir=="u"||dir=="d")){
  160. that.disable();
  161. setTimeout(function(){that.enable();},1500)
  162. }
  163. e.preventDefault();
  164. document.activeElement.blur();
  165. });
  166. }
  167. // User defined options
  168. for (i in options) that.options[i] = options[i];
  169. // Set starting position
  170. that.x = that.options.x;
  171. that.y = that.options.y;
  172. // Normalize options
  173. that.options.useTransform = hasTransform && that.options.useTransform;
  174. that.options.hScrollbar = that.options.hScroll && that.options.hScrollbar;
  175. that.options.vScrollbar = that.options.vScroll && that.options.vScrollbar;
  176. that.options.zoom = that.options.useTransform && that.options.zoom;
  177. that.options.useTransition = hasTransitionEnd && that.options.useTransition;
  178. // Helpers FIX ANDROID BUG!
  179. // translate3d and scale doesn't work together!
  180. // Ignoring 3d ONLY WHEN YOU SET that.options.zoom
  181. if ( that.options.zoom && isAndroid ){
  182. translateZ = '';
  183. }
  184. // Set some default styles
  185. that.scroller.style[transitionProperty] = that.options.useTransform ? cssVendor + 'transform' : 'top left';
  186. that.scroller.style[transitionDuration] = '0';
  187. that.scroller.style[transformOrigin] = '0 0';
  188. if (that.options.useTransition) that.scroller.style[transitionTimingFunction] = 'cubic-bezier(0.33,0.66,0.66,1)';
  189. if (that.options.useTransform) that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px)' + translateZ;
  190. else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px';
  191. if (that.options.useTransition) that.options.fixedScrollbar = true;
  192. if(that.options.pullUpAction || that.options.pullDownAction){
  193. var pullCls = " center c-eee";
  194. if (that.options.pullDownAction) {
  195. var pullDown = document.createElement("div");
  196. var pullDown_id = document.createAttribute("id");
  197. pullDown_id.nodeValue = "pullDown";
  198. pullDown.setAttributeNode(pullDown_id);
  199. var pullDown_class = document.createAttribute("class");
  200. pullDown_class.nodeValue = "uhide";
  201. pullDown.setAttributeNode(pullDown_class);
  202. var pullDownIcon = document.createElement("span");
  203. var pullDownIcon_class = document.createAttribute("class");
  204. pullDownIcon_class.nodeValue = "pullDownIcon";
  205. pullDownIcon.setAttributeNode(pullDownIcon_class);
  206. var pullDownLabel = document.createElement("span");
  207. var pullDownLabel_class = document.createAttribute("class");
  208. pullDownLabel_class.nodeValue = "pullDownLabel";
  209. pullDownLabel.setAttributeNode(pullDownLabel_class);
  210. pullDown.appendChild(pullDownIcon);
  211. pullDown.appendChild(pullDownLabel);
  212. var ul = that.scroller.children[0];
  213. that.scroller.insertBefore(pullDown,ul);
  214. }
  215. if (that.options.pullUpAction) {
  216. var pullUp = document.createElement("div");
  217. var pullUp_id = document.createAttribute("id");
  218. pullUp_id.nodeValue = "pullUp";
  219. pullUp.setAttributeNode(pullUp_id);
  220. var pullUp_class = document.createAttribute("class");
  221. pullUp_class.nodeValue = "uhide";
  222. pullUp.setAttributeNode(pullUp_class);
  223. var pullUpIcon = document.createElement("span");
  224. var pullUpIcon_class = document.createAttribute("class");
  225. pullUpIcon_class.nodeValue = "pullUpIcon";
  226. pullUpIcon.setAttributeNode(pullUpIcon_class);
  227. var pullUpLabel = document.createElement("span");
  228. var pullUpLabel_class = document.createAttribute("class");
  229. pullUpLabel_class.nodeValue = "pullUpLabel";
  230. pullUpLabel.setAttributeNode(pullUpLabel_class);
  231. pullUp.appendChild(pullUpIcon);
  232. pullUp.appendChild(pullUpLabel);
  233. that.scroller.appendChild(pullUp);
  234. }
  235. var pullDownEl = document.getElementById('pullDown');
  236. var pullUpEl = document.getElementById('pullUp');
  237. var pullDownOffset=0;
  238. if(pullDownEl){
  239. pullDownOffset = pullDownEl.offsetHeight;
  240. that.options.topOffset = pullDownOffset;
  241. }
  242. var pullUpOffset = 0;
  243. if(pullUpEl){
  244. pullUpOffset = pullUpEl.offsetHeight;
  245. }
  246. that.options.onRefresh = function (){
  247. if (pullDownEl&&pullDownEl.className.match('loading')) {
  248. pullDownEl.className = "uhide"+pullCls;
  249. pullDownEl.querySelector('.pullDownLabel').innerHTML = '下拉刷新';
  250. } else if (pullUpEl&&pullUpEl.className.match('loading')) {
  251. pullUpEl.className = "uhide"+pullCls;
  252. pullUpEl.querySelector('.pullUpLabel').innerHTML = '上拉刷新';
  253. }
  254. };
  255. var wh = that.wrapper.offsetHeight*0.05;
  256. that.options.onScrollMove = function (){
  257. if (pullDownEl&&this.y > wh && !pullDownEl.className.match('uhide')
  258. && (!pullUpEl || pullUpEl.offsetHeight==0) ) {
  259. pullDownEl.className = 'flip'+pullCls;
  260. pullDownEl.querySelector('.pullDownLabel').innerHTML = '释放刷新';
  261. this.minScrollY = 0;
  262. } else if (pullDownEl&&this.y > 0&&this.y<wh && pullDownEl.className.match('uhide')
  263. && (!pullUpEl || pullUpEl.offsetHeight==0)) {
  264. pullDownEl.className = pullCls;
  265. pullDownEl.querySelector('.pullDownLabel').innerHTML = '下拉刷新';
  266. this.minScrollY = -pullDownOffset;
  267. }
  268. if (pullUpEl && pullUpEl.offsetHeight>0
  269. && (this.maxScrollY<0&&this.y < (this.maxScrollY-pullUpEl.offsetHeight-wh)
  270. || (this.maxScrollY>0&&this.y < -wh))
  271. && !pullUpEl.className.match('uhide')
  272. && (!pullDownEl || pullDownEl.offsetHeight==0))
  273. {
  274. pullUpEl.className = 'flip'+pullCls;
  275. pullUpEl.querySelector('.pullUpLabel').innerHTML = '释放刷新';
  276. //this.maxScrollY = this.maxScrollY;
  277. }
  278. else if (pullUpEl && ((this.maxScrollY<0&&this.y<this.maxScrollY && this.y>this.maxScrollY-wh)
  279. || (this.maxScrollY>0&&this.y<0&&this.y>-wh) )
  280. // && pullUpEl.className.match('uhide')
  281. && (!pullDownEl || pullDownEl.offsetHeight==0))
  282. {
  283. pullUpEl.className = pullCls;
  284. pullUpEl.querySelector('.pullUpLabel').innerHTML = '上拉刷新';
  285. // this.maxScrollY = pullUpOffset;
  286. }
  287. }
  288. that.options.onScrollEnd = function () {
  289. if (pullDownEl&&pullDownEl.offsetHeight>0) {
  290. if(pullDownEl.className.match('flip')){
  291. pullDownEl.className = 'loading'+pullCls;
  292. pullDownEl.querySelector('.pullDownLabel').innerHTML = '请稍后...';
  293. that.options.pullDownAction(); // Execute custom function (ajax call?)
  294. }else{
  295. pullDownEl.className = 'uhide';
  296. }
  297. } else if (pullUpEl&&pullUpEl.offsetHeight>0) {
  298. if (pullUpEl.className.match('flip')) {
  299. pullUpEl.className = 'loading'+pullCls;
  300. pullUpEl.querySelector('.pullUpLabel').innerHTML = '请稍后...';
  301. that.options.pullUpAction(pullUpEl); // Execute custom function (ajax call?)
  302. }else{
  303. pullUpEl.className = 'uhide';
  304. }
  305. }
  306. }
  307. }
  308. that.refresh();
  309. that._bind(RESIZE_EV, window);
  310. that._bind(START_EV);
  311. if (!hasTouch) {
  312. if (that.options.wheelAction != 'none') {
  313. that._bind('DOMMouseScroll');
  314. that._bind('mousewheel');
  315. }
  316. }
  317. if (that.options.checkDOMChanges) that.checkDOMTime = setInterval(function () {
  318. that._checkDOMChanges();
  319. }, 500);
  320. };
  321. // Prototype
  322. iScroll.prototype = {
  323. enabled: true,
  324. x: 0,
  325. y: 0,
  326. steps: [],
  327. scale: 1,
  328. currPageX: 0, currPageY: 0,
  329. pagesX: [], pagesY: [],
  330. aniTime: null,
  331. wheelZoomCount: 0,
  332. handleEvent: function (e) {
  333. var that = this;
  334. switch(e.type) {
  335. case START_EV:
  336. if (!hasTouch && e.button !== 0) return;
  337. that._start(e);
  338. break;
  339. case MOVE_EV: that._move(e); break;
  340. case END_EV:
  341. case CANCEL_EV: that._end(e); break;
  342. case RESIZE_EV: that._resize(); break;
  343. case 'DOMMouseScroll': case 'mousewheel': that._wheel(e); break;
  344. case TRNEND_EV: that._transitionEnd(e); break;
  345. }
  346. },
  347. _checkDOMChanges: function () {
  348. if (this.moved || this.zoomed || this.animating ||
  349. (this.scrollerW == this.scroller.offsetWidth * this.scale && this.scrollerH == this.scroller.offsetHeight * this.scale)) return;
  350. this.refresh();
  351. },
  352. _scrollbar: function (dir) {
  353. var that = this,
  354. bar;
  355. if (!that[dir + 'Scrollbar']) {
  356. if (that[dir + 'ScrollbarWrapper']) {
  357. if (hasTransform) that[dir + 'ScrollbarIndicator'].style[transform] = '';
  358. that[dir + 'ScrollbarWrapper'].parentNode.removeChild(that[dir + 'ScrollbarWrapper']);
  359. that[dir + 'ScrollbarWrapper'] = null;
  360. that[dir + 'ScrollbarIndicator'] = null;
  361. }
  362. return;
  363. }
  364. if (!that[dir + 'ScrollbarWrapper']) {
  365. // Create the scrollbar wrapper
  366. bar = doc.createElement('div');
  367. if (that.options.scrollbarClass) bar.className = that.options.scrollbarClass + dir.toUpperCase();
  368. else bar.style.cssText = 'position:absolute;z-index:100;' + (dir == 'h' ? 'height:7px;bottom:1px;left:2px;right:' + (that.vScrollbar ? '7' : '2') + 'px' : 'width:7px;bottom:' + (that.hScrollbar ? '7' : '2') + 'px;top:2px;right:1px');
  369. bar.style.cssText += ';pointer-events:none;' + cssVendor + 'transition-property:opacity;' + cssVendor + 'transition-duration:' + (that.options.fadeScrollbar ? '350ms' : '0') + ';overflow:hidden;opacity:' + (that.options.hideScrollbar ? '0' : '1');
  370. that.wrapper.appendChild(bar);
  371. that[dir + 'ScrollbarWrapper'] = bar;
  372. // Create the scrollbar indicator
  373. bar = doc.createElement('div');
  374. if (!that.options.scrollbarClass) {
  375. bar.style.cssText = 'position:absolute;z-index:100;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);' + cssVendor + 'background-clip:padding-box;' + cssVendor + 'box-sizing:border-box;' + (dir == 'h' ? 'height:100%' : 'width:100%') + ';' + cssVendor + 'border-radius:3px;border-radius:3px';
  376. }
  377. bar.style.cssText += ';pointer-events:none;' + cssVendor + 'transition-property:' + cssVendor + 'transform;' + cssVendor + 'transition-timing-function:cubic-bezier(0.33,0.66,0.66,1);' + cssVendor + 'transition-duration:0;' + cssVendor + 'transform: translate(0,0)' + translateZ;
  378. if (that.options.useTransition) bar.style.cssText += ';' + cssVendor + 'transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)';
  379. that[dir + 'ScrollbarWrapper'].appendChild(bar);
  380. that[dir + 'ScrollbarIndicator'] = bar;
  381. }
  382. if (dir == 'h') {
  383. that.hScrollbarSize = that.hScrollbarWrapper.clientWidth;
  384. that.hScrollbarIndicatorSize = m.max(m.round(that.hScrollbarSize * that.hScrollbarSize / that.scrollerW), 8);
  385. that.hScrollbarIndicator.style.width = that.hScrollbarIndicatorSize + 'px';
  386. that.hScrollbarMaxScroll = that.hScrollbarSize - that.hScrollbarIndicatorSize;
  387. that.hScrollbarProp = that.hScrollbarMaxScroll / that.maxScrollX;
  388. } else {
  389. that.vScrollbarSize = that.vScrollbarWrapper.clientHeight;
  390. that.vScrollbarIndicatorSize = m.max(m.round(that.vScrollbarSize * that.vScrollbarSize / that.scrollerH), 8);
  391. that.vScrollbarIndicator.style.height = that.vScrollbarIndicatorSize + 'px';
  392. that.vScrollbarMaxScroll = that.vScrollbarSize - that.vScrollbarIndicatorSize;
  393. that.vScrollbarProp = that.vScrollbarMaxScroll / that.maxScrollY;
  394. }
  395. // Reset position
  396. that._scrollbarPos(dir, true);
  397. },
  398. _resize: function () {
  399. var that = this;
  400. setTimeout(function () {
  401. that.refresh();
  402. }, 200);
  403. },
  404. _pos: function (x, y) {
  405. if (this.zoomed) return;
  406. x = this.hScroll ? x : 0;
  407. y = this.vScroll ? y : 0;
  408. if (this.options.useTransform) {
  409. this.scroller.style[transform] = 'translate(' + x + 'px,' + y + 'px) scale(' + this.scale + ')' + translateZ;
  410. } else {
  411. x = m.round(x);
  412. y = m.round(y);
  413. this.scroller.style.left = x + 'px';
  414. this.scroller.style.top = y + 'px';
  415. }
  416. this.x = x;
  417. this.y = y;
  418. this._scrollbarPos('h');
  419. this._scrollbarPos('v');
  420. },
  421. _scrollbarPos: function (dir, hidden) {
  422. var that = this,
  423. pos = dir == 'h' ? that.x : that.y,
  424. size;
  425. if (!that[dir + 'Scrollbar']) return;
  426. pos = that[dir + 'ScrollbarProp'] * pos;
  427. if (pos < 0) {
  428. if (!that.options.fixedScrollbar) {
  429. size = that[dir + 'ScrollbarIndicatorSize'] + m.round(pos * 3);
  430. if (size < 8) size = 8;
  431. that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px';
  432. }
  433. pos = 0;
  434. } else if (pos > that[dir + 'ScrollbarMaxScroll']) {
  435. if (!that.options.fixedScrollbar) {
  436. size = that[dir + 'ScrollbarIndicatorSize'] - m.round((pos - that[dir + 'ScrollbarMaxScroll']) * 3);
  437. if (size < 8) size = 8;
  438. that[dir + 'ScrollbarIndicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px';
  439. pos = that[dir + 'ScrollbarMaxScroll'] + (that[dir + 'ScrollbarIndicatorSize'] - size);
  440. } else {
  441. pos = that[dir + 'ScrollbarMaxScroll'];
  442. }
  443. }
  444. that[dir + 'ScrollbarWrapper'].style[transitionDelay] = '0';
  445. that[dir + 'ScrollbarWrapper'].style.opacity = hidden && that.options.hideScrollbar ? '0' : '1';
  446. that[dir + 'ScrollbarIndicator'].style[transform] = 'translate(' + (dir == 'h' ? pos + 'px,0)' : '0,' + pos + 'px)') + translateZ;
  447. },
  448. _start: function (e) {
  449. var that = this,
  450. point = hasTouch ? e.touches[0] : e,
  451. matrix, x, y,
  452. c1, c2;
  453. if (!that.enabled) return;
  454. if (that.options.onBeforeScrollStart) that.options.onBeforeScrollStart.call(that, e);
  455. if (that.options.useTransition || that.options.zoom) that._transitionTime(0);
  456. that.moved = false;
  457. that.animating = false;
  458. that.zoomed = false;
  459. that.distX = 0;
  460. that.distY = 0;
  461. that.absDistX = 0;
  462. that.absDistY = 0;
  463. that.dirX = 0;
  464. that.dirY = 0;
  465. // Gesture start
  466. if (that.options.zoom && hasTouch && e.touches.length > 1) {
  467. c1 = m.abs(e.touches[0].pageX-e.touches[1].pageX);
  468. c2 = m.abs(e.touches[0].pageY-e.touches[1].pageY);
  469. that.touchesDistStart = m.sqrt(c1 * c1 + c2 * c2);
  470. that.originX = m.abs(e.touches[0].pageX + e.touches[1].pageX - that.wrapperOffsetLeft * 2) / 2 - that.x;
  471. that.originY = m.abs(e.touches[0].pageY + e.touches[1].pageY - that.wrapperOffsetTop * 2) / 2 - that.y;
  472. if (that.options.onZoomStart) that.options.onZoomStart.call(that, e);
  473. }
  474. if (that.options.momentum) {
  475. if (that.options.useTransform) {
  476. // Very lame general purpose alternative to CSSMatrix
  477. matrix = getComputedStyle(that.scroller, null)[transform].replace(/[^0-9\-.,]/g, '').split(',');
  478. x = +(matrix[12] || matrix[4]);
  479. y = +(matrix[13] || matrix[5]);
  480. } else {
  481. x = +getComputedStyle(that.scroller, null).left.replace(/[^0-9-]/g, '');
  482. y = +getComputedStyle(that.scroller, null).top.replace(/[^0-9-]/g, '');
  483. }
  484. if (x != that.x || y != that.y) {
  485. if (that.options.useTransition) that._unbind(TRNEND_EV);
  486. else cancelFrame(that.aniTime);
  487. that.steps = [];
  488. that._pos(x, y);
  489. if (that.options.onScrollEnd) that.options.onScrollEnd.call(that);
  490. }
  491. }
  492. that.absStartX = that.x; // Needed by snap threshold
  493. that.absStartY = that.y;
  494. that.startX = that.x;
  495. that.startY = that.y;
  496. that.pointX = point.pageX;
  497. that.pointY = point.pageY;
  498. that.startTime = e.timeStamp || Date.now();
  499. if (that.options.onScrollStart) that.options.onScrollStart.call(that, e);
  500. that._bind(MOVE_EV, window);
  501. that._bind(END_EV, window);
  502. that._bind(CANCEL_EV, window);
  503. },
  504. _move: function (e) {
  505. var that = this,
  506. point = hasTouch ? e.touches[0] : e,
  507. deltaX = point.pageX - that.pointX,
  508. deltaY = point.pageY - that.pointY,
  509. newX = that.x + deltaX,
  510. newY = that.y + deltaY,
  511. c1, c2, scale,
  512. timestamp = e.timeStamp || Date.now();
  513. if (that.options.onBeforeScrollMove) that.options.onBeforeScrollMove.call(that, e);
  514. // Zoom
  515. if (that.options.zoom && hasTouch && e.touches.length > 1) {
  516. c1 = m.abs(e.touches[0].pageX - e.touches[1].pageX);
  517. c2 = m.abs(e.touches[0].pageY - e.touches[1].pageY);
  518. that.touchesDist = m.sqrt(c1*c1+c2*c2);
  519. that.zoomed = true;
  520. scale = 1 / that.touchesDistStart * that.touchesDist * this.scale;
  521. if (scale < that.options.zoomMin) scale = 0.5 * that.options.zoomMin * Math.pow(2.0, scale / that.options.zoomMin);
  522. else if (scale > that.options.zoomMax) scale = 2.0 * that.options.zoomMax * Math.pow(0.5, that.options.zoomMax / scale);
  523. that.lastScale = scale / this.scale;
  524. newX = this.originX - this.originX * that.lastScale + this.x;
  525. newY = this.originY - this.originY * that.lastScale + this.y;
  526. this.scroller.style[transform] = 'translate(' + newX + 'px,' + newY + 'px) scale(' + scale + ')' + translateZ;
  527. if (that.options.onZoom) that.options.onZoom.call(that, e);
  528. return;
  529. }
  530. that.pointX = point.pageX;
  531. that.pointY = point.pageY;
  532. // Slow down if outside of the boundaries
  533. if (newX > 0 || newX < that.maxScrollX) {
  534. newX = that.options.bounce ? that.x + (deltaX / 2) : newX >= 0 || that.maxScrollX >= 0 ? 0 : that.maxScrollX;
  535. }
  536. if (newY > that.minScrollY || newY < that.maxScrollY) {
  537. newY = that.options.bounce ? that.y + (deltaY / 2) : newY >= that.minScrollY || that.maxScrollY >= 0 ? that.minScrollY : that.maxScrollY;
  538. }
  539. that.distX += deltaX;
  540. that.distY += deltaY;
  541. that.absDistX = m.abs(that.distX);
  542. that.absDistY = m.abs(that.distY);
  543. if (that.absDistX < 6 && that.absDistY < 6) {
  544. return;
  545. }
  546. // Lock direction
  547. if (that.options.lockDirection) {
  548. if (that.absDistX > that.absDistY + 5) {
  549. newY = that.y;
  550. deltaY = 0;
  551. } else if (that.absDistY > that.absDistX + 5) {
  552. newX = that.x;
  553. deltaX = 0;
  554. }
  555. }
  556. that.moved = true;
  557. that._pos(newX, newY);
  558. that.dirX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
  559. that.dirY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
  560. if (timestamp - that.startTime > 300) {
  561. that.startTime = timestamp;
  562. that.startX = that.x;
  563. that.startY = that.y;
  564. }
  565. if (that.options.onScrollMove) that.options.onScrollMove.call(that, e);
  566. },
  567. _end: function (e) {
  568. if (hasTouch && e.touches.length !== 0) return;
  569. var that = this,
  570. point = hasTouch ? e.changedTouches[0] : e,
  571. target, ev,
  572. momentumX = { dist:0, time:0 },
  573. momentumY = { dist:0, time:0 },
  574. duration = (e.timeStamp || Date.now()) - that.startTime,
  575. newPosX = that.x,
  576. newPosY = that.y,
  577. distX, distY,
  578. newDuration,
  579. snap,
  580. scale;
  581. that._unbind(MOVE_EV, window);
  582. that._unbind(END_EV, window);
  583. that._unbind(CANCEL_EV, window);
  584. if (that.options.onBeforeScrollEnd) that.options.onBeforeScrollEnd.call(that, e);
  585. if (that.zoomed) {
  586. scale = that.scale * that.lastScale;
  587. scale = Math.max(that.options.zoomMin, scale);
  588. scale = Math.min(that.options.zoomMax, scale);
  589. that.lastScale = scale / that.scale;
  590. that.scale = scale;
  591. that.x = that.originX - that.originX * that.lastScale + that.x;
  592. that.y = that.originY - that.originY * that.lastScale + that.y;
  593. that.scroller.style[transitionDuration] = '200ms';
  594. that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px) scale(' + that.scale + ')' + translateZ;
  595. that.zoomed = false;
  596. that.refresh();
  597. if (that.options.onZoomEnd) that.options.onZoomEnd.call(that, e);
  598. return;
  599. }
  600. if (!that.moved) {
  601. if (hasTouch) {
  602. if (that.doubleTapTimer && that.options.zoom) {
  603. // Double tapped
  604. clearTimeout(that.doubleTapTimer);
  605. that.doubleTapTimer = null;
  606. if (that.options.onZoomStart) that.options.onZoomStart.call(that, e);
  607. that.zoom(that.pointX, that.pointY, that.scale == 1 ? that.options.doubleTapZoom : 1);
  608. if (that.options.onZoomEnd) {
  609. setTimeout(function() {
  610. that.options.onZoomEnd.call(that, e);
  611. }, 200); // 200 is default zoom duration
  612. }
  613. } else if (this.options.handleClick) {
  614. that.doubleTapTimer = setTimeout(function () {
  615. that.doubleTapTimer = null;
  616. // Find the last touched element
  617. target = point.target;
  618. while (target.nodeType != 1) target = target.parentNode;
  619. if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {
  620. ev = doc.createEvent('MouseEvents');
  621. ev.initMouseEvent('click', true, true, e.view, 1,
  622. point.screenX, point.screenY, point.clientX, point.clientY,
  623. e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
  624. 0, null);
  625. ev._fake = true;
  626. target.dispatchEvent(ev);
  627. }
  628. }, that.options.zoom ? 250 : 0);
  629. }
  630. }
  631. that._resetPos(400);
  632. if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
  633. return;
  634. }
  635. if (duration < 300 && that.options.momentum) {
  636. momentumX = newPosX ? that._momentum(newPosX - that.startX, duration, -that.x, that.scrollerW - that.wrapperW + that.x, that.options.bounce ? that.wrapperW : 0) : momentumX;
  637. momentumY = newPosY ? that._momentum(newPosY - that.startY, duration, -that.y, (that.maxScrollY < 0 ? that.scrollerH - that.wrapperH + that.y - that.minScrollY : 0), that.options.bounce ? that.wrapperH : 0) : momentumY;
  638. newPosX = that.x + momentumX.dist;
  639. newPosY = that.y + momentumY.dist;
  640. if ((that.x > 0 && newPosX > 0) || (that.x < that.maxScrollX && newPosX < that.maxScrollX)) momentumX = { dist:0, time:0 };
  641. if ((that.y > that.minScrollY && newPosY > that.minScrollY) || (that.y < that.maxScrollY && newPosY < that.maxScrollY)) momentumY = { dist:0, time:0 };
  642. }
  643. if (momentumX.dist || momentumY.dist) {
  644. newDuration = m.max(m.max(momentumX.time, momentumY.time), 10);
  645. // Do we need to snap?
  646. if (that.options.snap) {
  647. distX = newPosX - that.absStartX;
  648. distY = newPosY - that.absStartY;
  649. if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) { that.scrollTo(that.absStartX, that.absStartY, 200); }
  650. else {
  651. snap = that._snap(newPosX, newPosY);
  652. newPosX = snap.x;
  653. newPosY = snap.y;
  654. newDuration = m.max(snap.time, newDuration);
  655. }
  656. }
  657. that.scrollTo(m.round(newPosX), m.round(newPosY), newDuration);
  658. if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
  659. return;
  660. }
  661. // Do we need to snap?
  662. if (that.options.snap) {
  663. distX = newPosX - that.absStartX;
  664. distY = newPosY - that.absStartY;
  665. if (m.abs(distX) < that.options.snapThreshold && m.abs(distY) < that.options.snapThreshold) that.scrollTo(that.absStartX, that.absStartY, 200);
  666. else {
  667. snap = that._snap(that.x, that.y);
  668. if (snap.x != that.x || snap.y != that.y) that.scrollTo(snap.x, snap.y, snap.time);
  669. }
  670. if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
  671. return;
  672. }
  673. that._resetPos(200);
  674. if (that.options.onTouchEnd) that.options.onTouchEnd.call(that, e);
  675. },
  676. _resetPos: function (time) {
  677. var that = this,
  678. resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x,
  679. resetY = that.y >= that.minScrollY || that.maxScrollY > 0 ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y;
  680. if (resetX == that.x && resetY == that.y) {
  681. if (that.moved) {
  682. that.moved = false;
  683. if (that.options.onScrollEnd) that.options.onScrollEnd.call(that); // Execute custom code on scroll end
  684. }
  685. if (that.hScrollbar && that.options.hideScrollbar) {
  686. if (vendor == 'webkit') that.hScrollbarWrapper.style[transitionDelay] = '300ms';
  687. that.hScrollbarWrapper.style.opacity = '0';
  688. }
  689. if (that.vScrollbar && that.options.hideScrollbar) {
  690. if (vendor == 'webkit') that.vScrollbarWrapper.style[transitionDelay] = '300ms';
  691. that.vScrollbarWrapper.style.opacity = '0';
  692. }
  693. return;
  694. }
  695. that.scrollTo(resetX, resetY, time || 0);
  696. },
  697. _wheel: function (e) {
  698. var that = this,
  699. wheelDeltaX, wheelDeltaY,
  700. deltaX, deltaY,
  701. deltaScale;
  702. if ('wheelDeltaX' in e) {
  703. wheelDeltaX = e.wheelDeltaX / 12;
  704. wheelDeltaY = e.wheelDeltaY / 12;
  705. } else if('wheelDelta' in e) {
  706. wheelDeltaX = wheelDeltaY = e.wheelDelta / 12;
  707. } else if ('detail' in e) {
  708. wheelDeltaX = wheelDeltaY = -e.detail * 3;
  709. } else {
  710. return;
  711. }
  712. if (that.options.wheelAction == 'zoom') {
  713. deltaScale = that.scale * Math.pow(2, 1/3 * (wheelDeltaY ? wheelDeltaY / Math.abs(wheelDeltaY) : 0));
  714. if (deltaScale < that.options.zoomMin) deltaScale = that.options.zoomMin;
  715. if (deltaScale > that.options.zoomMax) deltaScale = that.options.zoomMax;
  716. if (deltaScale != that.scale) {
  717. if (!that.wheelZoomCount && that.options.onZoomStart) that.options.onZoomStart.call(that, e);
  718. that.wheelZoomCount++;
  719. that.zoom(e.pageX, e.pageY, deltaScale, 400);
  720. setTimeout(function() {
  721. that.wheelZoomCount--;
  722. if (!that.wheelZoomCount && that.options.onZoomEnd) that.options.onZoomEnd.call(that, e);
  723. }, 400);
  724. }
  725. return;
  726. }
  727. deltaX = that.x + wheelDeltaX;
  728. deltaY = that.y + wheelDeltaY;
  729. if (deltaX > 0) deltaX = 0;
  730. else if (deltaX < that.maxScrollX) deltaX = that.maxScrollX;
  731. if (deltaY > that.minScrollY) deltaY = that.minScrollY;
  732. else if (deltaY < that.maxScrollY) deltaY = that.maxScrollY;
  733. if (that.maxScrollY < 0) {
  734. that.scrollTo(deltaX, deltaY, 0);
  735. }
  736. },
  737. _transitionEnd: function (e) {
  738. var that = this;
  739. if (e.target != that.scroller) return;
  740. that._unbind(TRNEND_EV);
  741. that._startAni();
  742. },
  743. /**
  744. *
  745. * Utilities
  746. *
  747. */
  748. _startAni: function () {
  749. var that = this,
  750. startX = that.x, startY = that.y,
  751. startTime = Date.now(),
  752. step, easeOut,
  753. animate;
  754. if (that.animating) return;
  755. if (!that.steps.length) {
  756. that._resetPos(400);
  757. return;
  758. }
  759. step = that.steps.shift();
  760. if (step.x == startX && step.y == startY) step.time = 0;
  761. that.animating = true;
  762. that.moved = true;
  763. if (that.options.useTransition) {
  764. that._transitionTime(step.time);
  765. that._pos(step.x, step.y);
  766. that.animating = false;
  767. if (step.time) that._bind(TRNEND_EV);
  768. else that._resetPos(0);
  769. return;
  770. }
  771. animate = function () {
  772. var now = Date.now(),
  773. newX, newY;
  774. if (now >= startTime + step.time) {
  775. that._pos(step.x, step.y);
  776. that.animating = false;
  777. if (that.options.onAnimationEnd) that.options.onAnimationEnd.call(that); // Execute custom code on animation end
  778. that._startAni();
  779. return;
  780. }
  781. now = (now - startTime) / step.time - 1;
  782. easeOut = m.sqrt(1 - now * now);
  783. newX = (step.x - startX) * easeOut + startX;
  784. newY = (step.y - startY) * easeOut + startY;
  785. that._pos(newX, newY);
  786. if (that.animating) that.aniTime = nextFrame(animate);
  787. };
  788. animate();
  789. },
  790. _transitionTime: function (time) {
  791. time += 'ms';
  792. this.scroller.style[transitionDuration] = time;
  793. if (this.hScrollbar) this.hScrollbarIndicator.style[transitionDuration] = time;
  794. if (this.vScrollbar) this.vScrollbarIndicator.style[transitionDuration] = time;
  795. },
  796. _momentum: function (dist, time, maxDistUpper, maxDistLower, size) {
  797. var deceleration = 0.0006,
  798. speed = m.abs(dist) / time,
  799. newDist = (speed * speed) / (2 * deceleration),
  800. newTime = 0, outsideDist = 0;
  801. // Proportinally reduce speed if we are outside of the boundaries
  802. if (dist > 0 && newDist > maxDistUpper) {
  803. outsideDist = size / (6 / (newDist / speed * deceleration));
  804. maxDistUpper = maxDistUpper + outsideDist;
  805. speed = speed * maxDistUpper / newDist;
  806. newDist = maxDistUpper;
  807. } else if (dist < 0 && newDist > maxDistLower) {
  808. outsideDist = size / (6 / (newDist / speed * deceleration));
  809. maxDistLower = maxDistLower + outsideDist;
  810. speed = speed * maxDistLower / newDist;
  811. newDist = maxDistLower;
  812. }
  813. newDist = newDist * (dist < 0 ? -1 : 1);
  814. newTime = speed / deceleration;
  815. return { dist: newDist, time: m.round(newTime) };
  816. },
  817. _offset: function (el) {
  818. var left = -el.offsetLeft,
  819. top = -el.offsetTop;
  820. while (el = el.offsetParent) {
  821. left -= el.offsetLeft;
  822. top -= el.offsetTop;
  823. }
  824. if (el != this.wrapper) {
  825. left *= this.scale;
  826. top *= this.scale;
  827. }
  828. return { left: left, top: top };
  829. },
  830. _snap: function (x, y) {
  831. var that = this,
  832. i, l,
  833. page, time,
  834. sizeX, sizeY;
  835. // Check page X
  836. page = that.pagesX.length - 1;
  837. for (i=0, l=that.pagesX.length; i<l; i++) {
  838. if (x >= that.pagesX[i]) {
  839. page = i;
  840. break;
  841. }
  842. }
  843. if (page == that.currPageX && page > 0 && that.dirX < 0) page--;
  844. x = that.pagesX[page];
  845. sizeX = m.abs(x - that.pagesX[that.currPageX]);
  846. sizeX = sizeX ? m.abs(that.x - x) / sizeX * 500 : 0;
  847. that.currPageX = page;
  848. // Check page Y
  849. page = that.pagesY.length-1;
  850. for (i=0; i<page; i++) {
  851. if (y >= that.pagesY[i]) {
  852. page = i;
  853. break;
  854. }
  855. }
  856. if (page == that.currPageY && page > 0 && that.dirY < 0) page--;
  857. y = that.pagesY[page];
  858. sizeY = m.abs(y - that.pagesY[that.currPageY]);
  859. sizeY = sizeY ? m.abs(that.y - y) / sizeY * 500 : 0;
  860. that.currPageY = page;
  861. // Snap with constant speed (proportional duration)
  862. time = m.round(m.max(sizeX, sizeY)) || 200;
  863. return { x: x, y: y, time: time };
  864. },
  865. _bind: function (type, el, bubble) {
  866. (el || this.scroller).addEventListener(type, this, !!bubble);
  867. },
  868. _unbind: function (type, el, bubble) {
  869. (el || this.scroller).removeEventListener(type, this, !!bubble);
  870. },
  871. /**
  872. *
  873. * Public methods
  874. *
  875. */
  876. destroy: function () {
  877. var that = this;
  878. that.scroller.style[transform] = '';
  879. // Remove the scrollbars
  880. that.hScrollbar = false;
  881. that.vScrollbar = false;
  882. that._scrollbar('h');
  883. that._scrollbar('v');
  884. // Remove the event listeners
  885. that._unbind(RESIZE_EV, window);
  886. that._unbind(START_EV);
  887. that._unbind(MOVE_EV, window);
  888. that._unbind(END_EV, window);
  889. that._unbind(CANCEL_EV, window);
  890. if (!that.options.hasTouch) {
  891. that._unbind('DOMMouseScroll');
  892. that._unbind('mousewheel');
  893. }
  894. if (that.options.useTransition) that._unbind(TRNEND_EV);
  895. if (that.options.checkDOMChanges) clearInterval(that.checkDOMTime);
  896. if (that.options.onDestroy) that.options.onDestroy.call(that);
  897. },
  898. refresh: function () {
  899. var that = this,
  900. offset,
  901. i, l,
  902. els,
  903. pos = 0,
  904. page = 0;
  905. if (that.scale < that.options.zoomMin) that.scale = that.options.zoomMin;
  906. that.wrapperW = that.wrapper.clientWidth || 1;
  907. that.wrapperH = that.wrapper.clientHeight || 1;
  908. that.minScrollY = -that.options.topOffset || 0;
  909. that.scrollerW = m.round(that.scroller.offsetWidth * that.scale);
  910. that.scrollerH = m.round((that.scroller.offsetHeight + that.minScrollY) * that.scale);
  911. that.maxScrollX = that.wrapperW - that.scrollerW;
  912. that.maxScrollY = that.wrapperH - that.scrollerH + that.minScrollY;
  913. that.dirX = 0;
  914. that.dirY = 0;
  915. if (that.options.onRefresh) that.options.onRefresh.call(that);
  916. that.hScroll = that.options.hScroll && that.maxScrollX < 0;
  917. that.vScroll = that.options.vScroll && (!that.options.bounceLock && !that.hScroll || that.scrollerH > that.wrapperH);
  918. that.hScrollbar = that.hScroll && that.options.hScrollbar;
  919. that.vScrollbar = that.vScroll && that.options.vScrollbar && that.scrollerH > that.wrapperH;
  920. offset = that._offset(that.wrapper);
  921. that.wrapperOffsetLeft = -offset.left;
  922. that.wrapperOffsetTop = -offset.top;
  923. // Prepare snap
  924. // if (typeof that.options.snap == 'string') {
  925. // that.pagesX = [];
  926. // that.pagesY = [];
  927. // els = that.scroller.querySelectorAll(that.options.snap);
  928. // for (i=0, l=els.length; i<l; i++) {
  929. // pos = that._offset(els[i]);
  930. // pos.left += that.wrapperOffsetLeft;
  931. // pos.top += that.wrapperOffsetTop;
  932. // that.pagesX[i] = pos.left < that.maxScrollX ? that.maxScrollX : pos.left * that.scale;
  933. // that.pagesY[i] = pos.top < that.maxScrollY ? that.maxScrollY : pos.top * that.scale;
  934. // }
  935. // } else if (that.options.snap) {
  936. // that.pagesX = [];
  937. // while (pos >= that.maxScrollX) {
  938. // that.pagesX[page] = pos;
  939. // pos = pos - that.wrapperW;
  940. // page++;
  941. // }
  942. // if (that.maxScrollX%that.wrapperW) that.pagesX[that.pagesX.length] = that.maxScrollX - that.pagesX[that.pagesX.length-1] + that.pagesX[that.pagesX.length-1];
  943. //
  944. // pos = 0;
  945. // page = 0;
  946. // that.pagesY = [];
  947. // while (pos >= that.maxScrollY) {
  948. // that.pagesY[page] = pos;
  949. // pos = pos - that.wrapperH;
  950. // page++;
  951. // }
  952. // if (that.maxScrollY%that.wrapperH) that.pagesY[that.pagesY.length] = that.maxScrollY - that.pagesY[that.pagesY.length-1] + that.pagesY[that.pagesY.length-1];
  953. // }
  954. // Prepare the scrollbars
  955. that._scrollbar('h');
  956. that._scrollbar('v');
  957. if (!that.zoomed) {
  958. that.scroller.style[transitionDuration] = '0';
  959. that._resetPos(400);
  960. }
  961. },
  962. scrollTo: function (x, y, time, relative) {
  963. var that = this,
  964. step = x,
  965. i, l;
  966. that.stop();
  967. if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }];
  968. for (i=0, l=step.length; i<l; i++) {
  969. if (step[i].relative) { step[i].x = that.x - step[i].x; step[i].y = that.y - step[i].y; }
  970. that.steps.push({ x: step[i].x, y: step[i].y, time: step[i].time || 0 });
  971. }
  972. that._startAni();
  973. },
  974. scrollToElement: function (el, time) {
  975. var that = this, pos;
  976. el = el.nodeType ? el : that.scroller.querySelector(el);
  977. if (!el) return;
  978. pos = that._offset(el);
  979. pos.left += that.wrapperOffsetLeft;
  980. pos.top += that.wrapperOffsetTop;
  981. pos.left = pos.left > 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left;
  982. pos.top = pos.top > that.minScrollY ? that.minScrollY : pos.top < that.maxScrollY ? that.maxScrollY : pos.top;
  983. time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time;
  984. that.scrollTo(pos.left, pos.top, time);
  985. },
  986. scrollToPage: function (pageX, pageY, time) {
  987. var that = this, x, y;
  988. time = time === undefined ? 400 : time;
  989. if (that.options.onScrollStart) that.options.onScrollStart.call(that);
  990. if (that.options.snap) {
  991. pageX = pageX == 'next' ? that.currPageX+1 : pageX == 'prev' ? that.currPageX-1 : pageX;
  992. pageY = pageY == 'next' ? that.currPageY+1 : pageY == 'prev' ? that.currPageY-1 : pageY;
  993. pageX = pageX < 0 ? 0 : pageX > that.pagesX.length-1 ? that.pagesX.length-1 : pageX;
  994. pageY = pageY < 0 ? 0 : pageY > that.pagesY.length-1 ? that.pagesY.length-1 : pageY;
  995. that.currPageX = pageX;
  996. that.currPageY = pageY;
  997. x = that.pagesX[pageX];
  998. y = that.pagesY[pageY];
  999. } else {
  1000. x = -that.wrapperW * pageX;
  1001. y = -that.wrapperH * pageY;
  1002. if (x < that.maxScrollX) x = that.maxScrollX;
  1003. if (y < that.maxScrollY) y = that.maxScrollY;
  1004. }
  1005. that.scrollTo(x, y, time);
  1006. },
  1007. disable: function () {
  1008. this.stop();
  1009. this._resetPos(0);
  1010. this.enabled = false;
  1011. // If disabled after touchstart we make sure that there are no left over events
  1012. this._unbind(MOVE_EV, window);
  1013. this._unbind(END_EV, window);
  1014. this._unbind(CANCEL_EV, window);
  1015. },
  1016. enable: function () {
  1017. this.enabled = true;
  1018. },
  1019. stop: function () {
  1020. if (this.options.useTransition) this._unbind(TRNEND_EV);
  1021. else cancelFrame(this.aniTime);
  1022. this.steps = [];
  1023. this.moved = false;
  1024. this.animating = false;
  1025. },
  1026. zoom: function (x, y, scale, time) {
  1027. var that = this,
  1028. relScale = scale / that.scale;
  1029. if (!that.options.useTransform) return;
  1030. that.zoomed = true;
  1031. time = time === undefined ? 200 : time;
  1032. x = x - that.wrapperOffsetLeft - that.x;
  1033. y = y - that.wrapperOffsetTop - that.y;
  1034. that.x = x - x * relScale + that.x;
  1035. that.y = y - y * relScale + that.y;
  1036. that.scale = scale;
  1037. that.refresh();
  1038. that.x = that.x > 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x;
  1039. that.y = that.y > that.minScrollY ? that.minScrollY : that.y < that.maxScrollY ? that.maxScrollY : that.y;
  1040. that.scroller.style[transitionDuration] = time + 'ms';
  1041. that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px) scale(' + scale + ')' + translateZ;
  1042. that.zoomed = false;
  1043. },
  1044. isReady: function () {
  1045. return !this.moved && !this.zoomed && !this.animating;
  1046. }
  1047. };
  1048. function prefixStyle (style) {
  1049. if ( vendor === '' ) return style;
  1050. style = style.charAt(0).toUpperCase() + style.substr(1);
  1051. return vendor + style;
  1052. }
  1053. dummyStyle = null; // for the sake of it
  1054. if (typeof exports !== 'undefined') exports.iScroll = iScroll;
  1055. else window.iScroll = iScroll;
  1056. })(window, document);