iscroll5.js 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170
  1. /*! iScroll v5.2.0-snapshot ~ (c) 2008-2017 Matteo Spinelli ~ http://cubiq.org/license */
  2. (function (window, document, Math) {
  3. var rAF = window.requestAnimationFrame ||
  4. window.webkitRequestAnimationFrame ||
  5. window.mozRequestAnimationFrame ||
  6. window.oRequestAnimationFrame ||
  7. window.msRequestAnimationFrame ||
  8. function (callback) { window.setTimeout(callback, 1000 / 60); };
  9. var utils = (function () {
  10. var me = {};
  11. var _elementStyle = document.createElement('div').style;
  12. var _vendor = (function () {
  13. var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
  14. transform,
  15. i = 0,
  16. l = vendors.length;
  17. for ( ; i < l; i++ ) {
  18. transform = vendors[i] + 'ransform';
  19. if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
  20. }
  21. return false;
  22. })();
  23. function _prefixStyle (style) {
  24. if ( _vendor === false ) return false;
  25. if ( _vendor === '' ) return style;
  26. return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
  27. }
  28. me.getTime = Date.now || function getTime () { return new Date().getTime(); };
  29. me.extend = function (target, obj) {
  30. for ( var i in obj ) {
  31. target[i] = obj[i];
  32. }
  33. };
  34. me.addEvent = function (el, type, fn, capture) {
  35. el.addEventListener(type, fn, !!capture);
  36. };
  37. me.removeEvent = function (el, type, fn, capture) {
  38. el.removeEventListener(type, fn, !!capture);
  39. };
  40. me.prefixPointerEvent = function (pointerEvent) {
  41. return window.MSPointerEvent ?
  42. 'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8):
  43. pointerEvent;
  44. };
  45. me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
  46. var distance = current - start,
  47. speed = Math.abs(distance) / time,
  48. destination,
  49. duration;
  50. deceleration = deceleration === undefined ? 0.0006 : deceleration;
  51. destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
  52. duration = speed / deceleration;
  53. if ( destination < lowerMargin ) {
  54. destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
  55. distance = Math.abs(destination - current);
  56. duration = distance / speed;
  57. } else if ( destination > 0 ) {
  58. destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
  59. distance = Math.abs(current) + destination;
  60. duration = distance / speed;
  61. }
  62. return {
  63. destination: Math.round(destination),
  64. duration: duration
  65. };
  66. };
  67. var _transform = _prefixStyle('transform');
  68. me.extend(me, {
  69. hasTransform: _transform !== false,
  70. hasPerspective: _prefixStyle('perspective') in _elementStyle,
  71. hasTouch: 'ontouchstart' in window,
  72. hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
  73. hasTransition: _prefixStyle('transition') in _elementStyle
  74. });
  75. /*
  76. This should find all Android browsers lower than build 535.19 (both stock browser and webview)
  77. - galaxy S2 is ok
  78. - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
  79. - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
  80. - galaxy S3 is badAndroid (stock brower, webview)
  81. `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
  82. - galaxy S4 is badAndroid (stock brower, webview)
  83. `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
  84. - galaxy S5 is OK
  85. `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
  86. - galaxy S6 is OK
  87. `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
  88. */
  89. me.isBadAndroid = (function() {
  90. var appVersion = window.navigator.appVersion;
  91. // Android browser is not a chrome browser.
  92. if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
  93. var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
  94. if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
  95. return parseFloat(safariVersion[1]) < 535.19;
  96. } else {
  97. return true;
  98. }
  99. } else {
  100. return false;
  101. }
  102. })();
  103. me.extend(me.style = {}, {
  104. transform: _transform,
  105. transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
  106. transitionDuration: _prefixStyle('transitionDuration'),
  107. transitionDelay: _prefixStyle('transitionDelay'),
  108. transformOrigin: _prefixStyle('transformOrigin'),
  109. touchAction: _prefixStyle('touchAction')
  110. });
  111. me.hasClass = function (e, c) {
  112. var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
  113. return re.test(e.className);
  114. };
  115. me.addClass = function (e, c) {
  116. if ( me.hasClass(e, c) ) {
  117. return;
  118. }
  119. var newclass = e.className.split(' ');
  120. newclass.push(c);
  121. e.className = newclass.join(' ');
  122. };
  123. me.removeClass = function (e, c) {
  124. if ( !me.hasClass(e, c) ) {
  125. return;
  126. }
  127. var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
  128. e.className = e.className.replace(re, ' ');
  129. };
  130. me.offset = function (el) {
  131. var left = -el.offsetLeft,
  132. top = -el.offsetTop;
  133. // jshint -W084
  134. while (el = el.offsetParent) {
  135. left -= el.offsetLeft;
  136. top -= el.offsetTop;
  137. }
  138. // jshint +W084
  139. return {
  140. left: left,
  141. top: top
  142. };
  143. };
  144. me.preventDefaultException = function (el, exceptions) {
  145. for ( var i in exceptions ) {
  146. if ( exceptions[i].test(el[i]) ) {
  147. return true;
  148. }
  149. }
  150. return false;
  151. };
  152. me.extend(me.eventType = {}, {
  153. touchstart: 1,
  154. touchmove: 1,
  155. touchend: 1,
  156. mousedown: 2,
  157. mousemove: 2,
  158. mouseup: 2,
  159. pointerdown: 3,
  160. pointermove: 3,
  161. pointerup: 3,
  162. MSPointerDown: 3,
  163. MSPointerMove: 3,
  164. MSPointerUp: 3
  165. });
  166. me.extend(me.ease = {}, {
  167. quadratic: {
  168. style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
  169. fn: function (k) {
  170. return k * ( 2 - k );
  171. }
  172. },
  173. circular: {
  174. style: 'cubic-bezier(0.1, 0.57, 0.1, 1)', // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
  175. fn: function (k) {
  176. return Math.sqrt( 1 - ( --k * k ) );
  177. }
  178. },
  179. back: {
  180. style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
  181. fn: function (k) {
  182. var b = 4;
  183. return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
  184. }
  185. },
  186. bounce: {
  187. style: '',
  188. fn: function (k) {
  189. if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
  190. return 7.5625 * k * k;
  191. } else if ( k < ( 2 / 2.75 ) ) {
  192. return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
  193. } else if ( k < ( 2.5 / 2.75 ) ) {
  194. return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
  195. } else {
  196. return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
  197. }
  198. }
  199. },
  200. elastic: {
  201. style: '',
  202. fn: function (k) {
  203. var f = 0.22,
  204. e = 0.4;
  205. if ( k === 0 ) { return 0; }
  206. if ( k == 1 ) { return 1; }
  207. return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
  208. }
  209. }
  210. });
  211. me.tap = function (e, eventName) {
  212. var ev = document.createEvent('Event');
  213. ev.initEvent(eventName, true, true);
  214. ev.pageX = e.pageX;
  215. ev.pageY = e.pageY;
  216. e.target.dispatchEvent(ev);
  217. };
  218. me.click = function (e) {
  219. var target = e.target,
  220. ev;
  221. if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
  222. // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
  223. // initMouseEvent is deprecated.
  224. ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
  225. ev.initEvent('click', true, true);
  226. ev.view = e.view || window;
  227. ev.detail = 1;
  228. ev.screenX = target.screenX || 0;
  229. ev.screenY = target.screenY || 0;
  230. ev.clientX = target.clientX || 0;
  231. ev.clientY = target.clientY || 0;
  232. ev.ctrlKey = !!e.ctrlKey;
  233. ev.altKey = !!e.altKey;
  234. ev.shiftKey = !!e.shiftKey;
  235. ev.metaKey = !!e.metaKey;
  236. ev.button = 0;
  237. ev.relatedTarget = null;
  238. ev._constructed = true;
  239. target.dispatchEvent(ev);
  240. }
  241. };
  242. me.getTouchAction = function(eventPassthrough, addPinch) {
  243. var touchAction = 'none';
  244. if ( eventPassthrough === 'vertical' ) {
  245. touchAction = 'pan-y';
  246. } else if (eventPassthrough === 'horizontal' ) {
  247. touchAction = 'pan-x';
  248. }
  249. if (addPinch && touchAction != 'none') {
  250. // add pinch-zoom support if the browser supports it, but if not (eg. Chrome <55) do nothing
  251. touchAction += ' pinch-zoom';
  252. }
  253. return touchAction;
  254. };
  255. me.getRect = function(el) {
  256. if (el instanceof SVGElement) {
  257. var rect = el.getBoundingClientRect();
  258. return {
  259. top : rect.top,
  260. left : rect.left,
  261. width : rect.width,
  262. height : rect.height
  263. };
  264. } else {
  265. return {
  266. top : el.offsetTop,
  267. left : el.offsetLeft,
  268. width : el.offsetWidth,
  269. height : el.offsetHeight
  270. };
  271. }
  272. };
  273. return me;
  274. })();
  275. function IScroll (el, options) {
  276. this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
  277. this.scroller = this.wrapper.children[0];
  278. this.scrollerStyle = this.scroller.style; // cache style for better performance
  279. this.options = {
  280. resizeScrollbars: true,
  281. mouseWheelSpeed: 20,
  282. snapThreshold: 0.334,
  283. // INSERT POINT: OPTIONS
  284. disablePointer : !utils.hasPointer,
  285. disableTouch : utils.hasPointer || !utils.hasTouch,
  286. disableMouse : utils.hasPointer || utils.hasTouch,
  287. startX: 0,
  288. startY: 0,
  289. scrollY: true,
  290. directionLockThreshold: 5,
  291. momentum: true,
  292. bounce: true,
  293. bounceTime: 600,
  294. bounceEasing: '',
  295. preventDefault: true,
  296. preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ },
  297. HWCompositing: true,
  298. useTransition: true,
  299. useTransform: true,
  300. bindToWrapper: typeof window.onmousedown === "undefined"
  301. };
  302. for ( var i in options ) {
  303. this.options[i] = options[i];
  304. }
  305. // Normalize options
  306. this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
  307. this.options.useTransition = utils.hasTransition && this.options.useTransition;
  308. this.options.useTransform = utils.hasTransform && this.options.useTransform;
  309. this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
  310. this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
  311. // If you want eventPassthrough I have to lock one of the axes
  312. this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
  313. this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
  314. // With eventPassthrough we also need lockDirection mechanism
  315. this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
  316. this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
  317. this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
  318. this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
  319. if ( this.options.tap === true ) {
  320. this.options.tap = 'tap';
  321. }
  322. // https://github.com/cubiq/iscroll/issues/1029
  323. if (!this.options.useTransition && !this.options.useTransform) {
  324. if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
  325. this.scrollerStyle.position = "relative";
  326. }
  327. }
  328. if ( this.options.shrinkScrollbars == 'scale' ) {
  329. this.options.useTransition = false;
  330. }
  331. this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
  332. // INSERT POINT: NORMALIZATION
  333. // Some defaults
  334. this.x = 0;
  335. this.y = 0;
  336. this.directionX = 0;
  337. this.directionY = 0;
  338. this._events = {};
  339. // INSERT POINT: DEFAULTS
  340. this._init();
  341. this.refresh();
  342. this.scrollTo(this.options.startX, this.options.startY);
  343. this.enable();
  344. }
  345. IScroll.prototype = {
  346. version: '5.2.0-snapshot',
  347. _init: function () {
  348. this._initEvents();
  349. if ( this.options.scrollbars || this.options.indicators ) {
  350. this._initIndicators();
  351. }
  352. if ( this.options.mouseWheel ) {
  353. this._initWheel();
  354. }
  355. if ( this.options.snap ) {
  356. this._initSnap();
  357. }
  358. if ( this.options.keyBindings ) {
  359. this._initKeys();
  360. }
  361. // INSERT POINT: _init
  362. },
  363. destroy: function () {
  364. this._initEvents(true);
  365. clearTimeout(this.resizeTimeout);
  366. this.resizeTimeout = null;
  367. this._execEvent('destroy');
  368. },
  369. _transitionEnd: function (e) {
  370. if ( e.target != this.scroller || !this.isInTransition ) {
  371. return;
  372. }
  373. this._transitionTime();
  374. if ( !this.resetPosition(this.options.bounceTime) ) {
  375. this.isInTransition = false;
  376. this._execEvent('scrollEnd');
  377. }
  378. },
  379. _start: function (e) {
  380. // React to left mouse button only
  381. if ( utils.eventType[e.type] != 1 ) {
  382. // for button property
  383. // http://unixpapa.com/js/mouse.html
  384. var button;
  385. if (!e.which) {
  386. /* IE case */
  387. button = (e.button < 2) ? 0 :
  388. ((e.button == 4) ? 1 : 2);
  389. } else {
  390. /* All others */
  391. button = e.button;
  392. }
  393. if ( button !== 0 ) {
  394. return;
  395. }
  396. }
  397. if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
  398. return;
  399. }
  400. if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
  401. e.preventDefault();
  402. }
  403. var point = e.touches ? e.touches[0] : e,
  404. pos;
  405. this.initiated = utils.eventType[e.type];
  406. this.moved = false;
  407. this.distX = 0;
  408. this.distY = 0;
  409. this.directionX = 0;
  410. this.directionY = 0;
  411. this.directionLocked = 0;
  412. this.startTime = utils.getTime();
  413. if ( this.options.useTransition && this.isInTransition ) {
  414. this._transitionTime();
  415. this.isInTransition = false;
  416. pos = this.getComputedPosition();
  417. this._translate(Math.round(pos.x), Math.round(pos.y));
  418. this._execEvent('scrollEnd');
  419. } else if ( !this.options.useTransition && this.isAnimating ) {
  420. this.isAnimating = false;
  421. this._execEvent('scrollEnd');
  422. }
  423. this.startX = this.x;
  424. this.startY = this.y;
  425. this.absStartX = this.x;
  426. this.absStartY = this.y;
  427. this.pointX = point.pageX;
  428. this.pointY = point.pageY;
  429. this._execEvent('beforeScrollStart');
  430. },
  431. _move: function (e) {
  432. if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
  433. return;
  434. }
  435. if ( this.options.preventDefault ) { // increases performance on Android? TODO: check!
  436. e.preventDefault();
  437. }
  438. var point = e.touches ? e.touches[0] : e,
  439. deltaX = point.pageX - this.pointX,
  440. deltaY = point.pageY - this.pointY,
  441. timestamp = utils.getTime(),
  442. newX, newY,
  443. absDistX, absDistY;
  444. this.pointX = point.pageX;
  445. this.pointY = point.pageY;
  446. this.distX += deltaX;
  447. this.distY += deltaY;
  448. absDistX = Math.abs(this.distX);
  449. absDistY = Math.abs(this.distY);
  450. // We need to move at least 10 pixels for the scrolling to initiate
  451. if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
  452. return;
  453. }
  454. // If you are scrolling in one direction lock the other
  455. if ( !this.directionLocked && !this.options.freeScroll ) {
  456. if ( absDistX > absDistY + this.options.directionLockThreshold ) {
  457. this.directionLocked = 'h'; // lock horizontally
  458. } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
  459. this.directionLocked = 'v'; // lock vertically
  460. } else {
  461. this.directionLocked = 'n'; // no lock
  462. }
  463. }
  464. if ( this.directionLocked == 'h' ) {
  465. if ( this.options.eventPassthrough == 'vertical' ) {
  466. e.preventDefault();
  467. } else if ( this.options.eventPassthrough == 'horizontal' ) {
  468. this.initiated = false;
  469. return;
  470. }
  471. deltaY = 0;
  472. } else if ( this.directionLocked == 'v' ) {
  473. if ( this.options.eventPassthrough == 'horizontal' ) {
  474. e.preventDefault();
  475. } else if ( this.options.eventPassthrough == 'vertical' ) {
  476. this.initiated = false;
  477. return;
  478. }
  479. deltaX = 0;
  480. }
  481. deltaX = this.hasHorizontalScroll ? deltaX : 0;
  482. deltaY = this.hasVerticalScroll ? deltaY : 0;
  483. newX = this.x + deltaX;
  484. newY = this.y + deltaY;
  485. // Slow down if outside of the boundaries
  486. if ( newX > 0 || newX < this.maxScrollX ) {
  487. newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
  488. }
  489. if ( newY > 0 || newY < this.maxScrollY ) {
  490. newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
  491. }
  492. this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
  493. this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
  494. if ( !this.moved ) {
  495. this._execEvent('scrollStart');
  496. }
  497. this.moved = true;
  498. this._translate(newX, newY);
  499. /* REPLACE START: _move */
  500. if ( timestamp - this.startTime > 300 ) {
  501. this.startTime = timestamp;
  502. this.startX = this.x;
  503. this.startY = this.y;
  504. }
  505. /* REPLACE END: _move */
  506. },
  507. _end: function (e) {
  508. if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
  509. return;
  510. }
  511. if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
  512. e.preventDefault();
  513. }
  514. var point = e.changedTouches ? e.changedTouches[0] : e,
  515. momentumX,
  516. momentumY,
  517. duration = utils.getTime() - this.startTime,
  518. newX = Math.round(this.x),
  519. newY = Math.round(this.y),
  520. distanceX = Math.abs(newX - this.startX),
  521. distanceY = Math.abs(newY - this.startY),
  522. time = 0,
  523. easing = '';
  524. this.isInTransition = 0;
  525. this.initiated = 0;
  526. this.endTime = utils.getTime();
  527. // reset if we are outside of the boundaries
  528. if ( this.resetPosition(this.options.bounceTime) ) {
  529. return;
  530. }
  531. this.scrollTo(newX, newY); // ensures that the last position is rounded
  532. // we scrolled less than 10 pixels
  533. if ( !this.moved ) {
  534. if ( this.options.tap ) {
  535. utils.tap(e, this.options.tap);
  536. }
  537. if ( this.options.click ) {
  538. utils.click(e);
  539. }
  540. this._execEvent('scrollCancel');
  541. return;
  542. }
  543. if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
  544. this._execEvent('flick');
  545. return;
  546. }
  547. // start momentum animation if needed
  548. if ( this.options.momentum && duration < 300 ) {
  549. momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration) : { destination: newX, duration: 0 };
  550. momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration) : { destination: newY, duration: 0 };
  551. newX = momentumX.destination;
  552. newY = momentumY.destination;
  553. time = Math.max(momentumX.duration, momentumY.duration);
  554. this.isInTransition = 1;
  555. }
  556. if ( this.options.snap ) {
  557. var snap = this._nearestSnap(newX, newY);
  558. this.currentPage = snap;
  559. time = this.options.snapSpeed || Math.max(
  560. Math.max(
  561. Math.min(Math.abs(newX - snap.x), 1000),
  562. Math.min(Math.abs(newY - snap.y), 1000)
  563. ), 300);
  564. newX = snap.x;
  565. newY = snap.y;
  566. this.directionX = 0;
  567. this.directionY = 0;
  568. easing = this.options.bounceEasing;
  569. }
  570. // INSERT POINT: _end
  571. if ( newX != this.x || newY != this.y ) {
  572. // change easing function when scroller goes out of the boundaries
  573. if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
  574. easing = utils.ease.quadratic;
  575. }
  576. this.scrollTo(newX, newY, time, easing);
  577. return;
  578. }
  579. this._execEvent('scrollEnd');
  580. },
  581. _resize: function () {
  582. var that = this;
  583. clearTimeout(this.resizeTimeout);
  584. this.resizeTimeout = setTimeout(function () {
  585. that.refresh();
  586. }, this.options.resizePolling);
  587. },
  588. resetPosition: function (time) {
  589. var x = this.x,
  590. y = this.y;
  591. time = time || 0;
  592. if ( !this.hasHorizontalScroll || this.x > 0 ) {
  593. x = 0;
  594. } else if ( this.x < this.maxScrollX ) {
  595. x = this.maxScrollX;
  596. }
  597. if ( !this.hasVerticalScroll || this.y > 0 ) {
  598. y = 0;
  599. } else if ( this.y < this.maxScrollY ) {
  600. y = this.maxScrollY;
  601. }
  602. if ( x == this.x && y == this.y ) {
  603. return false;
  604. }
  605. this.scrollTo(x, y, time, this.options.bounceEasing);
  606. return true;
  607. },
  608. disable: function () {
  609. this.enabled = false;
  610. },
  611. enable: function () {
  612. this.enabled = true;
  613. },
  614. refresh: function () {
  615. utils.getRect(this.wrapper); // Force reflow
  616. this.wrapperWidth = this.wrapper.clientWidth;
  617. this.wrapperHeight = this.wrapper.clientHeight;
  618. var rect = utils.getRect(this.scroller);
  619. /* REPLACE START: refresh */
  620. this.scrollerWidth = rect.width;
  621. this.scrollerHeight = rect.height;
  622. this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
  623. this.maxScrollY = this.wrapperHeight - this.scrollerHeight;
  624. /* REPLACE END: refresh */
  625. this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;
  626. this.hasVerticalScroll = this.options.scrollY && this.maxScrollY < 0;
  627. if ( !this.hasHorizontalScroll ) {
  628. this.maxScrollX = 0;
  629. this.scrollerWidth = this.wrapperWidth;
  630. }
  631. if ( !this.hasVerticalScroll ) {
  632. this.maxScrollY = 0;
  633. this.scrollerHeight = this.wrapperHeight;
  634. }
  635. this.endTime = 0;
  636. this.directionX = 0;
  637. this.directionY = 0;
  638. if(utils.hasPointer && !this.options.disablePointer) {
  639. // The wrapper should have `touchAction` property for using pointerEvent.
  640. this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, true);
  641. // case. not support 'pinch-zoom'
  642. // https://github.com/cubiq/iscroll/issues/1118#issuecomment-270057583
  643. if (!this.wrapper.style[utils.style.touchAction]) {
  644. this.wrapper.style[utils.style.touchAction] = utils.getTouchAction(this.options.eventPassthrough, false);
  645. }
  646. }
  647. this.wrapperOffset = utils.offset(this.wrapper);
  648. this._execEvent('refresh');
  649. this.resetPosition();
  650. // INSERT POINT: _refresh
  651. },
  652. on: function (type, fn) {
  653. if ( !this._events[type] ) {
  654. this._events[type] = [];
  655. }
  656. this._events[type].push(fn);
  657. },
  658. off: function (type, fn) {
  659. if ( !this._events[type] ) {
  660. return;
  661. }
  662. var index = this._events[type].indexOf(fn);
  663. if ( index > -1 ) {
  664. this._events[type].splice(index, 1);
  665. }
  666. },
  667. _execEvent: function (type) {
  668. if ( !this._events[type] ) {
  669. return;
  670. }
  671. var i = 0,
  672. l = this._events[type].length;
  673. if ( !l ) {
  674. return;
  675. }
  676. for ( ; i < l; i++ ) {
  677. this._events[type][i].apply(this, [].slice.call(arguments, 1));
  678. }
  679. },
  680. scrollBy: function (x, y, time, easing) {
  681. x = this.x + x;
  682. y = this.y + y;
  683. time = time || 0;
  684. this.scrollTo(x, y, time, easing);
  685. },
  686. scrollTo: function (x, y, time, easing) {
  687. easing = easing || utils.ease.circular;
  688. this.isInTransition = this.options.useTransition && time > 0;
  689. var transitionType = this.options.useTransition && easing.style;
  690. if ( !time || transitionType ) {
  691. if(transitionType) {
  692. this._transitionTimingFunction(easing.style);
  693. this._transitionTime(time);
  694. }
  695. this._translate(x, y);
  696. } else {
  697. this._animate(x, y, time, easing.fn);
  698. }
  699. },
  700. scrollToElement: function (el, time, offsetX, offsetY, easing) {
  701. el = el.nodeType ? el : this.scroller.querySelector(el);
  702. if ( !el ) {
  703. return;
  704. }
  705. var pos = utils.offset(el);
  706. pos.left -= this.wrapperOffset.left;
  707. pos.top -= this.wrapperOffset.top;
  708. // if offsetX/Y are true we center the element to the screen
  709. var elRect = utils.getRect(el);
  710. var wrapperRect = utils.getRect(this.wrapper);
  711. if ( offsetX === true ) {
  712. offsetX = Math.round(elRect.width / 2 - wrapperRect.width / 2);
  713. }
  714. if ( offsetY === true ) {
  715. offsetY = Math.round(elRect.height / 2 - wrapperRect.height / 2);
  716. }
  717. pos.left -= offsetX || 0;
  718. pos.top -= offsetY || 0;
  719. pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
  720. pos.top = pos.top > 0 ? 0 : pos.top < this.maxScrollY ? this.maxScrollY : pos.top;
  721. time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
  722. this.scrollTo(pos.left, pos.top, time, easing);
  723. },
  724. _transitionTime: function (time) {
  725. if (!this.options.useTransition) {
  726. return;
  727. }
  728. time = time || 0;
  729. var durationProp = utils.style.transitionDuration;
  730. if(!durationProp) {
  731. return;
  732. }
  733. this.scrollerStyle[durationProp] = time + 'ms';
  734. if ( !time && utils.isBadAndroid ) {
  735. this.scrollerStyle[durationProp] = '0.0001ms';
  736. // remove 0.0001ms
  737. var self = this;
  738. rAF(function() {
  739. if(self.scrollerStyle[durationProp] === '0.0001ms') {
  740. self.scrollerStyle[durationProp] = '0s';
  741. }
  742. });
  743. }
  744. if ( this.indicators ) {
  745. for ( var i = this.indicators.length; i--; ) {
  746. this.indicators[i].transitionTime(time);
  747. }
  748. }
  749. // INSERT POINT: _transitionTime
  750. },
  751. _transitionTimingFunction: function (easing) {
  752. this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
  753. if ( this.indicators ) {
  754. for ( var i = this.indicators.length; i--; ) {
  755. this.indicators[i].transitionTimingFunction(easing);
  756. }
  757. }
  758. // INSERT POINT: _transitionTimingFunction
  759. },
  760. _translate: function (x, y) {
  761. if ( this.options.useTransform ) {
  762. /* REPLACE START: _translate */
  763. this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
  764. /* REPLACE END: _translate */
  765. } else {
  766. x = Math.round(x);
  767. y = Math.round(y);
  768. this.scrollerStyle.left = x + 'px';
  769. this.scrollerStyle.top = y + 'px';
  770. }
  771. this.x = x;
  772. this.y = y;
  773. if ( this.indicators ) {
  774. for ( var i = this.indicators.length; i--; ) {
  775. this.indicators[i].updatePosition();
  776. }
  777. }
  778. // INSERT POINT: _translate
  779. },
  780. _initEvents: function (remove) {
  781. var eventType = remove ? utils.removeEvent : utils.addEvent,
  782. target = this.options.bindToWrapper ? this.wrapper : window;
  783. eventType(window, 'orientationchange', this);
  784. eventType(window, 'resize', this);
  785. if ( this.options.click ) {
  786. eventType(this.wrapper, 'click', this, true);
  787. }
  788. if ( !this.options.disableMouse ) {
  789. eventType(this.wrapper, 'mousedown', this);
  790. eventType(target, 'mousemove', this);
  791. eventType(target, 'mousecancel', this);
  792. eventType(target, 'mouseup', this);
  793. }
  794. if ( utils.hasPointer && !this.options.disablePointer ) {
  795. eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
  796. eventType(target, utils.prefixPointerEvent('pointermove'), this);
  797. eventType(target, utils.prefixPointerEvent('pointercancel'), this);
  798. eventType(target, utils.prefixPointerEvent('pointerup'), this);
  799. }
  800. if ( utils.hasTouch && !this.options.disableTouch ) {
  801. eventType(this.wrapper, 'touchstart', this);
  802. eventType(target, 'touchmove', this);
  803. eventType(target, 'touchcancel', this);
  804. eventType(target, 'touchend', this);
  805. }
  806. eventType(this.scroller, 'transitionend', this);
  807. eventType(this.scroller, 'webkitTransitionEnd', this);
  808. eventType(this.scroller, 'oTransitionEnd', this);
  809. eventType(this.scroller, 'MSTransitionEnd', this);
  810. },
  811. getComputedPosition: function () {
  812. var matrix = window.getComputedStyle(this.scroller, null),
  813. x, y;
  814. if ( this.options.useTransform ) {
  815. matrix = matrix[utils.style.transform].split(')')[0].split(', ');
  816. x = +(matrix[12] || matrix[4]);
  817. y = +(matrix[13] || matrix[5]);
  818. } else {
  819. x = +matrix.left.replace(/[^-\d.]/g, '');
  820. y = +matrix.top.replace(/[^-\d.]/g, '');
  821. }
  822. return { x: x, y: y };
  823. },
  824. _initIndicators: function () {
  825. var interactive = this.options.interactiveScrollbars,
  826. customStyle = typeof this.options.scrollbars != 'string',
  827. indicators = [],
  828. indicator;
  829. var that = this;
  830. this.indicators = [];
  831. if ( this.options.scrollbars ) {
  832. // Vertical scrollbar
  833. if ( this.options.scrollY ) {
  834. indicator = {
  835. el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
  836. interactive: interactive,
  837. defaultScrollbars: true,
  838. customStyle: customStyle,
  839. resize: this.options.resizeScrollbars,
  840. shrink: this.options.shrinkScrollbars,
  841. fade: this.options.fadeScrollbars,
  842. listenX: false
  843. };
  844. this.wrapper.appendChild(indicator.el);
  845. indicators.push(indicator);
  846. }
  847. // Horizontal scrollbar
  848. if ( this.options.scrollX ) {
  849. indicator = {
  850. el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
  851. interactive: interactive,
  852. defaultScrollbars: true,
  853. customStyle: customStyle,
  854. resize: this.options.resizeScrollbars,
  855. shrink: this.options.shrinkScrollbars,
  856. fade: this.options.fadeScrollbars,
  857. listenY: false
  858. };
  859. this.wrapper.appendChild(indicator.el);
  860. indicators.push(indicator);
  861. }
  862. }
  863. if ( this.options.indicators ) {
  864. // TODO: check concat compatibility
  865. indicators = indicators.concat(this.options.indicators);
  866. }
  867. for ( var i = indicators.length; i--; ) {
  868. this.indicators.push( new Indicator(this, indicators[i]) );
  869. }
  870. // TODO: check if we can use array.map (wide compatibility and performance issues)
  871. function _indicatorsMap (fn) {
  872. if (that.indicators) {
  873. for ( var i = that.indicators.length; i--; ) {
  874. fn.call(that.indicators[i]);
  875. }
  876. }
  877. }
  878. if ( this.options.fadeScrollbars ) {
  879. this.on('scrollEnd', function () {
  880. _indicatorsMap(function () {
  881. this.fade();
  882. });
  883. });
  884. this.on('scrollCancel', function () {
  885. _indicatorsMap(function () {
  886. this.fade();
  887. });
  888. });
  889. this.on('scrollStart', function () {
  890. _indicatorsMap(function () {
  891. this.fade(1);
  892. });
  893. });
  894. this.on('beforeScrollStart', function () {
  895. _indicatorsMap(function () {
  896. this.fade(1, true);
  897. });
  898. });
  899. }
  900. this.on('refresh', function () {
  901. _indicatorsMap(function () {
  902. this.refresh();
  903. });
  904. });
  905. this.on('destroy', function () {
  906. _indicatorsMap(function () {
  907. this.destroy();
  908. });
  909. delete this.indicators;
  910. });
  911. },
  912. _initWheel: function () {
  913. utils.addEvent(this.wrapper, 'wheel', this);
  914. utils.addEvent(this.wrapper, 'mousewheel', this);
  915. utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
  916. this.on('destroy', function () {
  917. clearTimeout(this.wheelTimeout);
  918. this.wheelTimeout = null;
  919. utils.removeEvent(this.wrapper, 'wheel', this);
  920. utils.removeEvent(this.wrapper, 'mousewheel', this);
  921. utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
  922. });
  923. },
  924. _wheel: function (e) {
  925. if ( !this.enabled ) {
  926. return;
  927. }
  928. e.preventDefault();
  929. var wheelDeltaX, wheelDeltaY,
  930. newX, newY,
  931. that = this;
  932. if ( this.wheelTimeout === undefined ) {
  933. that._execEvent('scrollStart');
  934. }
  935. // Execute the scrollEnd event after 400ms the wheel stopped scrolling
  936. clearTimeout(this.wheelTimeout);
  937. this.wheelTimeout = setTimeout(function () {
  938. if(!that.options.snap) {
  939. that._execEvent('scrollEnd');
  940. }
  941. that.wheelTimeout = undefined;
  942. }, 400);
  943. if ( 'deltaX' in e ) {
  944. if (e.deltaMode === 1) {
  945. wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
  946. wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
  947. } else {
  948. wheelDeltaX = -e.deltaX;
  949. wheelDeltaY = -e.deltaY;
  950. }
  951. } else if ( 'wheelDeltaX' in e ) {
  952. wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
  953. wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
  954. } else if ( 'wheelDelta' in e ) {
  955. wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
  956. } else if ( 'detail' in e ) {
  957. wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
  958. } else {
  959. return;
  960. }
  961. wheelDeltaX *= this.options.invertWheelDirection;
  962. wheelDeltaY *= this.options.invertWheelDirection;
  963. if ( !this.hasVerticalScroll ) {
  964. wheelDeltaX = wheelDeltaY;
  965. wheelDeltaY = 0;
  966. }
  967. if ( this.options.snap ) {
  968. newX = this.currentPage.pageX;
  969. newY = this.currentPage.pageY;
  970. if ( wheelDeltaX > 0 ) {
  971. newX--;
  972. } else if ( wheelDeltaX < 0 ) {
  973. newX++;
  974. }
  975. if ( wheelDeltaY > 0 ) {
  976. newY--;
  977. } else if ( wheelDeltaY < 0 ) {
  978. newY++;
  979. }
  980. this.goToPage(newX, newY);
  981. return;
  982. }
  983. newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
  984. newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
  985. this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
  986. this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
  987. if ( newX > 0 ) {
  988. newX = 0;
  989. } else if ( newX < this.maxScrollX ) {
  990. newX = this.maxScrollX;
  991. }
  992. if ( newY > 0 ) {
  993. newY = 0;
  994. } else if ( newY < this.maxScrollY ) {
  995. newY = this.maxScrollY;
  996. }
  997. this.scrollTo(newX, newY, 0);
  998. // INSERT POINT: _wheel
  999. },
  1000. _initSnap: function () {
  1001. this.currentPage = {};
  1002. if ( typeof this.options.snap == 'string' ) {
  1003. this.options.snap = this.scroller.querySelectorAll(this.options.snap);
  1004. }
  1005. this.on('refresh', function () {
  1006. var i = 0, l,
  1007. m = 0, n,
  1008. cx, cy,
  1009. x = 0, y,
  1010. stepX = this.options.snapStepX || this.wrapperWidth,
  1011. stepY = this.options.snapStepY || this.wrapperHeight,
  1012. el,
  1013. rect;
  1014. this.pages = [];
  1015. if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
  1016. return;
  1017. }
  1018. if ( this.options.snap === true ) {
  1019. cx = Math.round( stepX / 2 );
  1020. cy = Math.round( stepY / 2 );
  1021. while ( x > -this.scrollerWidth ) {
  1022. this.pages[i] = [];
  1023. l = 0;
  1024. y = 0;
  1025. while ( y > -this.scrollerHeight ) {
  1026. this.pages[i][l] = {
  1027. x: Math.max(x, this.maxScrollX),
  1028. y: Math.max(y, this.maxScrollY),
  1029. width: stepX,
  1030. height: stepY,
  1031. cx: x - cx,
  1032. cy: y - cy
  1033. };
  1034. y -= stepY;
  1035. l++;
  1036. }
  1037. x -= stepX;
  1038. i++;
  1039. }
  1040. } else {
  1041. el = this.options.snap;
  1042. l = el.length;
  1043. n = -1;
  1044. for ( ; i < l; i++ ) {
  1045. rect = utils.getRect(el[i]);
  1046. if ( i === 0 || rect.left <= utils.getRect(el[i-1]).left ) {
  1047. m = 0;
  1048. n++;
  1049. }
  1050. if ( !this.pages[m] ) {
  1051. this.pages[m] = [];
  1052. }
  1053. x = Math.max(-rect.left, this.maxScrollX);
  1054. y = Math.max(-rect.top, this.maxScrollY);
  1055. cx = x - Math.round(rect.width / 2);
  1056. cy = y - Math.round(rect.height / 2);
  1057. this.pages[m][n] = {
  1058. x: x,
  1059. y: y,
  1060. width: rect.width,
  1061. height: rect.height,
  1062. cx: cx,
  1063. cy: cy
  1064. };
  1065. if ( x > this.maxScrollX ) {
  1066. m++;
  1067. }
  1068. }
  1069. }
  1070. this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
  1071. // Update snap threshold if needed
  1072. if ( this.options.snapThreshold % 1 === 0 ) {
  1073. this.snapThresholdX = this.options.snapThreshold;
  1074. this.snapThresholdY = this.options.snapThreshold;
  1075. } else {
  1076. this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
  1077. this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
  1078. }
  1079. });
  1080. this.on('flick', function () {
  1081. var time = this.options.snapSpeed || Math.max(
  1082. Math.max(
  1083. Math.min(Math.abs(this.x - this.startX), 1000),
  1084. Math.min(Math.abs(this.y - this.startY), 1000)
  1085. ), 300);
  1086. this.goToPage(
  1087. this.currentPage.pageX + this.directionX,
  1088. this.currentPage.pageY + this.directionY,
  1089. time
  1090. );
  1091. });
  1092. },
  1093. _nearestSnap: function (x, y) {
  1094. if ( !this.pages.length ) {
  1095. return { x: 0, y: 0, pageX: 0, pageY: 0 };
  1096. }
  1097. var i = 0,
  1098. l = this.pages.length,
  1099. m = 0;
  1100. // Check if we exceeded the snap threshold
  1101. if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
  1102. Math.abs(y - this.absStartY) < this.snapThresholdY ) {
  1103. return this.currentPage;
  1104. }
  1105. if ( x > 0 ) {
  1106. x = 0;
  1107. } else if ( x < this.maxScrollX ) {
  1108. x = this.maxScrollX;
  1109. }
  1110. if ( y > 0 ) {
  1111. y = 0;
  1112. } else if ( y < this.maxScrollY ) {
  1113. y = this.maxScrollY;
  1114. }
  1115. for ( ; i < l; i++ ) {
  1116. if ( x >= this.pages[i][0].cx ) {
  1117. x = this.pages[i][0].x;
  1118. break;
  1119. }
  1120. }
  1121. l = this.pages[i].length;
  1122. for ( ; m < l; m++ ) {
  1123. if ( y >= this.pages[0][m].cy ) {
  1124. y = this.pages[0][m].y;
  1125. break;
  1126. }
  1127. }
  1128. if ( i == this.currentPage.pageX ) {
  1129. i += this.directionX;
  1130. if ( i < 0 ) {
  1131. i = 0;
  1132. } else if ( i >= this.pages.length ) {
  1133. i = this.pages.length - 1;
  1134. }
  1135. x = this.pages[i][0].x;
  1136. }
  1137. if ( m == this.currentPage.pageY ) {
  1138. m += this.directionY;
  1139. if ( m < 0 ) {
  1140. m = 0;
  1141. } else if ( m >= this.pages[0].length ) {
  1142. m = this.pages[0].length - 1;
  1143. }
  1144. y = this.pages[0][m].y;
  1145. }
  1146. return {
  1147. x: x,
  1148. y: y,
  1149. pageX: i,
  1150. pageY: m
  1151. };
  1152. },
  1153. goToPage: function (x, y, time, easing) {
  1154. easing = easing || this.options.bounceEasing;
  1155. if ( x >= this.pages.length ) {
  1156. x = this.pages.length - 1;
  1157. } else if ( x < 0 ) {
  1158. x = 0;
  1159. }
  1160. if ( y >= this.pages[x].length ) {
  1161. y = this.pages[x].length - 1;
  1162. } else if ( y < 0 ) {
  1163. y = 0;
  1164. }
  1165. var posX = this.pages[x][y].x,
  1166. posY = this.pages[x][y].y;
  1167. time = time === undefined ? this.options.snapSpeed || Math.max(
  1168. Math.max(
  1169. Math.min(Math.abs(posX - this.x), 1000),
  1170. Math.min(Math.abs(posY - this.y), 1000)
  1171. ), 300) : time;
  1172. this.currentPage = {
  1173. x: posX,
  1174. y: posY,
  1175. pageX: x,
  1176. pageY: y
  1177. };
  1178. this.scrollTo(posX, posY, time, easing);
  1179. },
  1180. next: function (time, easing) {
  1181. var x = this.currentPage.pageX,
  1182. y = this.currentPage.pageY;
  1183. x++;
  1184. if ( x >= this.pages.length && this.hasVerticalScroll ) {
  1185. x = 0;
  1186. y++;
  1187. }
  1188. this.goToPage(x, y, time, easing);
  1189. },
  1190. prev: function (time, easing) {
  1191. var x = this.currentPage.pageX,
  1192. y = this.currentPage.pageY;
  1193. x--;
  1194. if ( x < 0 && this.hasVerticalScroll ) {
  1195. x = 0;
  1196. y--;
  1197. }
  1198. this.goToPage(x, y, time, easing);
  1199. },
  1200. _initKeys: function (e) {
  1201. // default key bindings
  1202. var keys = {
  1203. pageUp: 33,
  1204. pageDown: 34,
  1205. end: 35,
  1206. home: 36,
  1207. left: 37,
  1208. up: 38,
  1209. right: 39,
  1210. down: 40
  1211. };
  1212. var i;
  1213. // if you give me characters I give you keycode
  1214. if ( typeof this.options.keyBindings == 'object' ) {
  1215. for ( i in this.options.keyBindings ) {
  1216. if ( typeof this.options.keyBindings[i] == 'string' ) {
  1217. this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCodeAt(0);
  1218. }
  1219. }
  1220. } else {
  1221. this.options.keyBindings = {};
  1222. }
  1223. for ( i in keys ) {
  1224. this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
  1225. }
  1226. utils.addEvent(window, 'keydown', this);
  1227. this.on('destroy', function () {
  1228. utils.removeEvent(window, 'keydown', this);
  1229. });
  1230. },
  1231. _key: function (e) {
  1232. if ( !this.enabled ) {
  1233. return;
  1234. }
  1235. var snap = this.options.snap, // we are using this alot, better to cache it
  1236. newX = snap ? this.currentPage.pageX : this.x,
  1237. newY = snap ? this.currentPage.pageY : this.y,
  1238. now = utils.getTime(),
  1239. prevTime = this.keyTime || 0,
  1240. acceleration = 0.250,
  1241. pos;
  1242. if ( this.options.useTransition && this.isInTransition ) {
  1243. pos = this.getComputedPosition();
  1244. this._translate(Math.round(pos.x), Math.round(pos.y));
  1245. this.isInTransition = false;
  1246. }
  1247. this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
  1248. switch ( e.keyCode ) {
  1249. case this.options.keyBindings.pageUp:
  1250. if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
  1251. newX += snap ? 1 : this.wrapperWidth;
  1252. } else {
  1253. newY += snap ? 1 : this.wrapperHeight;
  1254. }
  1255. break;
  1256. case this.options.keyBindings.pageDown:
  1257. if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
  1258. newX -= snap ? 1 : this.wrapperWidth;
  1259. } else {
  1260. newY -= snap ? 1 : this.wrapperHeight;
  1261. }
  1262. break;
  1263. case this.options.keyBindings.end:
  1264. newX = snap ? this.pages.length-1 : this.maxScrollX;
  1265. newY = snap ? this.pages[0].length-1 : this.maxScrollY;
  1266. break;
  1267. case this.options.keyBindings.home:
  1268. newX = 0;
  1269. newY = 0;
  1270. break;
  1271. case this.options.keyBindings.left:
  1272. newX += snap ? -1 : 5 + this.keyAcceleration>>0;
  1273. break;
  1274. case this.options.keyBindings.up:
  1275. newY += snap ? 1 : 5 + this.keyAcceleration>>0;
  1276. break;
  1277. case this.options.keyBindings.right:
  1278. newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
  1279. break;
  1280. case this.options.keyBindings.down:
  1281. newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
  1282. break;
  1283. default:
  1284. return;
  1285. }
  1286. if ( snap ) {
  1287. this.goToPage(newX, newY);
  1288. return;
  1289. }
  1290. if ( newX > 0 ) {
  1291. newX = 0;
  1292. this.keyAcceleration = 0;
  1293. } else if ( newX < this.maxScrollX ) {
  1294. newX = this.maxScrollX;
  1295. this.keyAcceleration = 0;
  1296. }
  1297. if ( newY > 0 ) {
  1298. newY = 0;
  1299. this.keyAcceleration = 0;
  1300. } else if ( newY < this.maxScrollY ) {
  1301. newY = this.maxScrollY;
  1302. this.keyAcceleration = 0;
  1303. }
  1304. this.scrollTo(newX, newY, 0);
  1305. this.keyTime = now;
  1306. },
  1307. _animate: function (destX, destY, duration, easingFn) {
  1308. var that = this,
  1309. startX = this.x,
  1310. startY = this.y,
  1311. startTime = utils.getTime(),
  1312. destTime = startTime + duration;
  1313. function step () {
  1314. var now = utils.getTime(),
  1315. newX, newY,
  1316. easing;
  1317. if ( now >= destTime ) {
  1318. that.isAnimating = false;
  1319. that._translate(destX, destY);
  1320. if ( !that.resetPosition(that.options.bounceTime) ) {
  1321. that._execEvent('scrollEnd');
  1322. }
  1323. return;
  1324. }
  1325. now = ( now - startTime ) / duration;
  1326. easing = easingFn(now);
  1327. newX = ( destX - startX ) * easing + startX;
  1328. newY = ( destY - startY ) * easing + startY;
  1329. that._translate(newX, newY);
  1330. if ( that.isAnimating ) {
  1331. rAF(step);
  1332. }
  1333. }
  1334. this.isAnimating = true;
  1335. step();
  1336. },
  1337. handleEvent: function (e) {
  1338. switch ( e.type ) {
  1339. case 'touchstart':
  1340. case 'pointerdown':
  1341. case 'MSPointerDown':
  1342. case 'mousedown':
  1343. this._start(e);
  1344. break;
  1345. case 'touchmove':
  1346. case 'pointermove':
  1347. case 'MSPointerMove':
  1348. case 'mousemove':
  1349. this._move(e);
  1350. break;
  1351. case 'touchend':
  1352. case 'pointerup':
  1353. case 'MSPointerUp':
  1354. case 'mouseup':
  1355. case 'touchcancel':
  1356. case 'pointercancel':
  1357. case 'MSPointerCancel':
  1358. case 'mousecancel':
  1359. this._end(e);
  1360. break;
  1361. case 'orientationchange':
  1362. case 'resize':
  1363. this._resize();
  1364. break;
  1365. case 'transitionend':
  1366. case 'webkitTransitionEnd':
  1367. case 'oTransitionEnd':
  1368. case 'MSTransitionEnd':
  1369. this._transitionEnd(e);
  1370. break;
  1371. case 'wheel':
  1372. case 'DOMMouseScroll':
  1373. case 'mousewheel':
  1374. this._wheel(e);
  1375. break;
  1376. case 'keydown':
  1377. this._key(e);
  1378. break;
  1379. case 'click':
  1380. if ( this.enabled && !e._constructed ) {
  1381. e.preventDefault();
  1382. e.stopPropagation();
  1383. }
  1384. break;
  1385. }
  1386. }
  1387. };
  1388. function createDefaultScrollbar (direction, interactive, type) {
  1389. var scrollbar = document.createElement('div'),
  1390. indicator = document.createElement('div');
  1391. if ( type === true ) {
  1392. scrollbar.style.cssText = 'position:absolute;z-index:9999';
  1393. indicator.style.cssText = '-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px';
  1394. }
  1395. indicator.className = 'iScrollIndicator';
  1396. if ( direction == 'h' ) {
  1397. if ( type === true ) {
  1398. scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
  1399. indicator.style.height = '100%';
  1400. }
  1401. scrollbar.className = 'iScrollHorizontalScrollbar';
  1402. } else {
  1403. if ( type === true ) {
  1404. scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
  1405. indicator.style.width = '100%';
  1406. }
  1407. scrollbar.className = 'iScrollVerticalScrollbar';
  1408. }
  1409. scrollbar.style.cssText += ';overflow:hidden';
  1410. if ( !interactive ) {
  1411. scrollbar.style.pointerEvents = 'none';
  1412. }
  1413. scrollbar.appendChild(indicator);
  1414. return scrollbar;
  1415. }
  1416. function Indicator (scroller, options) {
  1417. this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
  1418. this.wrapperStyle = this.wrapper.style;
  1419. this.indicator = this.wrapper.children[0];
  1420. this.indicatorStyle = this.indicator.style;
  1421. this.scroller = scroller;
  1422. this.options = {
  1423. listenX: true,
  1424. listenY: true,
  1425. interactive: false,
  1426. resize: true,
  1427. defaultScrollbars: false,
  1428. shrink: false,
  1429. fade: false,
  1430. speedRatioX: 0,
  1431. speedRatioY: 0
  1432. };
  1433. for ( var i in options ) {
  1434. this.options[i] = options[i];
  1435. }
  1436. this.sizeRatioX = 1;
  1437. this.sizeRatioY = 1;
  1438. this.maxPosX = 0;
  1439. this.maxPosY = 0;
  1440. if ( this.options.interactive ) {
  1441. if ( !this.options.disableTouch ) {
  1442. utils.addEvent(this.indicator, 'touchstart', this);
  1443. utils.addEvent(window, 'touchend', this);
  1444. }
  1445. if ( !this.options.disablePointer ) {
  1446. utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
  1447. utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
  1448. }
  1449. if ( !this.options.disableMouse ) {
  1450. utils.addEvent(this.indicator, 'mousedown', this);
  1451. utils.addEvent(window, 'mouseup', this);
  1452. }
  1453. }
  1454. if ( this.options.fade ) {
  1455. this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
  1456. var durationProp = utils.style.transitionDuration;
  1457. if(!durationProp) {
  1458. return;
  1459. }
  1460. this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';
  1461. // remove 0.0001ms
  1462. var self = this;
  1463. if(utils.isBadAndroid) {
  1464. rAF(function() {
  1465. if(self.wrapperStyle[durationProp] === '0.0001ms') {
  1466. self.wrapperStyle[durationProp] = '0s';
  1467. }
  1468. });
  1469. }
  1470. this.wrapperStyle.opacity = '0';
  1471. }
  1472. }
  1473. Indicator.prototype = {
  1474. handleEvent: function (e) {
  1475. switch ( e.type ) {
  1476. case 'touchstart':
  1477. case 'pointerdown':
  1478. case 'MSPointerDown':
  1479. case 'mousedown':
  1480. this._start(e);
  1481. break;
  1482. case 'touchmove':
  1483. case 'pointermove':
  1484. case 'MSPointerMove':
  1485. case 'mousemove':
  1486. this._move(e);
  1487. break;
  1488. case 'touchend':
  1489. case 'pointerup':
  1490. case 'MSPointerUp':
  1491. case 'mouseup':
  1492. case 'touchcancel':
  1493. case 'pointercancel':
  1494. case 'MSPointerCancel':
  1495. case 'mousecancel':
  1496. this._end(e);
  1497. break;
  1498. }
  1499. },
  1500. destroy: function () {
  1501. if ( this.options.fadeScrollbars ) {
  1502. clearTimeout(this.fadeTimeout);
  1503. this.fadeTimeout = null;
  1504. }
  1505. if ( this.options.interactive ) {
  1506. utils.removeEvent(this.indicator, 'touchstart', this);
  1507. utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
  1508. utils.removeEvent(this.indicator, 'mousedown', this);
  1509. utils.removeEvent(window, 'touchmove', this);
  1510. utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
  1511. utils.removeEvent(window, 'mousemove', this);
  1512. utils.removeEvent(window, 'touchend', this);
  1513. utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
  1514. utils.removeEvent(window, 'mouseup', this);
  1515. }
  1516. if ( this.options.defaultScrollbars && this.wrapper.parentNode ) {
  1517. this.wrapper.parentNode.removeChild(this.wrapper);
  1518. }
  1519. },
  1520. _start: function (e) {
  1521. var point = e.touches ? e.touches[0] : e;
  1522. e.preventDefault();
  1523. e.stopPropagation();
  1524. this.transitionTime();
  1525. this.initiated = true;
  1526. this.moved = false;
  1527. this.lastPointX = point.pageX;
  1528. this.lastPointY = point.pageY;
  1529. this.startTime = utils.getTime();
  1530. if ( !this.options.disableTouch ) {
  1531. utils.addEvent(window, 'touchmove', this);
  1532. }
  1533. if ( !this.options.disablePointer ) {
  1534. utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
  1535. }
  1536. if ( !this.options.disableMouse ) {
  1537. utils.addEvent(window, 'mousemove', this);
  1538. }
  1539. this.scroller._execEvent('beforeScrollStart');
  1540. },
  1541. _move: function (e) {
  1542. var point = e.touches ? e.touches[0] : e,
  1543. deltaX, deltaY,
  1544. newX, newY,
  1545. timestamp = utils.getTime();
  1546. if ( !this.moved ) {
  1547. this.scroller._execEvent('scrollStart');
  1548. }
  1549. this.moved = true;
  1550. deltaX = point.pageX - this.lastPointX;
  1551. this.lastPointX = point.pageX;
  1552. deltaY = point.pageY - this.lastPointY;
  1553. this.lastPointY = point.pageY;
  1554. newX = this.x + deltaX;
  1555. newY = this.y + deltaY;
  1556. this._pos(newX, newY);
  1557. // INSERT POINT: indicator._move
  1558. e.preventDefault();
  1559. e.stopPropagation();
  1560. },
  1561. _end: function (e) {
  1562. if ( !this.initiated ) {
  1563. return;
  1564. }
  1565. this.initiated = false;
  1566. e.preventDefault();
  1567. e.stopPropagation();
  1568. utils.removeEvent(window, 'touchmove', this);
  1569. utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
  1570. utils.removeEvent(window, 'mousemove', this);
  1571. if ( this.scroller.options.snap ) {
  1572. var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
  1573. var time = this.options.snapSpeed || Math.max(
  1574. Math.max(
  1575. Math.min(Math.abs(this.scroller.x - snap.x), 1000),
  1576. Math.min(Math.abs(this.scroller.y - snap.y), 1000)
  1577. ), 300);
  1578. if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
  1579. this.scroller.directionX = 0;
  1580. this.scroller.directionY = 0;
  1581. this.scroller.currentPage = snap;
  1582. this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
  1583. }
  1584. }
  1585. if ( this.moved ) {
  1586. this.scroller._execEvent('scrollEnd');
  1587. }
  1588. },
  1589. transitionTime: function (time) {
  1590. time = time || 0;
  1591. var durationProp = utils.style.transitionDuration;
  1592. if(!durationProp) {
  1593. return;
  1594. }
  1595. this.indicatorStyle[durationProp] = time + 'ms';
  1596. if ( !time && utils.isBadAndroid ) {
  1597. this.indicatorStyle[durationProp] = '0.0001ms';
  1598. // remove 0.0001ms
  1599. var self = this;
  1600. rAF(function() {
  1601. if(self.indicatorStyle[durationProp] === '0.0001ms') {
  1602. self.indicatorStyle[durationProp] = '0s';
  1603. }
  1604. });
  1605. }
  1606. },
  1607. transitionTimingFunction: function (easing) {
  1608. this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
  1609. },
  1610. refresh: function () {
  1611. this.transitionTime();
  1612. if ( this.options.listenX && !this.options.listenY ) {
  1613. this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
  1614. } else if ( this.options.listenY && !this.options.listenX ) {
  1615. this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
  1616. } else {
  1617. this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
  1618. }
  1619. if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
  1620. utils.addClass(this.wrapper, 'iScrollBothScrollbars');
  1621. utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
  1622. if ( this.options.defaultScrollbars && this.options.customStyle ) {
  1623. if ( this.options.listenX ) {
  1624. this.wrapper.style.right = '8px';
  1625. } else {
  1626. this.wrapper.style.bottom = '8px';
  1627. }
  1628. }
  1629. } else {
  1630. utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
  1631. utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
  1632. if ( this.options.defaultScrollbars && this.options.customStyle ) {
  1633. if ( this.options.listenX ) {
  1634. this.wrapper.style.right = '2px';
  1635. } else {
  1636. this.wrapper.style.bottom = '2px';
  1637. }
  1638. }
  1639. }
  1640. utils.getRect(this.wrapper); // force refresh
  1641. if ( this.options.listenX ) {
  1642. this.wrapperWidth = this.wrapper.clientWidth;
  1643. if ( this.options.resize ) {
  1644. this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
  1645. this.indicatorStyle.width = this.indicatorWidth + 'px';
  1646. } else {
  1647. this.indicatorWidth = this.indicator.clientWidth;
  1648. }
  1649. this.maxPosX = this.wrapperWidth - this.indicatorWidth;
  1650. if ( this.options.shrink == 'clip' ) {
  1651. this.minBoundaryX = -this.indicatorWidth + 8;
  1652. this.maxBoundaryX = this.wrapperWidth - 8;
  1653. } else {
  1654. this.minBoundaryX = 0;
  1655. this.maxBoundaryX = this.maxPosX;
  1656. }
  1657. this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
  1658. }
  1659. if ( this.options.listenY ) {
  1660. this.wrapperHeight = this.wrapper.clientHeight;
  1661. if ( this.options.resize ) {
  1662. this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
  1663. this.indicatorStyle.height = this.indicatorHeight + 'px';
  1664. } else {
  1665. this.indicatorHeight = this.indicator.clientHeight;
  1666. }
  1667. this.maxPosY = this.wrapperHeight - this.indicatorHeight;
  1668. if ( this.options.shrink == 'clip' ) {
  1669. this.minBoundaryY = -this.indicatorHeight + 8;
  1670. this.maxBoundaryY = this.wrapperHeight - 8;
  1671. } else {
  1672. this.minBoundaryY = 0;
  1673. this.maxBoundaryY = this.maxPosY;
  1674. }
  1675. this.maxPosY = this.wrapperHeight - this.indicatorHeight;
  1676. this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
  1677. }
  1678. this.updatePosition();
  1679. },
  1680. updatePosition: function () {
  1681. var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
  1682. y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
  1683. if ( !this.options.ignoreBoundaries ) {
  1684. if ( x < this.minBoundaryX ) {
  1685. if ( this.options.shrink == 'scale' ) {
  1686. this.width = Math.max(this.indicatorWidth + x, 8);
  1687. this.indicatorStyle.width = this.width + 'px';
  1688. }
  1689. x = this.minBoundaryX;
  1690. } else if ( x > this.maxBoundaryX ) {
  1691. if ( this.options.shrink == 'scale' ) {
  1692. this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
  1693. this.indicatorStyle.width = this.width + 'px';
  1694. x = this.maxPosX + this.indicatorWidth - this.width;
  1695. } else {
  1696. x = this.maxBoundaryX;
  1697. }
  1698. } else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
  1699. this.width = this.indicatorWidth;
  1700. this.indicatorStyle.width = this.width + 'px';
  1701. }
  1702. if ( y < this.minBoundaryY ) {
  1703. if ( this.options.shrink == 'scale' ) {
  1704. this.height = Math.max(this.indicatorHeight + y * 3, 8);
  1705. this.indicatorStyle.height = this.height + 'px';
  1706. }
  1707. y = this.minBoundaryY;
  1708. } else if ( y > this.maxBoundaryY ) {
  1709. if ( this.options.shrink == 'scale' ) {
  1710. this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
  1711. this.indicatorStyle.height = this.height + 'px';
  1712. y = this.maxPosY + this.indicatorHeight - this.height;
  1713. } else {
  1714. y = this.maxBoundaryY;
  1715. }
  1716. } else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
  1717. this.height = this.indicatorHeight;
  1718. this.indicatorStyle.height = this.height + 'px';
  1719. }
  1720. }
  1721. this.x = x;
  1722. this.y = y;
  1723. if ( this.scroller.options.useTransform ) {
  1724. this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
  1725. } else {
  1726. this.indicatorStyle.left = x + 'px';
  1727. this.indicatorStyle.top = y + 'px';
  1728. }
  1729. },
  1730. _pos: function (x, y) {
  1731. if ( x < 0 ) {
  1732. x = 0;
  1733. } else if ( x > this.maxPosX ) {
  1734. x = this.maxPosX;
  1735. }
  1736. if ( y < 0 ) {
  1737. y = 0;
  1738. } else if ( y > this.maxPosY ) {
  1739. y = this.maxPosY;
  1740. }
  1741. x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
  1742. y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
  1743. this.scroller.scrollTo(x, y);
  1744. },
  1745. fade: function (val, hold) {
  1746. if ( hold && !this.visible ) {
  1747. return;
  1748. }
  1749. clearTimeout(this.fadeTimeout);
  1750. this.fadeTimeout = null;
  1751. var time = val ? 250 : 500,
  1752. delay = val ? 0 : 300;
  1753. val = val ? '1' : '0';
  1754. this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
  1755. this.fadeTimeout = setTimeout((function (val) {
  1756. this.wrapperStyle.opacity = val;
  1757. this.visible = +val;
  1758. }).bind(this, val), delay);
  1759. }
  1760. };
  1761. IScroll.utils = utils;
  1762. if ( typeof module != 'undefined' && module.exports ) {
  1763. module.exports = IScroll;
  1764. } else if ( typeof define == 'function' && define.amd ) {
  1765. define( function () { return IScroll; } );
  1766. } else {
  1767. window.IScroll = IScroll;
  1768. }
  1769. })(window, document, Math);