/*! * iscroll v4.2.5 ~ copyright (c) 2012 matteo spinelli, http://cubiq.org * released under mit license, http://cubiq.org/license */ (function(window, doc){ var m = math, dummystyle = doc.createelement('div').style, vendor = (function () { var vendors = 't,webkitt,mozt,mst,ot'.split(','), t, i = 0, l = vendors.length; for ( ; i < l; i++ ) { t = vendors[i] + 'ransform'; if ( t in dummystyle ) { return vendors[i].substr(0, vendors[i].length - 1); } } return false; })(), cssvendor = vendor ? '-' + vendor.tolowercase() + '-' : '', // style properties transform = prefixstyle('transform'), transitionproperty = prefixstyle('transitionproperty'), transitionduration = prefixstyle('transitionduration'), transformorigin = prefixstyle('transformorigin'), transitiontimingfunction = prefixstyle('transitiontimingfunction'), transitiondelay = prefixstyle('transitiondelay'), // browser capabilities isandroid = (/android/gi).test(navigator.appversion), isidevice = (/iphone|ipad/gi).test(navigator.appversion), istouchpad = (/hp-tablet/gi).test(navigator.appversion), has3d = prefixstyle('perspective') in dummystyle, hastouch = 'ontouchstart' in window && !istouchpad, hastransform = vendor !== false, hastransitionend = prefixstyle('transition') in dummystyle, resize_ev = 'onorientationchange' in window ? 'orientationchange' : 'resize', start_ev = hastouch ? 'touchstart' : 'mousedown', move_ev = hastouch ? 'touchmove' : 'mousemove', end_ev = hastouch ? 'touchend' : 'mouseup', cancel_ev = hastouch ? 'touchcancel' : 'mouseup', trnend_ev = (function () { if ( vendor === false ) return false; var transitionend = { '' : 'transitionend', 'webkit' : 'webkittransitionend', 'moz' : 'transitionend', 'o' : 'otransitionend', 'ms' : 'mstransitionend' }; return transitionend[vendor]; })(), nextframe = (function() { return window.requestanimationframe || window.webkitrequestanimationframe || window.mozrequestanimationframe || window.orequestanimationframe || window.msrequestanimationframe || function(callback) { return settimeout(callback, 1); }; })(), cancelframe = (function () { return window.cancelrequestanimationframe || window.webkitcancelanimationframe || window.webkitcancelrequestanimationframe || window.mozcancelrequestanimationframe || window.ocancelrequestanimationframe || window.mscancelrequestanimationframe || cleartimeout; })(), // helpers translatez = has3d ? ' translatez(0)' : '', // constructor iscroll = function (el, options) { var that = this, i; that.wrapper = typeof el == 'object' ? el : doc.getelementbyid(el); that.wrapper.style.overflow = 'hidden'; that.scroller = that.wrapper.children[0]; // default options that.options = { hscroll: true, vscroll: true, x: 0, y: 0, bounce: true, bouncelock: false, momentum: true, lockdirection: true, usetransform: true, usetransition: false, topoffset: 0, checkdomchanges: false, // experimental handleclick: true, // scrollbar hscrollbar: true, vscrollbar: true, fixedscrollbar: isandroid, hidescrollbar: isidevice, fadescrollbar: isidevice && has3d, scrollbarclass: '', // zoom zoom: false, zoommin: 1, zoommax: 4, doubletapzoom: 2, wheelaction: 'scroll', // snap snap: false, snapthreshold: 1, // events onrefresh: null, onbeforescrollstart: function (e) { e.preventdefault(); }, onscrollstart: null, onbeforescrollmove: null, onscrollmove: null, onbeforescrollend: null, onscrollend: null, ontouchend: null, ondestroy: null, onzoomstart: null, onzoom: null, onzoomend: null }; // user defined options for (i in options) that.options[i] = options[i]; // set starting position that.x = that.options.x; that.y = that.options.y; // normalize options that.options.usetransform = hastransform && that.options.usetransform; that.options.hscrollbar = that.options.hscroll && that.options.hscrollbar; that.options.vscrollbar = that.options.vscroll && that.options.vscrollbar; that.options.zoom = that.options.usetransform && that.options.zoom; that.options.usetransition = hastransitionend && that.options.usetransition; // helpers fix android bug! // translate3d and scale doesn't work together! // ignoring 3d only when you set that.options.zoom if ( that.options.zoom && isandroid ){ translatez = ''; } // set some default styles that.scroller.style[transitionproperty] = that.options.usetransform ? cssvendor + 'transform' : 'top left'; that.scroller.style[transitionduration] = '0'; that.scroller.style[transformorigin] = '0 0'; if (that.options.usetransition) that.scroller.style[transitiontimingfunction] = 'cubic-bezier(0.33,0.66,0.66,1)'; if (that.options.usetransform) that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px)' + translatez; else that.scroller.style.csstext += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px'; if (that.options.usetransition) that.options.fixedscrollbar = true; that.refresh(); that._bind(resize_ev, window); that._bind(start_ev); if (!hastouch) { if (that.options.wheelaction != 'none') { that._bind('dommousescroll'); that._bind('mousewheel'); } } if (that.options.checkdomchanges) that.checkdomtime = setinterval(function () { that._checkdomchanges(); }, 500); }; // prototype iscroll.prototype = { enabled: true, x: 0, y: 0, steps: [], scale: 1, currpagex: 0, currpagey: 0, pagesx: [], pagesy: [], anitime: null, wheelzoomcount: 0, handleevent: function (e) { var that = this; switch(e.type) { case start_ev: if (!hastouch && e.button !== 0) return; that._start(e); break; case move_ev: that._move(e); break; case end_ev: case cancel_ev: that._end(e); break; case resize_ev: that._resize(); break; case 'dommousescroll': case 'mousewheel': that._wheel(e); break; case trnend_ev: that._transitionend(e); break; } }, _checkdomchanges: function () { if (this.moved || this.zoomed || this.animating || (this.scrollerw == this.scroller.offsetwidth * this.scale && this.scrollerh == this.scroller.offsetheight * this.scale)) return; this.refresh(); }, _scrollbar: function (dir) { var that = this, bar; if (!that[dir + 'scrollbar']) { if (that[dir + 'scrollbarwrapper']) { if (hastransform) that[dir + 'scrollbarindicator'].style[transform] = ''; that[dir + 'scrollbarwrapper'].parentnode.removechild(that[dir + 'scrollbarwrapper']); that[dir + 'scrollbarwrapper'] = null; that[dir + 'scrollbarindicator'] = null; } return; } if (!that[dir + 'scrollbarwrapper']) { // create the scrollbar wrapper bar = doc.createelement('div'); if (that.options.scrollbarclass) bar.classname = that.options.scrollbarclass + dir.touppercase(); 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'); 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'); that.wrapper.appendchild(bar); that[dir + 'scrollbarwrapper'] = bar; // create the scrollbar indicator bar = doc.createelement('div'); if (!that.options.scrollbarclass) { 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'; } 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; if (that.options.usetransition) bar.style.csstext += ';' + cssvendor + 'transition-timing-function:cubic-bezier(0.33,0.66,0.66,1)'; that[dir + 'scrollbarwrapper'].appendchild(bar); that[dir + 'scrollbarindicator'] = bar; } if (dir == 'h') { that.hscrollbarsize = that.hscrollbarwrapper.clientwidth; that.hscrollbarindicatorsize = m.max(m.round(that.hscrollbarsize * that.hscrollbarsize / that.scrollerw), 8); that.hscrollbarindicator.style.width = that.hscrollbarindicatorsize + 'px'; that.hscrollbarmaxscroll = that.hscrollbarsize - that.hscrollbarindicatorsize; that.hscrollbarprop = that.hscrollbarmaxscroll / that.maxscrollx; } else { that.vscrollbarsize = that.vscrollbarwrapper.clientheight; that.vscrollbarindicatorsize = m.max(m.round(that.vscrollbarsize * that.vscrollbarsize / that.scrollerh), 8); that.vscrollbarindicator.style.height = that.vscrollbarindicatorsize + 'px'; that.vscrollbarmaxscroll = that.vscrollbarsize - that.vscrollbarindicatorsize; that.vscrollbarprop = that.vscrollbarmaxscroll / that.maxscrolly; } // reset position that._scrollbarpos(dir, true); }, _resize: function () { var that = this; settimeout(function () { that.refresh(); }, isandroid ? 200 : 0); }, _pos: function (x, y) { if (this.zoomed) return; x = this.hscroll ? x : 0; y = this.vscroll ? y : 0; if (this.options.usetransform) { this.scroller.style[transform] = 'translate(' + x + 'px,' + y + 'px) scale(' + this.scale + ')' + translatez; } else { x = m.round(x); y = m.round(y); this.scroller.style.left = x + 'px'; this.scroller.style.top = y + 'px'; } this.x = x; this.y = y; this._scrollbarpos('h'); this._scrollbarpos('v'); }, _scrollbarpos: function (dir, hidden) { var that = this, pos = dir == 'h' ? that.x : that.y, size; if (!that[dir + 'scrollbar']) return; pos = that[dir + 'scrollbarprop'] * pos; if (pos < 0) { if (!that.options.fixedscrollbar) { size = that[dir + 'scrollbarindicatorsize'] + m.round(pos * 3); if (size < 8) size = 8; that[dir + 'scrollbarindicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; } pos = 0; } else if (pos > that[dir + 'scrollbarmaxscroll']) { if (!that.options.fixedscrollbar) { size = that[dir + 'scrollbarindicatorsize'] - m.round((pos - that[dir + 'scrollbarmaxscroll']) * 3); if (size < 8) size = 8; that[dir + 'scrollbarindicator'].style[dir == 'h' ? 'width' : 'height'] = size + 'px'; pos = that[dir + 'scrollbarmaxscroll'] + (that[dir + 'scrollbarindicatorsize'] - size); } else { pos = that[dir + 'scrollbarmaxscroll']; } } that[dir + 'scrollbarwrapper'].style[transitiondelay] = '0'; that[dir + 'scrollbarwrapper'].style.opacity = hidden && that.options.hidescrollbar ? '0' : '1'; that[dir + 'scrollbarindicator'].style[transform] = 'translate(' + (dir == 'h' ? pos + 'px,0)' : '0,' + pos + 'px)') + translatez; }, _start: function (e) { var that = this, point = hastouch ? e.touches[0] : e, matrix, x, y, c1, c2; if (!that.enabled) return; if (that.options.onbeforescrollstart) that.options.onbeforescrollstart.call(that, e); if (that.options.usetransition || that.options.zoom) that._transitiontime(0); that.moved = false; that.animating = false; that.zoomed = false; that.distx = 0; that.disty = 0; that.absdistx = 0; that.absdisty = 0; that.dirx = 0; that.diry = 0; // gesture start if (that.options.zoom && hastouch && e.touches.length > 1) { c1 = m.abs(e.touches[0].pagex-e.touches[1].pagex); c2 = m.abs(e.touches[0].pagey-e.touches[1].pagey); that.touchesdiststart = m.sqrt(c1 * c1 + c2 * c2); that.originx = m.abs(e.touches[0].pagex + e.touches[1].pagex - that.wrapperoffsetleft * 2) / 2 - that.x; that.originy = m.abs(e.touches[0].pagey + e.touches[1].pagey - that.wrapperoffsettop * 2) / 2 - that.y; if (that.options.onzoomstart) that.options.onzoomstart.call(that, e); } if (that.options.momentum) { if (that.options.usetransform) { // very lame general purpose alternative to cssmatrix matrix = getcomputedstyle(that.scroller, null)[transform].replace(/[^0-9\-.,]/g, '').split(','); x = +(matrix[12] || matrix[4]); y = +(matrix[13] || matrix[5]); } else { x = +getcomputedstyle(that.scroller, null).left.replace(/[^0-9-]/g, ''); y = +getcomputedstyle(that.scroller, null).top.replace(/[^0-9-]/g, ''); } if (x != that.x || y != that.y) { if (that.options.usetransition) that._unbind(trnend_ev); else cancelframe(that.anitime); that.steps = []; that._pos(x, y); if (that.options.onscrollend) that.options.onscrollend.call(that); } } that.absstartx = that.x; // needed by snap threshold that.absstarty = that.y; that.startx = that.x; that.starty = that.y; that.pointx = point.pagex; that.pointy = point.pagey; that.starttime = e.timestamp || date.now(); if (that.options.onscrollstart) that.options.onscrollstart.call(that, e); that._bind(move_ev, window); that._bind(end_ev, window); that._bind(cancel_ev, window); }, _move: function (e) { var that = this, point = hastouch ? e.touches[0] : e, deltax = point.pagex - that.pointx, deltay = point.pagey - that.pointy, newx = that.x + deltax, newy = that.y + deltay, c1, c2, scale, timestamp = e.timestamp || date.now(); if (that.options.onbeforescrollmove) that.options.onbeforescrollmove.call(that, e); // zoom if (that.options.zoom && hastouch && e.touches.length > 1) { c1 = m.abs(e.touches[0].pagex - e.touches[1].pagex); c2 = m.abs(e.touches[0].pagey - e.touches[1].pagey); that.touchesdist = m.sqrt(c1*c1+c2*c2); that.zoomed = true; scale = 1 / that.touchesdiststart * that.touchesdist * this.scale; if (scale < that.options.zoommin) scale = 0.5 * that.options.zoommin * math.pow(2.0, scale / that.options.zoommin); else if (scale > that.options.zoommax) scale = 2.0 * that.options.zoommax * math.pow(0.5, that.options.zoommax / scale); that.lastscale = scale / this.scale; newx = this.originx - this.originx * that.lastscale + this.x; newy = this.originy - this.originy * that.lastscale + this.y; this.scroller.style[transform] = 'translate(' + newx + 'px,' + newy + 'px) scale(' + scale + ')' + translatez; if (that.options.onzoom) that.options.onzoom.call(that, e); return; } that.pointx = point.pagex; that.pointy = point.pagey; // slow down if outside of the boundaries if (newx > 0 || newx < that.maxscrollx) { newx = that.options.bounce ? that.x + (deltax / 2) : newx >= 0 || that.maxscrollx >= 0 ? 0 : that.maxscrollx; } if (newy > that.minscrolly || newy < that.maxscrolly) { newy = that.options.bounce ? that.y + (deltay / 2) : newy >= that.minscrolly || that.maxscrolly >= 0 ? that.minscrolly : that.maxscrolly; } that.distx += deltax; that.disty += deltay; that.absdistx = m.abs(that.distx); that.absdisty = m.abs(that.disty); if (that.absdistx < 6 && that.absdisty < 6) { return; } // lock direction if (that.options.lockdirection) { if (that.absdistx > that.absdisty + 5) { newy = that.y; deltay = 0; } else if (that.absdisty > that.absdistx + 5) { newx = that.x; deltax = 0; } } that.moved = true; that._pos(newx, newy); that.dirx = deltax > 0 ? -1 : deltax < 0 ? 1 : 0; that.diry = deltay > 0 ? -1 : deltay < 0 ? 1 : 0; if (timestamp - that.starttime > 300) { that.starttime = timestamp; that.startx = that.x; that.starty = that.y; } if (that.options.onscrollmove) that.options.onscrollmove.call(that, e); }, _end: function (e) { if (hastouch && e.touches.length !== 0) return; var that = this, point = hastouch ? e.changedtouches[0] : e, target, ev, momentumx = { dist:0, time:0 }, momentumy = { dist:0, time:0 }, duration = (e.timestamp || date.now()) - that.starttime, newposx = that.x, newposy = that.y, distx, disty, newduration, snap, scale; that._unbind(move_ev, window); that._unbind(end_ev, window); that._unbind(cancel_ev, window); if (that.options.onbeforescrollend) that.options.onbeforescrollend.call(that, e); if (that.zoomed) { scale = that.scale * that.lastscale; scale = math.max(that.options.zoommin, scale); scale = math.min(that.options.zoommax, scale); that.lastscale = scale / that.scale; that.scale = scale; that.x = that.originx - that.originx * that.lastscale + that.x; that.y = that.originy - that.originy * that.lastscale + that.y; that.scroller.style[transitionduration] = '200ms'; that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px) scale(' + that.scale + ')' + translatez; that.zoomed = false; that.refresh(); if (that.options.onzoomend) that.options.onzoomend.call(that, e); return; } if (!that.moved) { if (hastouch) { if (that.doubletaptimer && that.options.zoom) { // double tapped cleartimeout(that.doubletaptimer); that.doubletaptimer = null; if (that.options.onzoomstart) that.options.onzoomstart.call(that, e); that.zoom(that.pointx, that.pointy, that.scale == 1 ? that.options.doubletapzoom : 1); if (that.options.onzoomend) { settimeout(function() { that.options.onzoomend.call(that, e); }, 200); // 200 is default zoom duration } } else if (this.options.handleclick) { that.doubletaptimer = settimeout(function () { that.doubletaptimer = null; // find the last touched element target = point.target; while (target.nodetype != 1) target = target.parentnode; if (target.tagname != 'select' && target.tagname != 'input' && target.tagname != 'textarea') { ev = doc.createevent('mouseevents'); ev.initmouseevent('click', true, true, e.view, 1, point.screenx, point.screeny, point.clientx, point.clienty, e.ctrlkey, e.altkey, e.shiftkey, e.metakey, 0, null); ev._fake = true; target.dispatchevent(ev); } }, that.options.zoom ? 250 : 0); } } that._resetpos(400); if (that.options.ontouchend) that.options.ontouchend.call(that, e); return; } if (duration < 300 && that.options.momentum) { momentumx = newposx ? that._momentum(newposx - that.startx, duration, -that.x, that.scrollerw - that.wrapperw + that.x, that.options.bounce ? that.wrapperw : 0) : momentumx; 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; newposx = that.x + momentumx.dist; newposy = that.y + momentumy.dist; if ((that.x > 0 && newposx > 0) || (that.x < that.maxscrollx && newposx < that.maxscrollx)) momentumx = { dist:0, time:0 }; if ((that.y > that.minscrolly && newposy > that.minscrolly) || (that.y < that.maxscrolly && newposy < that.maxscrolly)) momentumy = { dist:0, time:0 }; } if (momentumx.dist || momentumy.dist) { newduration = m.max(m.max(momentumx.time, momentumy.time), 10); // do we need to snap? if (that.options.snap) { distx = newposx - that.absstartx; disty = newposy - that.absstarty; if (m.abs(distx) < that.options.snapthreshold && m.abs(disty) < that.options.snapthreshold) { that.scrollto(that.absstartx, that.absstarty, 200); } else { snap = that._snap(newposx, newposy); newposx = snap.x; newposy = snap.y; newduration = m.max(snap.time, newduration); } } that.scrollto(m.round(newposx), m.round(newposy), newduration); if (that.options.ontouchend) that.options.ontouchend.call(that, e); return; } // do we need to snap? if (that.options.snap) { distx = newposx - that.absstartx; disty = newposy - that.absstarty; if (m.abs(distx) < that.options.snapthreshold && m.abs(disty) < that.options.snapthreshold) that.scrollto(that.absstartx, that.absstarty, 200); else { snap = that._snap(that.x, that.y); if (snap.x != that.x || snap.y != that.y) that.scrollto(snap.x, snap.y, snap.time); } if (that.options.ontouchend) that.options.ontouchend.call(that, e); return; } that._resetpos(200); if (that.options.ontouchend) that.options.ontouchend.call(that, e); }, _resetpos: function (time) { var that = this, resetx = that.x >= 0 ? 0 : that.x < that.maxscrollx ? that.maxscrollx : that.x, resety = that.y >= that.minscrolly || that.maxscrolly > 0 ? that.minscrolly : that.y < that.maxscrolly ? that.maxscrolly : that.y; if (resetx == that.x && resety == that.y) { if (that.moved) { that.moved = false; if (that.options.onscrollend) that.options.onscrollend.call(that); // execute custom code on scroll end } if (that.hscrollbar && that.options.hidescrollbar) { if (vendor == 'webkit') that.hscrollbarwrapper.style[transitiondelay] = '300ms'; that.hscrollbarwrapper.style.opacity = '0'; } if (that.vscrollbar && that.options.hidescrollbar) { if (vendor == 'webkit') that.vscrollbarwrapper.style[transitiondelay] = '300ms'; that.vscrollbarwrapper.style.opacity = '0'; } return; } that.scrollto(resetx, resety, time || 0); }, _wheel: function (e) { var that = this, wheeldeltax, wheeldeltay, deltax, deltay, deltascale; if ('wheeldeltax' in e) { wheeldeltax = e.wheeldeltax / 12; wheeldeltay = e.wheeldeltay / 12; } else if('wheeldelta' in e) { wheeldeltax = wheeldeltay = e.wheeldelta / 12; } else if ('detail' in e) { wheeldeltax = wheeldeltay = -e.detail * 3; } else { return; } if (that.options.wheelaction == 'zoom') { deltascale = that.scale * math.pow(2, 1/3 * (wheeldeltay ? wheeldeltay / math.abs(wheeldeltay) : 0)); if (deltascale < that.options.zoommin) deltascale = that.options.zoommin; if (deltascale > that.options.zoommax) deltascale = that.options.zoommax; if (deltascale != that.scale) { if (!that.wheelzoomcount && that.options.onzoomstart) that.options.onzoomstart.call(that, e); that.wheelzoomcount++; that.zoom(e.pagex, e.pagey, deltascale, 400); settimeout(function() { that.wheelzoomcount--; if (!that.wheelzoomcount && that.options.onzoomend) that.options.onzoomend.call(that, e); }, 400); } return; } deltax = that.x + wheeldeltax; deltay = that.y + wheeldeltay; if (deltax > 0) deltax = 0; else if (deltax < that.maxscrollx) deltax = that.maxscrollx; if (deltay > that.minscrolly) deltay = that.minscrolly; else if (deltay < that.maxscrolly) deltay = that.maxscrolly; if (that.maxscrolly < 0) { that.scrollto(deltax, deltay, 0); } }, _transitionend: function (e) { var that = this; if (e.target != that.scroller) return; that._unbind(trnend_ev); that._startani(); }, /** * * utilities * */ _startani: function () { var that = this, startx = that.x, starty = that.y, starttime = date.now(), step, easeout, animate; if (that.animating) return; if (!that.steps.length) { that._resetpos(400); return; } step = that.steps.shift(); if (step.x == startx && step.y == starty) step.time = 0; that.animating = true; that.moved = true; if (that.options.usetransition) { that._transitiontime(step.time); that._pos(step.x, step.y); that.animating = false; if (step.time) that._bind(trnend_ev); else that._resetpos(0); return; } animate = function () { var now = date.now(), newx, newy; if (now >= starttime + step.time) { that._pos(step.x, step.y); that.animating = false; if (that.options.onanimationend) that.options.onanimationend.call(that); // execute custom code on animation end that._startani(); return; } now = (now - starttime) / step.time - 1; easeout = m.sqrt(1 - now * now); newx = (step.x - startx) * easeout + startx; newy = (step.y - starty) * easeout + starty; that._pos(newx, newy); if (that.animating) that.anitime = nextframe(animate); }; animate(); }, _transitiontime: function (time) { time += 'ms'; this.scroller.style[transitionduration] = time; if (this.hscrollbar) this.hscrollbarindicator.style[transitionduration] = time; if (this.vscrollbar) this.vscrollbarindicator.style[transitionduration] = time; }, _momentum: function (dist, time, maxdistupper, maxdistlower, size) { var deceleration = 0.0006, speed = m.abs(dist) / time, newdist = (speed * speed) / (2 * deceleration), newtime = 0, outsidedist = 0; // proportinally reduce speed if we are outside of the boundaries if (dist > 0 && newdist > maxdistupper) { outsidedist = size / (6 / (newdist / speed * deceleration)); maxdistupper = maxdistupper + outsidedist; speed = speed * maxdistupper / newdist; newdist = maxdistupper; } else if (dist < 0 && newdist > maxdistlower) { outsidedist = size / (6 / (newdist / speed * deceleration)); maxdistlower = maxdistlower + outsidedist; speed = speed * maxdistlower / newdist; newdist = maxdistlower; } newdist = newdist * (dist < 0 ? -1 : 1); newtime = speed / deceleration; return { dist: newdist, time: m.round(newtime) }; }, _offset: function (el) { var left = -el.offsetleft, top = -el.offsettop; while (el = el.offsetparent) { left -= el.offsetleft; top -= el.offsettop; } if (el != this.wrapper) { left *= this.scale; top *= this.scale; } return { left: left, top: top }; }, _snap: function (x, y) { var that = this, i, l, page, time, sizex, sizey; // check page x page = that.pagesx.length - 1; for (i=0, l=that.pagesx.length; i= that.pagesx[i]) { page = i; break; } } if (page == that.currpagex && page > 0 && that.dirx < 0) page--; x = that.pagesx[page]; sizex = m.abs(x - that.pagesx[that.currpagex]); sizex = sizex ? m.abs(that.x - x) / sizex * 500 : 0; that.currpagex = page; // check page y page = that.pagesy.length-1; for (i=0; i= that.pagesy[i]) { page = i; break; } } if (page == that.currpagey && page > 0 && that.diry < 0) page--; y = that.pagesy[page]; sizey = m.abs(y - that.pagesy[that.currpagey]); sizey = sizey ? m.abs(that.y - y) / sizey * 500 : 0; that.currpagey = page; // snap with constant speed (proportional duration) time = m.round(m.max(sizex, sizey)) || 200; return { x: x, y: y, time: time }; }, _bind: function (type, el, bubble) { (el || this.scroller).addeventlistener(type, this, !!bubble); }, _unbind: function (type, el, bubble) { (el || this.scroller).removeeventlistener(type, this, !!bubble); }, /** * * public methods * */ destroy: function () { var that = this; that.scroller.style[transform] = ''; // remove the scrollbars that.hscrollbar = false; that.vscrollbar = false; that._scrollbar('h'); that._scrollbar('v'); // remove the event listeners that._unbind(resize_ev, window); that._unbind(start_ev); that._unbind(move_ev, window); that._unbind(end_ev, window); that._unbind(cancel_ev, window); if (!that.options.hastouch) { that._unbind('dommousescroll'); that._unbind('mousewheel'); } if (that.options.usetransition) that._unbind(trnend_ev); if (that.options.checkdomchanges) clearinterval(that.checkdomtime); if (that.options.ondestroy) that.options.ondestroy.call(that); }, refresh: function () { var that = this, offset, i, l, els, pos = 0, page = 0; if (that.scale < that.options.zoommin) that.scale = that.options.zoommin; that.wrapperw = that.wrapper.clientwidth || 1; that.wrapperh = that.wrapper.clientheight || 1; that.minscrolly = -that.options.topoffset || 0; that.scrollerw = m.round(that.scroller.offsetwidth * that.scale); that.scrollerh = m.round((that.scroller.offsetheight + that.minscrolly) * that.scale); that.maxscrollx = that.wrapperw - that.scrollerw; that.maxscrolly = that.wrapperh - that.scrollerh + that.minscrolly; that.dirx = 0; that.diry = 0; if (that.options.onrefresh) that.options.onrefresh.call(that); that.hscroll = that.options.hscroll && that.maxscrollx < 0; that.vscroll = that.options.vscroll && (!that.options.bouncelock && !that.hscroll || that.scrollerh > that.wrapperh); that.hscrollbar = that.hscroll && that.options.hscrollbar; that.vscrollbar = that.vscroll && that.options.vscrollbar && that.scrollerh > that.wrapperh; offset = that._offset(that.wrapper); that.wrapperoffsetleft = -offset.left; that.wrapperoffsettop = -offset.top; // prepare snap if (typeof that.options.snap == 'string') { that.pagesx = []; that.pagesy = []; els = that.scroller.queryselectorall(that.options.snap); for (i=0, l=els.length; i= that.maxscrollx) { that.pagesx[page] = pos; pos = pos - that.wrapperw; page++; } if (that.maxscrollx%that.wrapperw) that.pagesx[that.pagesx.length] = that.maxscrollx - that.pagesx[that.pagesx.length-1] + that.pagesx[that.pagesx.length-1]; pos = 0; page = 0; that.pagesy = []; while (pos >= that.maxscrolly) { that.pagesy[page] = pos; pos = pos - that.wrapperh; page++; } if (that.maxscrolly%that.wrapperh) that.pagesy[that.pagesy.length] = that.maxscrolly - that.pagesy[that.pagesy.length-1] + that.pagesy[that.pagesy.length-1]; } // prepare the scrollbars that._scrollbar('h'); that._scrollbar('v'); if (!that.zoomed) { that.scroller.style[transitionduration] = '0'; that._resetpos(400); } }, scrollto: function (x, y, time, relative) { var that = this, step = x, i, l; that.stop(); if (!step.length) step = [{ x: x, y: y, time: time, relative: relative }]; for (i=0, l=step.length; i 0 ? 0 : pos.left < that.maxscrollx ? that.maxscrollx : pos.left; pos.top = pos.top > that.minscrolly ? that.minscrolly : pos.top < that.maxscrolly ? that.maxscrolly : pos.top; time = time === undefined ? m.max(m.abs(pos.left)*2, m.abs(pos.top)*2) : time; that.scrollto(pos.left, pos.top, time); }, scrolltopage: function (pagex, pagey, time) { var that = this, x, y; time = time === undefined ? 400 : time; if (that.options.onscrollstart) that.options.onscrollstart.call(that); if (that.options.snap) { pagex = pagex == 'next' ? that.currpagex+1 : pagex == 'prev' ? that.currpagex-1 : pagex; pagey = pagey == 'next' ? that.currpagey+1 : pagey == 'prev' ? that.currpagey-1 : pagey; pagex = pagex < 0 ? 0 : pagex > that.pagesx.length-1 ? that.pagesx.length-1 : pagex; pagey = pagey < 0 ? 0 : pagey > that.pagesy.length-1 ? that.pagesy.length-1 : pagey; that.currpagex = pagex; that.currpagey = pagey; x = that.pagesx[pagex]; y = that.pagesy[pagey]; } else { x = -that.wrapperw * pagex; y = -that.wrapperh * pagey; if (x < that.maxscrollx) x = that.maxscrollx; if (y < that.maxscrolly) y = that.maxscrolly; } that.scrollto(x, y, time); }, disable: function () { this.stop(); this._resetpos(0); this.enabled = false; // if disabled after touchstart we make sure that there are no left over events this._unbind(move_ev, window); this._unbind(end_ev, window); this._unbind(cancel_ev, window); }, enable: function () { this.enabled = true; }, stop: function () { if (this.options.usetransition) this._unbind(trnend_ev); else cancelframe(this.anitime); this.steps = []; this.moved = false; this.animating = false; }, zoom: function (x, y, scale, time) { var that = this, relscale = scale / that.scale; if (!that.options.usetransform) return; that.zoomed = true; time = time === undefined ? 200 : time; x = x - that.wrapperoffsetleft - that.x; y = y - that.wrapperoffsettop - that.y; that.x = x - x * relscale + that.x; that.y = y - y * relscale + that.y; that.scale = scale; that.refresh(); that.x = that.x > 0 ? 0 : that.x < that.maxscrollx ? that.maxscrollx : that.x; that.y = that.y > that.minscrolly ? that.minscrolly : that.y < that.maxscrolly ? that.maxscrolly : that.y; that.scroller.style[transitionduration] = time + 'ms'; that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px) scale(' + scale + ')' + translatez; that.zoomed = false; }, isready: function () { return !this.moved && !this.zoomed && !this.animating; } }; function prefixstyle (style) { if ( vendor === '' ) return style; style = style.charat(0).touppercase() + style.substr(1); return vendor + style; } dummystyle = null; // for the sake of it if (typeof exports !== 'undefined') exports.iscroll = iscroll; else window.iscroll = iscroll; })(window, document);