/** * customized version of iscroll.js 0.1.3 * it fixes bugs affecting its integration with fullpage.js * @license */ /*! iscroll v5.2.0 ~ (c) 2008-2016 matteo spinelli ~ http://cubiq.org/license */ (function (window, document, math) { var raf = window.requestanimationframe || window.webkitrequestanimationframe || window.mozrequestanimationframe || window.orequestanimationframe || window.msrequestanimationframe || function (callback) { window.settimeout(callback, 1000 / 60); }; var utils = (function () { var me = {}; var _elementstyle = document.createelement('div').style; var _vendor = (function () { var vendors = ['t', 'webkitt', 'mozt', 'mst', 'ot'], transform, i = 0, l = vendors.length; for ( ; i < l; i++ ) { transform = vendors[i] + 'ransform'; if ( transform in _elementstyle ) return vendors[i].substr(0, vendors[i].length-1); } return false; })(); function _prefixstyle (style) { if ( _vendor === false ) return false; if ( _vendor === '' ) return style; return _vendor + style.charat(0).touppercase() + style.substr(1); } me.gettime = date.now || function gettime () { return new date().gettime(); }; me.extend = function (target, obj) { for ( var i in obj ) { target[i] = obj[i]; } }; me.addevent = function (el, type, fn, capture) { el.addeventlistener(type, fn, !!capture); }; me.removeevent = function (el, type, fn, capture) { el.removeeventlistener(type, fn, !!capture); }; me.prefixpointerevent = function (pointerevent) { return window.mspointerevent ? 'mspointer' + pointerevent.charat(7).touppercase() + pointerevent.substr(8): pointerevent; }; me.momentum = function (current, start, time, lowermargin, wrappersize, deceleration) { var distance = current - start, speed = math.abs(distance) / time, destination, duration; deceleration = deceleration === undefined ? 0.0006 : deceleration; destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 ); duration = speed / deceleration; if ( destination < lowermargin ) { destination = wrappersize ? lowermargin - ( wrappersize / 2.5 * ( speed / 8 ) ) : lowermargin; distance = math.abs(destination - current); duration = distance / speed; } else if ( destination > 0 ) { destination = wrappersize ? wrappersize / 2.5 * ( speed / 8 ) : 0; distance = math.abs(current) + destination; duration = distance / speed; } return { destination: math.round(destination), duration: duration }; }; var _transform = _prefixstyle('transform'); me.extend(me, { hastransform: _transform !== false, hasperspective: _prefixstyle('perspective') in _elementstyle, hastouch: 'ontouchstart' in window, haspointer: !!(window.pointerevent || window.mspointerevent), // ie10 is prefixed hastransition: _prefixstyle('transition') in _elementstyle }); /* this should find all android browsers lower than build 535.19 (both stock browser and webview) - galaxy s2 is ok - 2.3.6 : `applewebkit/533.1 (khtml, like gecko) version/4.0 mobile safari/533.1` - 4.0.4 : `applewebkit/534.30 (khtml, like gecko) version/4.0 mobile safari/534.30` - galaxy s3 is badandroid (stock brower, webview) `applewebkit/534.30 (khtml, like gecko) version/4.0 mobile safari/534.30` - galaxy s4 is badandroid (stock brower, webview) `applewebkit/534.30 (khtml, like gecko) version/4.0 mobile safari/534.30` - galaxy s5 is ok `applewebkit/537.36 (khtml, like gecko) version/4.0 mobile safari/537.36 (chrome/)` - galaxy s6 is ok `applewebkit/537.36 (khtml, like gecko) version/4.0 mobile safari/537.36 (chrome/)` */ me.isbadandroid = (function() { var appversion = window.navigator.appversion; // android browser is not a chrome browser. if (/android/.test(appversion) && !(/chrome\/\d/.test(appversion))) { var safariversion = appversion.match(/safari\/(\d+.\d)/); if(safariversion && typeof safariversion === "object" && safariversion.length >= 2) { return parsefloat(safariversion[1]) < 535.19; } else { return true; } } else { return false; } })(); me.extend(me.style = {}, { transform: _transform, transitiontimingfunction: _prefixstyle('transitiontimingfunction'), transitionduration: _prefixstyle('transitionduration'), transitiondelay: _prefixstyle('transitiondelay'), transformorigin: _prefixstyle('transformorigin') }); me.hasclass = function (e, c) { var re = new regexp("(^|\\s)" + c + "(\\s|$)"); return re.test(e.classname); }; me.addclass = function (e, c) { if ( me.hasclass(e, c) ) { return; } var newclass = e.classname.split(' '); newclass.push(c); e.classname = newclass.join(' '); }; me.removeclass = function (e, c) { if ( !me.hasclass(e, c) ) { return; } var re = new regexp("(^|\\s)" + c + "(\\s|$)", 'g'); e.classname = e.classname.replace(re, ' '); }; me.offset = function (el) { var left = -el.offsetleft, top = -el.offsettop; // jshint -w084 while (el = el.offsetparent) { left -= el.offsetleft; top -= el.offsettop; } // jshint +w084 return { left: left, top: top }; }; me.preventdefaultexception = function (el, exceptions) { for ( var i in exceptions ) { if ( exceptions[i].test(el[i]) ) { return true; } } return false; }; me.extend(me.eventtype = {}, { touchstart: 1, touchmove: 1, touchend: 1, mousedown: 2, mousemove: 2, mouseup: 2, pointerdown: 3, pointermove: 3, pointerup: 3, mspointerdown: 3, mspointermove: 3, mspointerup: 3 }); me.extend(me.ease = {}, { quadratic: { style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)', fn: function (k) { return k * ( 2 - k ); } }, circular: { 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) fn: function (k) { return math.sqrt( 1 - ( --k * k ) ); } }, back: { style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)', fn: function (k) { var b = 4; return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1; } }, bounce: { style: '', fn: function (k) { if ( ( k /= 1 ) < ( 1 / 2.75 ) ) { return 7.5625 * k * k; } else if ( k < ( 2 / 2.75 ) ) { return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; } else if ( k < ( 2.5 / 2.75 ) ) { return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; } else { return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; } } }, elastic: { style: '', fn: function (k) { var f = 0.22, e = 0.4; if ( k === 0 ) { return 0; } if ( k == 1 ) { return 1; } return ( e * math.pow( 2, - 10 * k ) * math.sin( ( k - f / 4 ) * ( 2 * math.pi ) / f ) + 1 ); } } }); me.tap = function (e, eventname) { var ev = document.createevent('event'); ev.initevent(eventname, true, true); ev.pagex = e.pagex; ev.pagey = e.pagey; e.target.dispatchevent(ev); }; me.click = function (e) { var target = e.target, ev; if ( !(/(select|input|textarea)/i).test(target.tagname) ) { // https://developer.mozilla.org/en-us/docs/web/api/mouseevent/initmouseevent // initmouseevent is deprecated. ev = document.createevent(window.mouseevent ? 'mouseevents' : 'event'); ev.initevent('click', true, true); ev.view = e.view || window; ev.detail = 1; ev.screenx = target.screenx || 0; ev.screeny = target.screeny || 0; ev.clientx = target.clientx || 0; ev.clienty = target.clienty || 0; ev.ctrlkey = !!e.ctrlkey; ev.altkey = !!e.altkey; ev.shiftkey = !!e.shiftkey; ev.metakey = !!e.metakey; ev.button = 0; ev.relatedtarget = null; ev._constructed = true; target.dispatchevent(ev); } }; return me; })(); function iscroll (el, options) { this.wrapper = typeof el == 'string' ? document.queryselector(el) : el; this.scroller = this.wrapper.children[0]; this.scrollerstyle = this.scroller.style; // cache style for better performance this.options = { resizescrollbars: true, mousewheelspeed: 20, snapthreshold: 0.334, // insert point: options disablepointer : !utils.haspointer, disabletouch : utils.haspointer || !utils.hastouch, disablemouse : utils.haspointer || utils.hastouch, startx: 0, starty: 0, scrolly: true, directionlockthreshold: 5, momentum: true, bounce: true, bouncetime: 600, bounceeasing: '', preventdefault: true, preventdefaultexception: { tagname: /^(input|textarea|button|select|label)$/ }, hwcompositing: true, usetransition: true, usetransform: true, bindtowrapper: typeof window.onmousedown === "undefined" }; for ( var i in options ) { this.options[i] = options[i]; } // normalize options this.translatez = this.options.hwcompositing && utils.hasperspective ? ' translatez(0)' : ''; this.options.usetransition = utils.hastransition && this.options.usetransition; this.options.usetransform = utils.hastransform && this.options.usetransform; this.options.eventpassthrough = this.options.eventpassthrough === true ? 'vertical' : this.options.eventpassthrough; this.options.preventdefault = !this.options.eventpassthrough && this.options.preventdefault; // if you want eventpassthrough i have to lock one of the axes this.options.scrolly = this.options.eventpassthrough == 'vertical' ? false : this.options.scrolly; this.options.scrollx = this.options.eventpassthrough == 'horizontal' ? false : this.options.scrollx; // with eventpassthrough we also need lockdirection mechanism this.options.freescroll = this.options.freescroll && !this.options.eventpassthrough; this.options.directionlockthreshold = this.options.eventpassthrough ? 0 : this.options.directionlockthreshold; this.options.bounceeasing = typeof this.options.bounceeasing == 'string' ? utils.ease[this.options.bounceeasing] || utils.ease.circular : this.options.bounceeasing; this.options.resizepolling = this.options.resizepolling === undefined ? 60 : this.options.resizepolling; if ( this.options.tap === true ) { this.options.tap = 'tap'; } // https://github.com/cubiq/iscroll/issues/1029 if (!this.options.usetransition && !this.options.usetransform) { if(!(/relative|absolute/i).test(this.scrollerstyle.position)) { this.scrollerstyle.position = "relative"; } } if ( this.options.shrinkscrollbars == 'scale' ) { this.options.usetransition = false; } this.options.invertwheeldirection = this.options.invertwheeldirection ? -1 : 1; // insert point: normalization // some defaults this.x = 0; this.y = 0; this.directionx = 0; this.directiony = 0; this._events = {}; // insert point: defaults this._init(); this.refresh(); this.scrollto(this.options.startx, this.options.starty); this.enable(); } iscroll.prototype = { version: '5.2.0', _init: function () { this._initevents(); if ( this.options.scrollbars || this.options.indicators ) { this._initindicators(); } if ( this.options.mousewheel ) { this._initwheel(); } if ( this.options.snap ) { this._initsnap(); } if ( this.options.keybindings ) { this._initkeys(); } // insert point: _init }, destroy: function () { this._initevents(true); cleartimeout(this.resizetimeout); this.resizetimeout = null; this._execevent('destroy'); }, _transitionend: function (e) { if ( e.target != this.scroller || !this.isintransition ) { return; } this._transitiontime(); if ( !this.resetposition(this.options.bouncetime) ) { this.isintransition = false; this._execevent('scrollend'); } }, _start: function (e) { // react to left mouse button only if ( utils.eventtype[e.type] != 1 ) { // for button property // http://unixpapa.com/js/mouse.html var button; if (!e.which) { /* ie case */ button = (e.button < 2) ? 0 : ((e.button == 4) ? 1 : 2); } else { /* all others */ button = e.button; } if ( button !== 0 ) { return; } } if ( !this.enabled || (this.initiated && utils.eventtype[e.type] !== this.initiated) ) { return; } if ( this.options.preventdefault && !utils.isbadandroid && !utils.preventdefaultexception(e.target, this.options.preventdefaultexception) ) { e.preventdefault(); } var point = e.touches ? e.touches[0] : e, pos; this.initiated = utils.eventtype[e.type]; this.moved = false; this.distx = 0; this.disty = 0; this.directionx = 0; this.directiony = 0; this.directionlocked = 0; this.starttime = utils.gettime(); if ( this.options.usetransition && this.isintransition ) { this._transitiontime(); this.isintransition = false; pos = this.getcomputedposition(); this._translate(math.round(pos.x), math.round(pos.y)); this._execevent('scrollend'); } else if ( !this.options.usetransition && this.isanimating ) { this.isanimating = false; this._execevent('scrollend'); } this.startx = this.x; this.starty = this.y; this.absstartx = this.x; this.absstarty = this.y; this.pointx = point.pagex; this.pointy = point.pagey; this._execevent('beforescrollstart'); }, _move: function (e) { if ( !this.enabled || utils.eventtype[e.type] !== this.initiated ) { return; } if ( this.options.preventdefault ) { // increases performance on android? todo: check! e.preventdefault(); } var point = e.touches ? e.touches[0] : e, deltax = point.pagex - this.pointx, deltay = point.pagey - this.pointy, timestamp = utils.gettime(), newx, newy, absdistx, absdisty; this.pointx = point.pagex; this.pointy = point.pagey; this.distx += deltax; this.disty += deltay; absdistx = math.abs(this.distx); absdisty = math.abs(this.disty); // we need to move at least 10 pixels for the scrolling to initiate if ( timestamp - this.endtime > 300 && (absdistx < 10 && absdisty < 10) ) { return; } // if you are scrolling in one direction lock the other if ( !this.directionlocked && !this.options.freescroll ) { if ( absdistx > absdisty + this.options.directionlockthreshold ) { this.directionlocked = 'h'; // lock horizontally } else if ( absdisty >= absdistx + this.options.directionlockthreshold ) { this.directionlocked = 'v'; // lock vertically } else { this.directionlocked = 'n'; // no lock } } if ( this.directionlocked == 'h' ) { if ( this.options.eventpassthrough == 'vertical' ) { e.preventdefault(); } else if ( this.options.eventpassthrough == 'horizontal' ) { this.initiated = false; return; } deltay = 0; } else if ( this.directionlocked == 'v' ) { if ( this.options.eventpassthrough == 'horizontal' ) { e.preventdefault(); } else if ( this.options.eventpassthrough == 'vertical' ) { this.initiated = false; return; } deltax = 0; } deltax = this.hashorizontalscroll ? deltax : 0; deltay = this.hasverticalscroll ? deltay : 0; newx = this.x + deltax; newy = this.y + deltay; // slow down if outside of the boundaries if ( newx > 0 || newx < this.maxscrollx ) { newx = this.options.bounce ? this.x + deltax / 3 : newx > 0 ? 0 : this.maxscrollx; } if ( newy > 0 || newy < this.maxscrolly ) { newy = this.options.bounce ? this.y + deltay / 3 : newy > 0 ? 0 : this.maxscrolly; } this.directionx = deltax > 0 ? -1 : deltax < 0 ? 1 : 0; this.directiony = deltay > 0 ? -1 : deltay < 0 ? 1 : 0; if ( !this.moved ) { this._execevent('scrollstart'); } this.moved = true; this._translate(newx, newy); /* replace start: _move */ if ( timestamp - this.starttime > 300 ) { this.starttime = timestamp; this.startx = this.x; this.starty = this.y; } /* replace end: _move */ }, _end: function (e) { if ( !this.enabled || utils.eventtype[e.type] !== this.initiated ) { return; } if ( this.options.preventdefault && !utils.preventdefaultexception(e.target, this.options.preventdefaultexception) ) { e.preventdefault(); } var point = e.changedtouches ? e.changedtouches[0] : e, momentumx, momentumy, duration = utils.gettime() - this.starttime, newx = math.round(this.x), newy = math.round(this.y), distancex = math.abs(newx - this.startx), distancey = math.abs(newy - this.starty), time = 0, easing = ''; this.isintransition = 0; this.initiated = 0; this.endtime = utils.gettime(); // reset if we are outside of the boundaries if ( this.resetposition(this.options.bouncetime) ) { return; } this.scrollto(newx, newy); // ensures that the last position is rounded // we scrolled less than 10 pixels if ( !this.moved ) { if ( this.options.tap ) { utils.tap(e, this.options.tap); } if ( this.options.click ) { utils.click(e); } this._execevent('scrollcancel'); return; } if ( this._events.flick && duration < 200 && distancex < 100 && distancey < 100 ) { this._execevent('flick'); return; } // start momentum animation if needed if ( this.options.momentum && duration < 300 ) { 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 }; 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 }; newx = momentumx.destination; newy = momentumy.destination; time = math.max(momentumx.duration, momentumy.duration); this.isintransition = 1; } if ( this.options.snap ) { var snap = this._nearestsnap(newx, newy); this.currentpage = snap; time = this.options.snapspeed || math.max( math.max( math.min(math.abs(newx - snap.x), 1000), math.min(math.abs(newy - snap.y), 1000) ), 300); newx = snap.x; newy = snap.y; this.directionx = 0; this.directiony = 0; easing = this.options.bounceeasing; } // insert point: _end if ( newx != this.x || newy != this.y ) { // change easing function when scroller goes out of the boundaries if ( newx > 0 || newx < this.maxscrollx || newy > 0 || newy < this.maxscrolly ) { easing = utils.ease.quadratic; } this.scrollto(newx, newy, time, easing); return; } this._execevent('scrollend'); }, _resize: function () { var that = this; cleartimeout(this.resizetimeout); this.resizetimeout = settimeout(function () { that.refresh(); }, this.options.resizepolling); }, resetposition: function (time) { var x = this.x, y = this.y; time = time || 0; if ( !this.hashorizontalscroll || this.x > 0 ) { x = 0; } else if ( this.x < this.maxscrollx ) { x = this.maxscrollx; } if ( !this.hasverticalscroll || this.y > 0 ) { y = 0; } else if ( this.y < this.maxscrolly ) { y = this.maxscrolly; } if ( x == this.x && y == this.y ) { return false; } this.scrollto(x, y, time, this.options.bounceeasing); return true; }, disable: function () { this.enabled = false; }, enable: function () { this.enabled = true; }, refresh: function () { var rf = this.wrapper.offsetheight; // force reflow this.wrapperwidth = this.wrapper.clientwidth; this.wrapperheight = this.wrapper.clientheight; /* replace start: refresh */ this.scrollerwidth = this.scroller.offsetwidth; this.scrollerheight = this.scroller.offsetheight; this.maxscrollx = this.wrapperwidth - this.scrollerwidth; this.maxscrolly = this.wrapperheight - this.scrollerheight; /* replace end: refresh */ this.hashorizontalscroll = this.options.scrollx && this.maxscrollx < 0; this.hasverticalscroll = this.options.scrolly && this.maxscrolly < 0; if ( !this.hashorizontalscroll ) { this.maxscrollx = 0; this.scrollerwidth = this.wrapperwidth; } if ( !this.hasverticalscroll ) { this.maxscrolly = 0; this.scrollerheight = this.wrapperheight; } this.endtime = 0; this.directionx = 0; this.directiony = 0; this.wrapperoffset = utils.offset(this.wrapper); this._execevent('refresh'); this.resetposition(); // insert point: _refresh }, on: function (type, fn) { if ( !this._events[type] ) { this._events[type] = []; } this._events[type].push(fn); }, off: function (type, fn) { if ( !this._events[type] ) { return; } var index = this._events[type].indexof(fn); if ( index > -1 ) { this._events[type].splice(index, 1); } }, _execevent: function (type) { if ( !this._events[type] ) { return; } var i = 0, l = this._events[type].length; if ( !l ) { return; } for ( ; i < l; i++ ) { this._events[type][i].apply(this, [].slice.call(arguments, 1)); } }, scrollby: function (x, y, time, easing) { x = this.x + x; y = this.y + y; time = time || 0; this.scrollto(x, y, time, easing); }, scrollto: function (x, y, time, easing) { easing = easing || utils.ease.circular; this.isintransition = this.options.usetransition && time > 0; var transitiontype = this.options.usetransition && easing.style; if ( !time || transitiontype ) { if(transitiontype) { this._transitiontimingfunction(easing.style); this._transitiontime(time); } this._translate(x, y); } else { this._animate(x, y, time, easing.fn); } }, scrolltoelement: function (el, time, offsetx, offsety, easing) { el = el.nodetype ? el : this.scroller.queryselector(el); if ( !el ) { return; } var pos = utils.offset(el); pos.left -= this.wrapperoffset.left; pos.top -= this.wrapperoffset.top; // if offsetx/y are true we center the element to the screen if ( offsetx === true ) { offsetx = math.round(el.offsetwidth / 2 - this.wrapper.offsetwidth / 2); } if ( offsety === true ) { offsety = math.round(el.offsetheight / 2 - this.wrapper.offsetheight / 2); } pos.left -= offsetx || 0; pos.top -= offsety || 0; pos.left = pos.left > 0 ? 0 : pos.left < this.maxscrollx ? this.maxscrollx : pos.left; pos.top = pos.top > 0 ? 0 : pos.top < this.maxscrolly ? this.maxscrolly : pos.top; time = time === undefined || time === null || time === 'auto' ? math.max(math.abs(this.x-pos.left), math.abs(this.y-pos.top)) : time; this.scrollto(pos.left, pos.top, time, easing); }, _transitiontime: function (time) { if (!this.options.usetransition) { return; } time = time || 0; var durationprop = utils.style.transitionduration; if(!durationprop) { return; } this.scrollerstyle[durationprop] = time + 'ms'; if ( !time && utils.isbadandroid ) { this.scrollerstyle[durationprop] = '0.0001ms'; // remove 0.0001ms var self = this; raf(function() { if(self.scrollerstyle[durationprop] === '0.0001ms') { self.scrollerstyle[durationprop] = '0s'; } }); } if ( this.indicators ) { for ( var i = this.indicators.length; i--; ) { this.indicators[i].transitiontime(time); } } // insert point: _transitiontime }, _transitiontimingfunction: function (easing) { this.scrollerstyle[utils.style.transitiontimingfunction] = easing; if ( this.indicators ) { for ( var i = this.indicators.length; i--; ) { this.indicators[i].transitiontimingfunction(easing); } } // insert point: _transitiontimingfunction }, _translate: function (x, y) { if ( this.options.usetransform ) { /* replace start: _translate */ this.scrollerstyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translatez; /* replace end: _translate */ } else { x = math.round(x); y = math.round(y); this.scrollerstyle.left = x + 'px'; this.scrollerstyle.top = y + 'px'; } this.x = x; this.y = y; if ( this.indicators ) { for ( var i = this.indicators.length; i--; ) { this.indicators[i].updateposition(); } } // insert point: _translate }, _initevents: function (remove) { var eventtype = remove ? utils.removeevent : utils.addevent, target = this.options.bindtowrapper ? this.wrapper : window; eventtype(window, 'orientationchange', this); eventtype(window, 'resize', this); if ( this.options.click ) { eventtype(this.wrapper, 'click', this, true); } if ( !this.options.disablemouse ) { eventtype(this.wrapper, 'mousedown', this); eventtype(target, 'mousemove', this); eventtype(target, 'mousecancel', this); eventtype(target, 'mouseup', this); } if ( utils.haspointer && !this.options.disablepointer ) { eventtype(this.wrapper, utils.prefixpointerevent('pointerdown'), this); eventtype(target, utils.prefixpointerevent('pointermove'), this); eventtype(target, utils.prefixpointerevent('pointercancel'), this); eventtype(target, utils.prefixpointerevent('pointerup'), this); } if ( utils.hastouch && !this.options.disabletouch ) { eventtype(this.wrapper, 'touchstart', this); eventtype(target, 'touchmove', this); eventtype(target, 'touchcancel', this); eventtype(target, 'touchend', this); } eventtype(this.scroller, 'transitionend', this); eventtype(this.scroller, 'webkittransitionend', this); eventtype(this.scroller, 'otransitionend', this); eventtype(this.scroller, 'mstransitionend', this); }, getcomputedposition: function () { var matrix = window.getcomputedstyle(this.scroller, null), x, y; if ( this.options.usetransform ) { matrix = matrix[utils.style.transform].split(')')[0].split(', '); x = +(matrix[12] || matrix[4]); y = +(matrix[13] || matrix[5]); } else { x = +matrix.left.replace(/[^-\d.]/g, ''); y = +matrix.top.replace(/[^-\d.]/g, ''); } return { x: x, y: y }; }, _initindicators: function () { var interactive = this.options.interactivescrollbars, customstyle = typeof this.options.scrollbars != 'string', indicators = [], indicator; console.warn(interactive ); var that = this; this.indicators = []; if ( this.options.scrollbars ) { // vertical scrollbar if ( this.options.scrolly ) { indicator = { el: createdefaultscrollbar('v', interactive, this.options.scrollbars), interactive: interactive, defaultscrollbars: true, customstyle: customstyle, resize: this.options.resizescrollbars, shrink: this.options.shrinkscrollbars, fade: this.options.fadescrollbars, listenx: false }; this.wrapper.appendchild(indicator.el); indicators.push(indicator); } // horizontal scrollbar if ( this.options.scrollx ) { indicator = { el: createdefaultscrollbar('h', interactive, this.options.scrollbars), interactive: interactive, defaultscrollbars: true, customstyle: customstyle, resize: this.options.resizescrollbars, shrink: this.options.shrinkscrollbars, fade: this.options.fadescrollbars, listeny: false }; this.wrapper.appendchild(indicator.el); indicators.push(indicator); } } if ( this.options.indicators ) { // todo: check concat compatibility indicators = indicators.concat(this.options.indicators); } for ( var i = indicators.length; i--; ) { this.indicators.push( new indicator(this, indicators[i]) ); } // todo: check if we can use array.map (wide compatibility and performance issues) function _indicatorsmap (fn) { if (that.indicators) { for ( var i = that.indicators.length; i--; ) { fn.call(that.indicators[i]); } } } if ( this.options.fadescrollbars ) { this.on('scrollend', function () { _indicatorsmap(function () { this.fade(); }); }); this.on('scrollcancel', function () { _indicatorsmap(function () { this.fade(); }); }); this.on('scrollstart', function () { _indicatorsmap(function () { this.fade(1); }); }); this.on('beforescrollstart', function () { _indicatorsmap(function () { this.fade(1, true); }); }); } this.on('refresh', function () { _indicatorsmap(function () { this.refresh(); }); }); this.on('destroy', function () { _indicatorsmap(function () { this.destroy(); }); delete this.indicators; }); }, _initwheel: function () { utils.addevent(this.wrapper, 'wheel', this); utils.addevent(this.wrapper, 'mousewheel', this); utils.addevent(this.wrapper, 'dommousescroll', this); this.on('destroy', function () { cleartimeout(this.wheeltimeout); this.wheeltimeout = null; utils.removeevent(this.wrapper, 'wheel', this); utils.removeevent(this.wrapper, 'mousewheel', this); utils.removeevent(this.wrapper, 'dommousescroll', this); }); }, _wheel: function (e) { if ( !this.enabled ) { return; } // in ie we can not preventdefault() or otherwise it won't scroll to the prev/next section. // i commented on it here back then: https://github.com/cubiq/iscroll/issues/980 // isie taken from: https://stackoverflow.com/a/49986758/1081396 var isie = window.navigator.useragent.match(/(msie|trident)/); if(!isie){ e.preventdefault(); } var wheeldeltax, wheeldeltay, newx, newy, that = this; if ( this.wheeltimeout === undefined ) { that._execevent('scrollstart'); } // execute the scrollend event after 400ms the wheel stopped scrolling cleartimeout(this.wheeltimeout); this.wheeltimeout = settimeout(function () { if(!that.options.snap) { that._execevent('scrollend'); } that.wheeltimeout = undefined; }, 400); if ( 'deltax' in e ) { if (e.deltamode === 1) { wheeldeltax = -e.deltax * this.options.mousewheelspeed; wheeldeltay = -e.deltay * this.options.mousewheelspeed; } else { wheeldeltax = -e.deltax; wheeldeltay = -e.deltay; } } else if ( 'wheeldeltax' in e ) { wheeldeltax = e.wheeldeltax / 120 * this.options.mousewheelspeed; wheeldeltay = e.wheeldeltay / 120 * this.options.mousewheelspeed; } else if ( 'wheeldelta' in e ) { wheeldeltax = wheeldeltay = e.wheeldelta / 120 * this.options.mousewheelspeed; } else if ( 'detail' in e ) { wheeldeltax = wheeldeltay = -e.detail / 3 * this.options.mousewheelspeed; } else { return; } wheeldeltax *= this.options.invertwheeldirection; wheeldeltay *= this.options.invertwheeldirection; if ( !this.hasverticalscroll ) { wheeldeltax = wheeldeltay; wheeldeltay = 0; } if ( this.options.snap ) { newx = this.currentpage.pagex; newy = this.currentpage.pagey; if ( wheeldeltax > 0 ) { newx--; } else if ( wheeldeltax < 0 ) { newx++; } if ( wheeldeltay > 0 ) { newy--; } else if ( wheeldeltay < 0 ) { newy++; } this.gotopage(newx, newy); return; } newx = this.x + math.round(this.hashorizontalscroll ? wheeldeltax : 0); newy = this.y + math.round(this.hasverticalscroll ? wheeldeltay : 0); this.directionx = wheeldeltax > 0 ? -1 : wheeldeltax < 0 ? 1 : 0; this.directiony = wheeldeltay > 0 ? -1 : wheeldeltay < 0 ? 1 : 0; if ( newx > 0 ) { newx = 0; } else if ( newx < this.maxscrollx ) { newx = this.maxscrollx; } if ( newy > 0 ) { newy = 0; } else if ( newy < this.maxscrolly ) { newy = this.maxscrolly; } this.scrollto(newx, newy, 0); // insert point: _wheel }, _initsnap: function () { this.currentpage = {}; if ( typeof this.options.snap == 'string' ) { this.options.snap = this.scroller.queryselectorall(this.options.snap); } this.on('refresh', function () { var i = 0, l, m = 0, n, cx, cy, x = 0, y, stepx = this.options.snapstepx || this.wrapperwidth, stepy = this.options.snapstepy || this.wrapperheight, el; this.pages = []; if ( !this.wrapperwidth || !this.wrapperheight || !this.scrollerwidth || !this.scrollerheight ) { return; } if ( this.options.snap === true ) { cx = math.round( stepx / 2 ); cy = math.round( stepy / 2 ); while ( x > -this.scrollerwidth ) { this.pages[i] = []; l = 0; y = 0; while ( y > -this.scrollerheight ) { this.pages[i][l] = { x: math.max(x, this.maxscrollx), y: math.max(y, this.maxscrolly), width: stepx, height: stepy, cx: x - cx, cy: y - cy }; y -= stepy; l++; } x -= stepx; i++; } } else { el = this.options.snap; l = el.length; n = -1; for ( ; i < l; i++ ) { if ( i === 0 || el[i].offsetleft <= el[i-1].offsetleft ) { m = 0; n++; } if ( !this.pages[m] ) { this.pages[m] = []; } x = math.max(-el[i].offsetleft, this.maxscrollx); y = math.max(-el[i].offsettop, this.maxscrolly); cx = x - math.round(el[i].offsetwidth / 2); cy = y - math.round(el[i].offsetheight / 2); this.pages[m][n] = { x: x, y: y, width: el[i].offsetwidth, height: el[i].offsetheight, cx: cx, cy: cy }; if ( x > this.maxscrollx ) { m++; } } } this.gotopage(this.currentpage.pagex || 0, this.currentpage.pagey || 0, 0); // update snap threshold if needed if ( this.options.snapthreshold % 1 === 0 ) { this.snapthresholdx = this.options.snapthreshold; this.snapthresholdy = this.options.snapthreshold; } else { this.snapthresholdx = math.round(this.pages[this.currentpage.pagex][this.currentpage.pagey].width * this.options.snapthreshold); this.snapthresholdy = math.round(this.pages[this.currentpage.pagex][this.currentpage.pagey].height * this.options.snapthreshold); } }); this.on('flick', function () { var time = this.options.snapspeed || math.max( math.max( math.min(math.abs(this.x - this.startx), 1000), math.min(math.abs(this.y - this.starty), 1000) ), 300); this.gotopage( this.currentpage.pagex + this.directionx, this.currentpage.pagey + this.directiony, time ); }); }, _nearestsnap: function (x, y) { if ( !this.pages.length ) { return { x: 0, y: 0, pagex: 0, pagey: 0 }; } var i = 0, l = this.pages.length, m = 0; // check if we exceeded the snap threshold if ( math.abs(x - this.absstartx) < this.snapthresholdx && math.abs(y - this.absstarty) < this.snapthresholdy ) { return this.currentpage; } if ( x > 0 ) { x = 0; } else if ( x < this.maxscrollx ) { x = this.maxscrollx; } if ( y > 0 ) { y = 0; } else if ( y < this.maxscrolly ) { y = this.maxscrolly; } for ( ; i < l; i++ ) { if ( x >= this.pages[i][0].cx ) { x = this.pages[i][0].x; break; } } l = this.pages[i].length; for ( ; m < l; m++ ) { if ( y >= this.pages[0][m].cy ) { y = this.pages[0][m].y; break; } } if ( i == this.currentpage.pagex ) { i += this.directionx; if ( i < 0 ) { i = 0; } else if ( i >= this.pages.length ) { i = this.pages.length - 1; } x = this.pages[i][0].x; } if ( m == this.currentpage.pagey ) { m += this.directiony; if ( m < 0 ) { m = 0; } else if ( m >= this.pages[0].length ) { m = this.pages[0].length - 1; } y = this.pages[0][m].y; } return { x: x, y: y, pagex: i, pagey: m }; }, gotopage: function (x, y, time, easing) { easing = easing || this.options.bounceeasing; if ( x >= this.pages.length ) { x = this.pages.length - 1; } else if ( x < 0 ) { x = 0; } if ( y >= this.pages[x].length ) { y = this.pages[x].length - 1; } else if ( y < 0 ) { y = 0; } var posx = this.pages[x][y].x, posy = this.pages[x][y].y; time = time === undefined ? this.options.snapspeed || math.max( math.max( math.min(math.abs(posx - this.x), 1000), math.min(math.abs(posy - this.y), 1000) ), 300) : time; this.currentpage = { x: posx, y: posy, pagex: x, pagey: y }; this.scrollto(posx, posy, time, easing); }, next: function (time, easing) { var x = this.currentpage.pagex, y = this.currentpage.pagey; x++; if ( x >= this.pages.length && this.hasverticalscroll ) { x = 0; y++; } this.gotopage(x, y, time, easing); }, prev: function (time, easing) { var x = this.currentpage.pagex, y = this.currentpage.pagey; x--; if ( x < 0 && this.hasverticalscroll ) { x = 0; y--; } this.gotopage(x, y, time, easing); }, _initkeys: function (e) { // default key bindings var keys = { pageup: 33, pagedown: 34, end: 35, home: 36, left: 37, up: 38, right: 39, down: 40 }; var i; // if you give me characters i give you keycode if ( typeof this.options.keybindings == 'object' ) { for ( i in this.options.keybindings ) { if ( typeof this.options.keybindings[i] == 'string' ) { this.options.keybindings[i] = this.options.keybindings[i].touppercase().charcodeat(0); } } } else { this.options.keybindings = {}; } for ( i in keys ) { this.options.keybindings[i] = this.options.keybindings[i] || keys[i]; } utils.addevent(window, 'keydown', this); this.on('destroy', function () { utils.removeevent(window, 'keydown', this); }); }, _key: function (e) { if ( !this.enabled ) { return; } var snap = this.options.snap, // we are using this alot, better to cache it newx = snap ? this.currentpage.pagex : this.x, newy = snap ? this.currentpage.pagey : this.y, now = utils.gettime(), prevtime = this.keytime || 0, acceleration = 0.250, pos; if ( this.options.usetransition && this.isintransition ) { pos = this.getcomputedposition(); this._translate(math.round(pos.x), math.round(pos.y)); this.isintransition = false; } this.keyacceleration = now - prevtime < 200 ? math.min(this.keyacceleration + acceleration, 50) : 0; switch ( e.keycode ) { case this.options.keybindings.pageup: if ( this.hashorizontalscroll && !this.hasverticalscroll ) { newx += snap ? 1 : this.wrapperwidth; } else { newy += snap ? 1 : this.wrapperheight; } break; case this.options.keybindings.pagedown: if ( this.hashorizontalscroll && !this.hasverticalscroll ) { newx -= snap ? 1 : this.wrapperwidth; } else { newy -= snap ? 1 : this.wrapperheight; } break; case this.options.keybindings.end: newx = snap ? this.pages.length-1 : this.maxscrollx; newy = snap ? this.pages[0].length-1 : this.maxscrolly; break; case this.options.keybindings.home: newx = 0; newy = 0; break; case this.options.keybindings.left: newx += snap ? -1 : 5 + this.keyacceleration>>0; break; case this.options.keybindings.up: newy += snap ? 1 : 5 + this.keyacceleration>>0; break; case this.options.keybindings.right: newx -= snap ? -1 : 5 + this.keyacceleration>>0; break; case this.options.keybindings.down: newy -= snap ? 1 : 5 + this.keyacceleration>>0; break; default: return; } if ( snap ) { this.gotopage(newx, newy); return; } if ( newx > 0 ) { newx = 0; this.keyacceleration = 0; } else if ( newx < this.maxscrollx ) { newx = this.maxscrollx; this.keyacceleration = 0; } if ( newy > 0 ) { newy = 0; this.keyacceleration = 0; } else if ( newy < this.maxscrolly ) { newy = this.maxscrolly; this.keyacceleration = 0; } this.scrollto(newx, newy, 0); this.keytime = now; }, _animate: function (destx, desty, duration, easingfn) { var that = this, startx = this.x, starty = this.y, starttime = utils.gettime(), desttime = starttime + duration; function step () { var now = utils.gettime(), newx, newy, easing; if ( now >= desttime ) { that.isanimating = false; that._translate(destx, desty); if ( !that.resetposition(that.options.bouncetime) ) { that._execevent('scrollend'); } return; } now = ( now - starttime ) / duration; easing = easingfn(now); newx = ( destx - startx ) * easing + startx; newy = ( desty - starty ) * easing + starty; that._translate(newx, newy); if ( that.isanimating ) { raf(step); } } this.isanimating = true; step(); }, handleevent: function (e) { switch ( e.type ) { case 'touchstart': case 'pointerdown': case 'mspointerdown': case 'mousedown': this._start(e); break; case 'touchmove': case 'pointermove': case 'mspointermove': case 'mousemove': this._move(e); break; case 'touchend': case 'pointerup': case 'mspointerup': case 'mouseup': case 'touchcancel': case 'pointercancel': case 'mspointercancel': case 'mousecancel': this._end(e); break; case 'orientationchange': case 'resize': this._resize(); break; case 'transitionend': case 'webkittransitionend': case 'otransitionend': case 'mstransitionend': this._transitionend(e); break; case 'wheel': case 'dommousescroll': case 'mousewheel': this._wheel(e); break; case 'keydown': this._key(e); break; case 'click': if ( this.enabled && !e._constructed ) { e.preventdefault(); e.stoppropagation(); } break; } } }; function createdefaultscrollbar (direction, interactive, type) { var scrollbar = document.createelement('div'), indicator = document.createelement('div'); if ( type === true ) { scrollbar.style.csstext = 'position:absolute;z-index:9999'; 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'; } indicator.classname = 'iscrollindicator'; if ( direction == 'h' ) { if ( type === true ) { scrollbar.style.csstext += ';height:7px;left:2px;right:2px;bottom:0'; indicator.style.height = '100%'; } scrollbar.classname = 'iscrollhorizontalscrollbar'; } else { if ( type === true ) { scrollbar.style.csstext += ';width:7px;bottom:2px;top:2px;right:1px'; indicator.style.width = '100%'; } scrollbar.classname = 'iscrollverticalscrollbar'; } scrollbar.style.csstext += ';overflow:hidden'; if ( !interactive ) { scrollbar.style.pointerevents = 'none'; } scrollbar.appendchild(indicator); return scrollbar; } function indicator (scroller, options) { this.wrapper = typeof options.el == 'string' ? document.queryselector(options.el) : options.el; this.wrapperstyle = this.wrapper.style; this.indicator = this.wrapper.children[0]; this.indicatorstyle = this.indicator.style; this.scroller = scroller; this.options = { listenx: true, listeny: true, interactive: false, resize: true, defaultscrollbars: false, shrink: false, fade: false, speedratiox: 0, speedratioy: 0 }; for ( var i in options ) { this.options[i] = options[i]; } this.sizeratiox = 1; this.sizeratioy = 1; this.maxposx = 0; this.maxposy = 0; if ( this.options.interactive ) { if ( !this.options.disabletouch ) { utils.addevent(this.indicator, 'touchstart', this); utils.addevent(window, 'touchend', this); } if ( !this.options.disablepointer ) { utils.addevent(this.indicator, utils.prefixpointerevent('pointerdown'), this); utils.addevent(window, utils.prefixpointerevent('pointerup'), this); } if ( !this.options.disablemouse ) { utils.addevent(this.indicator, 'mousedown', this); utils.addevent(window, 'mouseup', this); } } if ( this.options.fade ) { this.wrapperstyle[utils.style.transform] = this.scroller.translatez; var durationprop = utils.style.transitionduration; if(!durationprop) { return; } this.wrapperstyle[durationprop] = utils.isbadandroid ? '0.0001ms' : '0ms'; // remove 0.0001ms var self = this; if(utils.isbadandroid) { raf(function() { if(self.wrapperstyle[durationprop] === '0.0001ms') { self.wrapperstyle[durationprop] = '0s'; } }); } this.wrapperstyle.opacity = '0'; } } indicator.prototype = { handleevent: function (e) { switch ( e.type ) { case 'touchstart': case 'pointerdown': case 'mspointerdown': case 'mousedown': this._start(e); break; case 'touchmove': case 'pointermove': case 'mspointermove': case 'mousemove': this._move(e); break; case 'touchend': case 'pointerup': case 'mspointerup': case 'mouseup': case 'touchcancel': case 'pointercancel': case 'mspointercancel': case 'mousecancel': this._end(e); break; } }, destroy: function () { if ( this.options.fadescrollbars ) { cleartimeout(this.fadetimeout); this.fadetimeout = null; } if ( this.options.interactive ) { utils.removeevent(this.indicator, 'touchstart', this); utils.removeevent(this.indicator, utils.prefixpointerevent('pointerdown'), this); utils.removeevent(this.indicator, 'mousedown', this); utils.removeevent(window, 'touchmove', this); utils.removeevent(window, utils.prefixpointerevent('pointermove'), this); utils.removeevent(window, 'mousemove', this); utils.removeevent(window, 'touchend', this); utils.removeevent(window, utils.prefixpointerevent('pointerup'), this); utils.removeevent(window, 'mouseup', this); } if ( this.options.defaultscrollbars ) { this.wrapper.parentnode.removechild(this.wrapper); } }, _start: function (e) { var point = e.touches ? e.touches[0] : e; e.preventdefault(); e.stoppropagation(); this.transitiontime(); this.initiated = true; this.moved = false; this.lastpointx = point.pagex; this.lastpointy = point.pagey; this.starttime = utils.gettime(); if ( !this.options.disabletouch ) { utils.addevent(window, 'touchmove', this); } if ( !this.options.disablepointer ) { utils.addevent(window, utils.prefixpointerevent('pointermove'), this); } if ( !this.options.disablemouse ) { utils.addevent(window, 'mousemove', this); } this.scroller._execevent('beforescrollstart'); }, _move: function (e) { var point = e.touches ? e.touches[0] : e, deltax, deltay, newx, newy, timestamp = utils.gettime(); if ( !this.moved ) { this.scroller._execevent('scrollstart'); } this.moved = true; deltax = point.pagex - this.lastpointx; this.lastpointx = point.pagex; deltay = point.pagey - this.lastpointy; this.lastpointy = point.pagey; newx = this.x + deltax; newy = this.y + deltay; this._pos(newx, newy); // insert point: indicator._move e.preventdefault(); e.stoppropagation(); }, _end: function (e) { if ( !this.initiated ) { return; } this.initiated = false; e.preventdefault(); e.stoppropagation(); utils.removeevent(window, 'touchmove', this); utils.removeevent(window, utils.prefixpointerevent('pointermove'), this); utils.removeevent(window, 'mousemove', this); if ( this.scroller.options.snap ) { var snap = this.scroller._nearestsnap(this.scroller.x, this.scroller.y); var time = this.options.snapspeed || math.max( math.max( math.min(math.abs(this.scroller.x - snap.x), 1000), math.min(math.abs(this.scroller.y - snap.y), 1000) ), 300); if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) { this.scroller.directionx = 0; this.scroller.directiony = 0; this.scroller.currentpage = snap; this.scroller.scrollto(snap.x, snap.y, time, this.scroller.options.bounceeasing); } } if ( this.moved ) { this.scroller._execevent('scrollend'); } }, transitiontime: function (time) { time = time || 0; var durationprop = utils.style.transitionduration; if(!durationprop) { return; } this.indicatorstyle[durationprop] = time + 'ms'; if ( !time && utils.isbadandroid ) { this.indicatorstyle[durationprop] = '0.0001ms'; // remove 0.0001ms var self = this; raf(function() { if(self.indicatorstyle[durationprop] === '0.0001ms') { self.indicatorstyle[durationprop] = '0s'; } }); } }, transitiontimingfunction: function (easing) { this.indicatorstyle[utils.style.transitiontimingfunction] = easing; }, refresh: function () { this.transitiontime(); if ( this.options.listenx && !this.options.listeny ) { this.indicatorstyle.display = this.scroller.hashorizontalscroll ? 'block' : 'none'; } else if ( this.options.listeny && !this.options.listenx ) { this.indicatorstyle.display = this.scroller.hasverticalscroll ? 'block' : 'none'; } else { this.indicatorstyle.display = this.scroller.hashorizontalscroll || this.scroller.hasverticalscroll ? 'block' : 'none'; } if ( this.scroller.hashorizontalscroll && this.scroller.hasverticalscroll ) { utils.addclass(this.wrapper, 'iscrollbothscrollbars'); utils.removeclass(this.wrapper, 'iscrolllonescrollbar'); if ( this.options.defaultscrollbars && this.options.customstyle ) { if ( this.options.listenx ) { this.wrapper.style.right = '8px'; } else { this.wrapper.style.bottom = '8px'; } } } else { utils.removeclass(this.wrapper, 'iscrollbothscrollbars'); utils.addclass(this.wrapper, 'iscrolllonescrollbar'); if ( this.options.defaultscrollbars && this.options.customstyle ) { if ( this.options.listenx ) { this.wrapper.style.right = '2px'; } else { this.wrapper.style.bottom = '2px'; } } } var r = this.wrapper.offsetheight; // force refresh if ( this.options.listenx ) { this.wrapperwidth = this.wrapper.clientwidth; if ( this.options.resize ) { this.indicatorwidth = math.max(math.round(this.wrapperwidth * this.wrapperwidth / (this.scroller.scrollerwidth || this.wrapperwidth || 1)), 8); this.indicatorstyle.width = this.indicatorwidth + 'px'; } else { this.indicatorwidth = this.indicator.clientwidth; } this.maxposx = this.wrapperwidth - this.indicatorwidth; if ( this.options.shrink == 'clip' ) { this.minboundaryx = -this.indicatorwidth + 8; this.maxboundaryx = this.wrapperwidth - 8; } else { this.minboundaryx = 0; this.maxboundaryx = this.maxposx; } this.sizeratiox = this.options.speedratiox || (this.scroller.maxscrollx && (this.maxposx / this.scroller.maxscrollx)); } if ( this.options.listeny ) { this.wrapperheight = this.wrapper.clientheight; if ( this.options.resize ) { this.indicatorheight = math.max(math.round(this.wrapperheight * this.wrapperheight / (this.scroller.scrollerheight || this.wrapperheight || 1)), 8); this.indicatorstyle.height = this.indicatorheight + 'px'; } else { this.indicatorheight = this.indicator.clientheight; } this.maxposy = this.wrapperheight - this.indicatorheight; if ( this.options.shrink == 'clip' ) { this.minboundaryy = -this.indicatorheight + 8; this.maxboundaryy = this.wrapperheight - 8; } else { this.minboundaryy = 0; this.maxboundaryy = this.maxposy; } this.maxposy = this.wrapperheight - this.indicatorheight; this.sizeratioy = this.options.speedratioy || (this.scroller.maxscrolly && (this.maxposy / this.scroller.maxscrolly)); } this.updateposition(); }, updateposition: function () { var x = this.options.listenx && math.round(this.sizeratiox * this.scroller.x) || 0, y = this.options.listeny && math.round(this.sizeratioy * this.scroller.y) || 0; if ( !this.options.ignoreboundaries ) { if ( x < this.minboundaryx ) { if ( this.options.shrink == 'scale' ) { this.width = math.max(this.indicatorwidth + x, 8); this.indicatorstyle.width = this.width + 'px'; } x = this.minboundaryx; } else if ( x > this.maxboundaryx ) { if ( this.options.shrink == 'scale' ) { this.width = math.max(this.indicatorwidth - (x - this.maxposx), 8); this.indicatorstyle.width = this.width + 'px'; x = this.maxposx + this.indicatorwidth - this.width; } else { x = this.maxboundaryx; } } else if ( this.options.shrink == 'scale' && this.width != this.indicatorwidth ) { this.width = this.indicatorwidth; this.indicatorstyle.width = this.width + 'px'; } if ( y < this.minboundaryy ) { if ( this.options.shrink == 'scale' ) { this.height = math.max(this.indicatorheight + y * 3, 8); this.indicatorstyle.height = this.height + 'px'; } y = this.minboundaryy; } else if ( y > this.maxboundaryy ) { if ( this.options.shrink == 'scale' ) { this.height = math.max(this.indicatorheight - (y - this.maxposy) * 3, 8); this.indicatorstyle.height = this.height + 'px'; y = this.maxposy + this.indicatorheight - this.height; } else { y = this.maxboundaryy; } } else if ( this.options.shrink == 'scale' && this.height != this.indicatorheight ) { this.height = this.indicatorheight; this.indicatorstyle.height = this.height + 'px'; } } this.x = x; this.y = y; if ( this.scroller.options.usetransform ) { this.indicatorstyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translatez; } else { this.indicatorstyle.left = x + 'px'; this.indicatorstyle.top = y + 'px'; } }, _pos: function (x, y) { if ( x < 0 ) { x = 0; } else if ( x > this.maxposx ) { x = this.maxposx; } if ( y < 0 ) { y = 0; } else if ( y > this.maxposy ) { y = this.maxposy; } x = this.options.listenx ? math.round(x / this.sizeratiox) : this.scroller.x; y = this.options.listeny ? math.round(y / this.sizeratioy) : this.scroller.y; this.scroller.scrollto(x, y); }, fade: function (val, hold) { if ( hold && !this.visible ) { return; } cleartimeout(this.fadetimeout); this.fadetimeout = null; var time = val ? 250 : 500, delay = val ? 0 : 300; val = val ? '1' : '0'; this.wrapperstyle[utils.style.transitionduration] = time + 'ms'; this.fadetimeout = settimeout((function (val) { this.wrapperstyle.opacity = val; this.visible = +val; }).bind(this, val), delay); } }; iscroll.utils = utils; if ( typeof module != 'undefined' && module.exports ) { module.exports = iscroll; } else if ( typeof define == 'function' && define.amd ) { define( function () { return iscroll; } ); //making sure scrolloverflow works when using require.js //in the browser if(typeof window !== 'undefined'){ window.iscroll = iscroll; } } else { window.iscroll = iscroll; } })(window, document, math); /*! * scrolloverflow 2.0.4 module for fullpage.js >= 3 * https://github.com/alvarotrigo/fullpage.js * @license mit licensed * * copyright (c) 2015 alvarotrigo.com - a project by alvaro trigo */ (function (window, document) { window.fp_scrolloverflow = (function() { // check if iscroll is available in global scope if (!window.iscroll) { // otherwise create local one from module.exports window.iscroll = module.exports; } // keeping central set of classnames and selectors var scrollable = 'fp-scrollable'; var scrollable_sel = '.' + scrollable; var active = 'active'; var active_sel = '.' + active; var section = 'fp-section'; var section_sel = '.' + section; var section_active_sel = section_sel + active_sel; var slide = 'fp-slide'; var slide_sel = '.' + slide; var slide_active_sel = slide_sel + active_sel; var slides_wrapper = 'fp-slides'; var slides_wrapper_sel = '.' + slides_wrapper; var table_cell = 'fp-tablecell'; var table_cell_sel = '.' + table_cell; var responsive = 'fp-responsive'; var auto_height_responsive= 'fp-auto-height-responsive'; /* * turns iscroll `mousewheel` option off dynamically * https://github.com/cubiq/iscroll/issues/1036 */ iscroll.prototype.wheelon = function () { this.wrapper.addeventlistener('wheel', this); this.wrapper.addeventlistener('mousewheel', this); this.wrapper.addeventlistener('dommousescroll', this); }; /* * turns iscroll `mousewheel` option on dynamically * https://github.com/cubiq/iscroll/issues/1036 */ iscroll.prototype.wheeloff = function () { this.wrapper.removeeventlistener('wheel', this); this.wrapper.removeeventlistener('mousewheel', this); this.wrapper.removeeventlistener('dommousescroll', this); }; /** * returns an integer representing the padding dimensions in px. */ function getpaddings(element){ var section = fp_utils.closest(element, section_sel); if(section != null){ return parseint(getcomputedstyle(section)['padding-bottom']) + parseint(getcomputedstyle(section)['padding-top']); } return 0; } function scrollbarhandler(){ var self = this; self.options = null; self.init = function(options, iscrolloptions){ self.options = options; self.iscrolloptions = iscrolloptions; if(document.readystate === 'complete'){ createscrollbarforall(); fullpage_api.shared.afterrenderactions(); } //after dom and images are loaded window.addeventlistener('load', function(){ createscrollbarforall(); fullpage_api.shared.afterrenderactions(); }); return self; }; /** * creates the scrollbar for the sections and slides in the site */ function createscrollbarforall(){ if(fp_utils.hasclass(document.body, responsive)){ removeresponsivescrolloverflows(); } else{ foreachsectionandslide(createscrollbar); } } /** * checks if the element needs scrollbar and if the user wants to apply it. * if so it creates it. * * @param {object} element jquery object of the section or slide */ function createscrollbar(element){ //user doesn't want scrollbar here? sayonara baby! if(fp_utils.hasclass(element, 'fp-noscroll')) return; //necessary to make `scrollheight` work under opera 12 fp_utils.css(element, {'overflow': 'hidden'}); var scrolloverflowhandler = self.options.scrolloverflowhandler; var wrap = scrolloverflowhandler.wrapcontent(); var section = fp_utils.closest(element, section_sel); //in case element is a slide var scrollable = scrolloverflowhandler.scrollable(element); var contentheight; var paddings = getpaddings(section); //if there was scroll, the contentheight will be the one in the scrollable section if(scrollable != null){ contentheight = scrolloverflowhandler.scrollheight(element); } else{ contentheight = element.scrollheight; if(self.options.verticalcentered){ contentheight = $(table_cell_sel, element)[0].scrollheight; } } var scrollheight = fp_utils.getwindowheight(); var contentheightwidthpaddings = contentheight + paddings; var scrollheightwidthoutpaddings = scrollheight - paddings; //needs scroll? if ( contentheightwidthpaddings > scrollheight) { //did we already have an scrollbar ? updating it if(scrollable != null){ scrolloverflowhandler.update(element, scrollheightwidthoutpaddings); } //creating the scrolling else{ if(self.options.verticalcentered){ fp_utils.wrapinner($(table_cell_sel, element)[0], wrap.scroller); fp_utils.wrapinner($(table_cell_sel, element)[0], wrap.scrollable); }else{ fp_utils.wrapinner(element, wrap.scroller); fp_utils.wrapinner(element, wrap.scrollable); } scrolloverflowhandler.create(element, scrollheightwidthoutpaddings, self.iscrolloptions); } } //removing the scrolling when it is not necessary anymore else{ scrolloverflowhandler.remove(element); } //undo fp_utils.css(element, {'overflow': ''}); } /** * applies a callback function to each section in the site * or the slides within them */ function foreachsectionandslide(callback){ $(section_sel).foreach(function(section){ var slides = $(slide_sel, section); if(slides.length){ slides.foreach(function(slide){ callback(slide); }); }else{ callback(section); } }); } /** * removes scrolloverflow for sections using the class `fp-auto-height-responsive` */ function removeresponsivescrolloverflows(){ var scrolloverflowhandler = self.options.scrolloverflowhandler; foreachsectionandslide(function(element){ if(fp_utils.hasclass( fp_utils.closest(element, section_sel), auto_height_responsive)){ scrolloverflowhandler.remove(element); } }); } //public functions self.createscrollbarforall = createscrollbarforall; self.createscrollbar = createscrollbar; } /** * an object to handle overflow scrolling. * this uses jquery.slimscroll to accomplish overflow scrolling. * it is possible to pass in an alternate scrolloverflowhandler * to the fullpage.js option that implements the same functions * as this handler. * * @type {object} */ var $ = null; var g_fullpageoptions = null; var iscrollhandler = { refreshid: null, iscrollinstances: [], lastscrolly: null, // default options for iscroll.js used when using scrolloverflow iscrolloptions: { scrollbars: true, mousewheel: true, hidescrollbars: false, fadescrollbars: false, disablemouse: true, interactivescrollbars: true }, init: function(options){ $ = fp_utils.$; g_fullpageoptions = options; var istouch = (('ontouchstart' in window) || (navigator.msmaxtouchpoints > 0) || (navigator.maxtouchpoints)); //fixing bug in iscroll with links: https://github.com/cubiq/iscroll/issues/783 iscrollhandler.iscrolloptions.click = istouch; // see #2035 //extending iscroll options with the user custom ones iscrollhandler.iscrolloptions = fp_utils.deepextend(iscrollhandler.iscrolloptions, options.scrolloverflowoptions); return new scrollbarhandler().init(options, iscrollhandler.iscrolloptions); }, // enables or disables the mouse wheel for the active section or all slides in it togglewheel: function(value){ var scrollable = $(scrollable_sel, $(section_active_sel)[0]); scrollable.foreach(function(item){ var iscrollinstance = item.fp_iscrollinstance; if(iscrollinstance != null){ if(value){ iscrollinstance.wheelon(); } else{ iscrollinstance.wheeloff(); } } }); }, /** * turns off iscroll for the destination section. * when scrolling very fast on some trackpads (and apple laptops) the inertial scrolling would * scroll the destination section/slide before the sections animations ends. */ onleave: function(){ iscrollhandler.togglewheel(false); }, // turns off iscroll for the leaving section beforeleave: function(){ iscrollhandler.onleave() }, // turns on iscroll on section load afterload: function(){ iscrollhandler.togglewheel(true); }, /** * called when overflow scrolling is needed for a section. * * @param {object} element jquery object containing current section * @param {number} scrollheight current window height in pixels */ create: function(element, scrollheight, iscrolloptions) { var scrollable = $(scrollable_sel, element); scrollable.foreach(function(item) { fp_utils.css(item, {'height': scrollheight + 'px'}); var iscrollinstance = item.fp_iscrollinstance; if (iscrollinstance != null) { iscrollhandler.iscrollinstances.foreach(function(instance){ instance.destroy(); }); } console.log(iscrolloptions); iscrollinstance = new iscroll(item, { scrollbars: true, mousewheel: false, hidescrollbars: true, fadescrollbars: false, disablemouse: true, interactivescrollbars: false }); iscrollhandler.iscrollinstances.push(iscrollinstance); if(!fp_utils.hasclass(fp_utils.closest(element, section_sel), active)){ //off by default until the section gets active iscrollinstance.wheeloff(); } item.fp_iscrollinstance = iscrollinstance; }); }, /** * return a boolean depending on whether the scrollable element is a * the end or at the start of the scrolling depending on the given type. * * @param {string} type either 'top' or 'bottom' * @param {object} scrollable jquery object for the scrollable element * @return {boolean} */ isscrolled: function(type, scrollable) { var scroller = scrollable.fp_iscrollinstance; //no scroller? if (!scroller) { return true; } // two times reporting the same y position ? // that means we are on the top or on the bottom of the scroller if (type === 'top'){ return scroller.y >= 0 && !fp_utils.getscrolltop(scrollable); } else if (type === 'bottom') { return (0 - scroller.y) + fp_utils.getscrolltop(scrollable) + scrollable.offsetheight >= scrollable.scrollheight; } }, /** * returns the scrollable element for the given section. * if there are landscape slides, will only return a scrollable element * if it is in the active slide. * * @param {object} activesection jquery object containing current section * @return {boolean} */ scrollable: function(activesection){ // if there are landscape slides, we check if the scrolling bar is in the current one or not if ($(slides_wrapper_sel, activesection).length) { return $(scrollable_sel, $(slide_active_sel, activesection)[0] )[0]; } return $(scrollable_sel, activesection)[0]; }, /** * returns the scroll height of the wrapped content. * if this is larger than the window height minus section padding, * overflow scrolling is needed. * * @param {object} element jquery object containing current section * @return {number} */ scrollheight: function(element) { return $('.fp-scroller', $(scrollable_sel, element)[0] )[0].scrollheight; }, /** * called when overflow scrolling is no longer needed for a section. * * @param {object} element jquery object containing current section */ remove: function(element) { if(element == null) return; var scrollable = $(scrollable_sel, element)[0]; if (scrollable != null) { var iscrollinstance = scrollable.fp_iscrollinstance; if(iscrollinstance != null){ iscrollinstance.destroy(); } scrollable.fp_iscrollinstance = null; //unwrapping... fp_utils.unwrap($('.fp-scroller', element)[0]); fp_utils.unwrap($(scrollable_sel, element)[0]); } }, /** * called when overflow scrolling has already been setup but the * window height has potentially changed. * * @param {object} element jquery object containing current section * @param {number} scrollheight current window height in pixels */ update: function(element, scrollheight) { //using a timeout in order to execute the refresh function only once when `update` is called multiple times in a //short period of time. //it also comes on handy because iscroll requires the use of timeout when using `refresh`. cleartimeout(iscrollhandler.refreshid); iscrollhandler.refreshid = settimeout(function(){ iscrollhandler.iscrollinstances.foreach(function(instance){ instance.refresh(); //ugly hack that we are forced to use due to the timeout delay //otherwise done on the fullpage.js rebuild function fullpage_api.silentmoveto(fp_utils.index($(section_active_sel)[0]) + 1); }); }, 150); //updating the wrappers height fp_utils.css($(scrollable_sel, element)[0], {'height': scrollheight + 'px'}); if(g_fullpageoptions.verticalcentered){ fp_utils.css($(scrollable_sel, element)[0].parentnode, {'height': scrollheight + 'px'}); } }, /** * called to get any additional elements needed to wrap the section * content in order to facilitate overflow scrolling. * * @return {string|object} can be a string containing html, * a dom element, or jquery object. */ wrapcontent: function() { var scrollable = document.createelement('div'); scrollable.classname = scrollable; var scroller = document.createelement('div'); scroller.classname = 'fp-scroller'; return { scrollable: scrollable, scroller: scroller }; } }; return { iscrollhandler: iscrollhandler }; })(); })(window, document);