From 10e7b75b32bf5a697c622fe4e6756e7f99b6173c Mon Sep 17 00:00:00 2001 From: Max Lynch Date: Fri, 25 Oct 2013 18:16:41 -0500 Subject: [PATCH] Deleted scroller view --- dist/js/ionic.js | 2284 ++++++++------------------------------ js/views/scrollerView.js | 1799 ------------------------------ 2 files changed, 484 insertions(+), 3599 deletions(-) delete mode 100644 js/views/scrollerView.js diff --git a/dist/js/ionic.js b/dist/js/ionic.js index d0f0f01b21..ca4d16265e 100644 --- a/dist/js/ionic.js +++ b/dist/js/ionic.js @@ -2293,6 +2293,490 @@ window.ionic = { }, + _handleEndDrag: function(e) { + var _this = this; + + if(!this._dragOp) { + this._initDrag(); + return; + } + + this._dragOp.end(e, function() { + _this._initDrag(); + }); + }, + + /** + * Process the drag event to move the item to the left or right. + */ + _handleDrag: function(e) { + var _this = this, content, buttons; + + if(!this._dragOp) { + this._startDrag(e); + if(!this._dragOp) { return; } + } + + this._dragOp.drag(e); + } + }; + +})(ionic); +; +(function(ionic) { +'use strict'; + + + var DragOp = function() {}; + DragOp.prototype = { + start: function(e) { + }, + drag: function(e) { + }, + end: function(e) { + } + }; + + + /** + * The Pull To Refresh drag operation handles the well-known + * "pull to refresh" concept seen on various apps. This lets + * the user indicate they want to refresh a given list by dragging + * down. + * + * @param {object} opts the options for the pull to refresh drag. + */ + var PullToRefreshDrag = function(opts) { + this.dragThresholdY = opts.dragThresholdY || 10; + this.onRefreshOpening = opts.onRefreshOpening || function() {}; + this.onRefresh = opts.onRefresh || function() {}; + this.onRefreshHolding = opts.onRefreshHolding || function() {}; + this.el = opts.el; + }; + + PullToRefreshDrag.prototype = new DragOp(); + + PullToRefreshDrag.prototype.start = function(e) { + var content, refresher; + + content = ionic.DomUtil.getParentOrSelfWithClass(e.target, 'list'); + if(!content) { return; } + + // Grab the refresher element that will show as you drag down + refresher = content.querySelector('.list-refresher'); + if(!refresher) { + refresher = this._injectRefresher(); + } + + // Disable animations while dragging + refresher.classList.remove('list-refreshing'); + + this._currentDrag = { + refresher: refresher, + content: content, + isHolding: false + }; + }; + + PullToRefreshDrag.prototype._injectRefresher = function() { + var refresher = document.createElement('div'); + refresher.className = 'list-refresher'; + this.el.insertBefore(refresher, this.el.firstChild); + return refresher; + }; + + PullToRefreshDrag.prototype.drag = function(e) { + var _this = this; + + window.requestAnimationFrame(function() { + // We really aren't dragging + if(!_this._currentDrag) { + return; + } + + // Check if we should start dragging. Check if we've dragged past the threshold, + // or we are starting from the open state. + if(!_this._isDragging && Math.abs(e.gesture.deltaY) > _this.dragThresholdY) { + _this._isDragging = true; + } + + if(_this._isDragging) { + var refresher = _this._currentDrag.refresher; + + var currentHeight = parseFloat(refresher.style.height); + refresher.style.height = e.gesture.deltaY + 'px'; + + var newHeight = parseFloat(refresher.style.height); + var firstChildHeight = parseFloat(refresher.firstElementChild.offsetHeight); + + if(newHeight > firstChildHeight && !_this._currentDrag.isHolding) { + // The user is holding the refresh but hasn't let go of it + _this._currentDrag.isHolding = true; + _this.onRefreshHolding && _this.onRefreshHolding(); + } else { + // Indicate what ratio of opening the list refresh drag is + var ratio = Math.min(1, newHeight / firstChildHeight); + _this.onRefreshOpening && _this.onRefreshOpening(ratio); + } + } + }); + }; + + PullToRefreshDrag.prototype.end = function(e, doneCallback) { + var _this = this; + + // There is no drag, just end immediately + if(!this._currentDrag) { + return; + } + + var refresher = this._currentDrag.refresher; + + var currentHeight = parseFloat(refresher.style.height); + refresher.style.height = e.gesture.deltaY + 'px'; + + var firstChildHeight = parseFloat(refresher.firstElementChild.offsetHeight); + + if(currentHeight > firstChildHeight) { + //this.refreshing(); + refresher.classList.add('list-refreshing'); + refresher.style.height = firstChildHeight + 'px'; + this.onRefresh && _this.onRefresh(); + } else { + // Enable animations + refresher.classList.add('list-refreshing'); + refresher.style.height = '0px'; + this.onRefresh && _this.onRefresh(); + } + + this._currentDrag = null; + doneCallback && doneCallback(); + }; + + + var SlideDrag = function(opts) { + this.dragThresholdX = opts.dragThresholdX || 10; + this.el = opts.el; + }; + + SlideDrag.prototype = new DragOp(); + SlideDrag.prototype.start = function(e) { + var content, buttons, offsetX, buttonsWidth; + + if(e.target.classList.contains('list-item-content')) { + content = e.target; + } else if(e.target.classList.contains('list-item')) { + content = e.target.querySelector('.list-item-content'); + } + + // If we don't have a content area as one of our children (or ourselves), skip + if(!content) { + return; + } + + // Make sure we aren't animating as we slide + content.classList.remove('list-item-sliding'); + + // Grab the starting X point for the item (for example, so we can tell whether it is open or closed to start) + offsetX = parseFloat(content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; + + // Grab the buttons + buttons = content.parentNode.querySelector('.list-item-buttons'); + if(!buttons) { + return; + } + + buttonsWidth = buttons.offsetWidth; + + this._currentDrag = { + buttonsWidth: buttonsWidth, + content: content, + startOffsetX: offsetX + }; + }; + + SlideDrag.prototype.drag = function(e) { + var _this = this, buttonsWidth; + + window.requestAnimationFrame(function() { + // We really aren't dragging + if(!_this._currentDrag) { + return; + } + + // Check if we should start dragging. Check if we've dragged past the threshold, + // or we are starting from the open state. + if(!_this._isDragging && + ((Math.abs(e.gesture.deltaX) > _this.dragThresholdX) || + (Math.abs(_this._currentDrag.startOffsetX) > 0))) + { + _this._isDragging = true; + } + + if(_this._isDragging) { + buttonsWidth = _this._currentDrag.buttonsWidth; + + // Grab the new X point, capping it at zero + var newX = Math.min(0, _this._currentDrag.startOffsetX + e.gesture.deltaX); + + // If the new X position is past the buttons, we need to slow down the drag (rubber band style) + if(newX < -buttonsWidth) { + // Calculate the new X position, capped at the top of the buttons + newX = Math.min(-buttonsWidth, -buttonsWidth + (((e.gesture.deltaX + buttonsWidth) * 0.4))); + } + + _this._currentDrag.content.style.webkitTransform = 'translate3d(' + newX + 'px, 0, 0)'; + } + }); + }; + + SlideDrag.prototype.end = function(e, doneCallback) { + var _this = this; + + // There is no drag, just end immediately + if(!this._currentDrag) { + doneCallback && doneCallback(); + return; + } + + // If we are currently dragging, we want to snap back into place + // The final resting point X will be the width of the exposed buttons + var restingPoint = -this._currentDrag.buttonsWidth; + + // Check if the drag didn't clear the buttons mid-point + // and we aren't moving fast enough to swipe open + if(e.gesture.deltaX > -(this._currentDrag.buttonsWidth/2)) { + + // If we are going left but too slow, or going right, go back to resting + if(e.gesture.direction == "left" && Math.abs(e.gesture.velocityX) < 0.3) { + restingPoint = 0; + } else if(e.gesture.direction == "right") { + restingPoint = 0; + } + + } + + var content = this._currentDrag.content; + + var onRestingAnimationEnd = function(e) { + if(e.propertyName == '-webkit-transform') { + content.classList.remove('list-item-sliding'); + } + e.target.removeEventListener('webkitTransitionEnd', onRestingAnimationEnd); + }; + + window.requestAnimationFrame(function() { + var currentX = parseFloat(_this._currentDrag.content.style.webkitTransform.replace('translate3d(', '').split(',')[0]) || 0; + if(currentX !== restingPoint) { + _this._currentDrag.content.classList.add('list-item-sliding'); + _this._currentDrag.content.addEventListener('webkitTransitionEnd', onRestingAnimationEnd); + } + _this._currentDrag.content.style.webkitTransform = 'translate3d(' + restingPoint + 'px, 0, 0)'; + + // Kill the current drag + this._currentDrag = null; + + + // We are done, notify caller + doneCallback && doneCallback(); + }); + }; + + var ReorderDrag = function(opts) { + this.dragThresholdY = opts.dragThresholdY || 0; + this.el = opts.el; + }; + + ReorderDrag.prototype = new DragOp(); + + ReorderDrag.prototype.start = function(e) { + var content; + + + // Grab the starting Y point for the item + var offsetY = this.el.offsetTop;//parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[1]) || 0; + + var placeholder = this.el.cloneNode(true); + + placeholder.classList.add('list-item-placeholder'); + + this.el.parentNode.insertBefore(placeholder, this.el); + + this.el.classList.add('list-item-reordering'); + + + this._currentDrag = { + startOffsetTop: offsetY, + placeholder: placeholder + }; + }; + + ReorderDrag.prototype.drag = function(e) { + var _this = this; + + window.requestAnimationFrame(function() { + // We really aren't dragging + if(!_this._currentDrag) { + return; + } + + // Check if we should start dragging. Check if we've dragged past the threshold, + // or we are starting from the open state. + if(!_this._isDragging && Math.abs(e.gesture.deltaY) > _this.dragThresholdY) { + _this._isDragging = true; + } + + if(_this._isDragging) { + var newY = _this._currentDrag.startOffsetTop + e.gesture.deltaY; + + _this.el.style.top = newY + 'px'; + + _this._currentDrag.currentY = newY; + + _this._reorderItems(); + } + }); + }; + + // When an item is dragged, we need to reorder any items for sorting purposes + ReorderDrag.prototype._reorderItems = function() { + var placeholder = this._currentDrag.placeholder; + var siblings = Array.prototype.slice.call(this._currentDrag.placeholder.parentNode.children); + + // Remove the floating element from the child search list + siblings.splice(siblings.indexOf(this.el), 1); + + var index = siblings.indexOf(this._currentDrag.placeholder); + var topSibling = siblings[Math.max(0, index - 1)]; + var bottomSibling = siblings[Math.min(siblings.length, index+1)]; + var thisOffsetTop = this._currentDrag.currentY;// + this._currentDrag.startOffsetTop; + + if(topSibling && (thisOffsetTop < topSibling.offsetTop + topSibling.offsetHeight/2)) { + ionic.DomUtil.swapNodes(this._currentDrag.placeholder, topSibling); + return index - 1; + } else if(bottomSibling && thisOffsetTop > (bottomSibling.offsetTop + bottomSibling.offsetHeight/2)) { + ionic.DomUtil.swapNodes(bottomSibling, this._currentDrag.placeholder); + return index + 1; + } + }; + + ReorderDrag.prototype.end = function(e, doneCallback) { + if(!this._currentDrag) { + doneCallback && doneCallback(); + return; + } + + var placeholder = this._currentDrag.placeholder; + + // Reposition the element + this.el.classList.remove('list-item-reordering'); + this.el.style.top = 0; + + var finalPosition = ionic.DomUtil.getChildIndex(placeholder); + placeholder.parentNode.insertBefore(this.el, placeholder); + placeholder.parentNode.removeChild(placeholder); + + this._currentDrag = null; + doneCallback && doneCallback(); + }; + + + + /** + * The ListView handles a list of items. It will process drag animations, edit mode, + * and other operations that are common on mobile lists or table views. + */ + ionic.views.List = function(opts) { + var _this = this; + + this.el = opts.el; + + // The amount of dragging required to start sliding the element over (in pixels) + this.dragThresholdX = opts.dragThresholdX || 10; + + this.onRefresh = opts.onRefresh || function() {}; + this.onRefreshOpening = opts.onRefreshOpening || function() {}; + this.onRefreshHolding = opts.onRefreshHolding || function() {}; + + // Start the drag states + this._initDrag(); + + // Listen for drag and release events + window.ionic.onGesture('drag', function(e) { + _this._handleDrag(e); + }, this.el); + window.ionic.onGesture('release', function(e) { + _this._handleEndDrag(e); + }, this.el); + }; + + ionic.views.List.prototype = { + /** + * Called to tell the list to stop refreshing. This is useful + * if you are refreshing the list and are done with refreshing. + */ + stopRefreshing: function() { + var refresher = this.el.querySelector('.list-refresher'); + refresher.style.height = '0px'; + }, + + _initDrag: function() { + this._isDragging = false; + this._dragOp = null; + }, + + // Return the list item from the given target + _getItem: function(target) { + while(target) { + if(target.classList.contains('list-item')) { + return target; + } + target = target.parentNode; + } + return null; + }, + + + _startDrag: function(e) { + var _this = this; + + this._isDragging = false; + + // Check if this is a reorder drag + if(ionic.DomUtil.getParentOrSelfWithClass(e.target, 'list-item-drag') && (e.gesture.direction == 'up' || e.gesture.direction == 'down')) { + var item = this._getItem(e.target); + + if(item) { + this._dragOp = new ReorderDrag({ el: item }); + this._dragOp.start(e); + } + } + + // Check if this is a "pull down" drag for pull to refresh + else if(e.gesture.direction == 'down') { + this._dragOp = new PullToRefreshDrag({ + el: this.el, + onRefresh: function() { + _this.onRefresh && _this.onRefresh(); + }, + onRefreshHolding: function() { + _this.onRefreshHolding && _this.onRefreshHolding(); + }, + onRefreshOpening: function(ratio) { + _this.onRefreshOpening && _this.onRefreshOpening(ratio); + } + }); + this._dragOp.start(e); + } + + // Or check if this is a swipe to the side drag + else if(e.gesture.direction == 'left' || e.gesture.direction == 'right') { + this._dragOp = new SlideDrag({ el: this.el }); + this._dragOp.start(e); + } + }, + + _handleEndDrag: function(e) { var _this = this; @@ -3028,1806 +3512,6 @@ window.ionic = { })(ionic); ; -/** - * Adapted from the great iScroll 5 for Ionic. iScroll is licensed under MIT just like Ionic. - * iScroll v5.0.5 ~ (c) 2008-2013 Matteo Spinelli ~ http://cubiq.org/license - * - * Think of ionic.views.Scroll like a Javascript version of UIScrollView or any - * scroll container in any UI library. You could just use -webkit-overflow-scrolling: touch, - * but you lose control over scroll behavior that native developers have with things - * like UIScrollView, and you don't get events after the finger stops touching the - * device (after a flick, for example) - * - */ -(function (window, document, Math, ionic) { - 'use strict'; - -var rAF = window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function (callback) { window.setTimeout(callback, 1000 / 60); }; - -/** - * Utilities for calculating momentum, etc. - */ -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.momentum = function (current, start, time, lowerMargin, wrapperSize) { - var distance = current - start, - speed = Math.abs(distance) / time, - destination, - duration, - deceleration = 0.0006; - - 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: navigator.msPointerEnabled, - hasTransition: _prefixStyle('transition') in _elementStyle - }); - - me.isAndroidBrowser = /Android/.test(window.navigator.appVersion) && /Version\/\d/.test(window.navigator.appVersion); - - me.extend(me.style = {}, { - transform: _transform, - transitionTimingFunction: _prefixStyle('transitionTimingFunction'), - transitionDuration: _prefixStyle('transitionDuration'), - 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, - - 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 (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { - ev = document.createEvent('MouseEvents'); - ev.initMouseEvent('click', true, true, e.view, 1, - target.screenX, target.screenY, target.clientX, target.clientY, - e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, - 0, null); - - ev._constructed = true; - target.dispatchEvent(ev); - } - }; - - return me; -})(); - -ionic.views.Scroll = function (options) { - var el = options.el; - - this.wrapper = el;//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 = { - - resizeIndicator: true, - - mouseWheelSpeed: 20, - - snapThreshold: 0.334, - - startX: 0, - startY: 0, - scrollY: true, - directionLockThreshold: 5, - momentum: true, - - bounce: true, - bounceTime: 600, - bounceEasing: '', - - preventDefault: true, - preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ }, - - HWCompositing: true, - useTransition: true, - useTransform: true - }; - - 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'; - } - - this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1; - - // Some defaults - this.x = 0; - this.y = 0; - this.directionX = 0; - this.directionY = 0; - this._events = {}; - - this._init(); - this.refresh(); - - this.scrollTo(this.options.startX, this.options.startY); - this.enable(); -} - -ionic.views.Scroll.prototype = { - version: '5.0.5', - - _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(); - } - - }, - - destroy: function () { - this._initEvents(true); - - this._execEvent('destroy'); - }, - - _transitionEnd: function (e) { - if ( e.target != this.scroller ) { - return; - } - - this._transitionTime(0); - if ( !this.resetPosition(this.options.bounceTime) ) { - this._execEvent('scrollEnd'); - } - }, - - _start: function (e) { - // React to left mouse button only - if ( utils.eventType[e.type] != 1 ) { - if ( e.button !== 0 ) { - return; - } - } - - if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) { - return; - } - - if ( this.options.preventDefault && !utils.isAndroidBrowser && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) { - e.preventDefault(); // This seems to break default Android browser - } - - 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._transitionTime(); - - this.isAnimating = false; - this.startTime = utils.getTime(); - - if ( this.options.useTransition && this.isInTransition ) { - pos = this.getComputedPosition(); - - this._translate(Math.round(pos.x), Math.round(pos.y)); - this.isInTransition = false; - } - - 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('scrollStart'); - }, - - _move: function (e) { - // Make sure scrolling is enabled - if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) { - return; - } - - // Prevent default if necessary - if ( this.options.preventDefault ) { // increases performance on Android? TODO: check! - e.preventDefault(); - } - - // Grab the current touch point - var point = e.touches ? e.touches[0] : e, - // Check how far we've scrolled in the X direction - deltaX = this.hasHorizontalScroll ? point.pageX - this.pointX : 0, - // Check how far we've scrolled in the Y direction - deltaY = this.hasVerticalScroll ? point.pageY - this.pointY : 0, - // Get current timestmap - timestamp = utils.getTime(), - newX, newY, - absDistX, absDistY; - - // Store the current finger points - this.pointX = point.pageX; - this.pointY = point.pageY; - - // Calculate the total distance travelled - 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; - } - - // The new scroll point is the current position plus the delta moved since - // last - newX = this.x + deltaX; - newY = this.y + deltaY; - - // Slow down if outside of the boundaries (rubber banding) - 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; - } - - // Calcualte the directional multiplier - this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; - this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; - - // We moved - this.moved = true; - - // Translate to the new position - this._translate(newX, newY); - - // Check if the difference between right now and when we started is bigger than 300ms. - // We then reset the start positions to reduce the possibility of large errors - if ( timestamp - this.startTime > 300 ) { - this.startTime = timestamp; - this.startX = this.x; - this.startY = this.y; - } - - }, - - _end: function (e) { - // Check if scrolling is enabled - if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) { - return; - } - - // Prevent default if enabled - if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) { - e.preventDefault(); - } - - // Get the current point - var point = e.changedTouches ? e.changedTouches[0] : e, - momentumX, - momentumY, - // Calculate how long we've been dragging for time wise - duration = utils.getTime() - this.startTime, - // Store the new position rounded - newX = Math.round(this.x), - newY = Math.round(this.y), - // Calculate the total distance travelled - distanceX = Math.abs(newX - this.startX), - distanceY = Math.abs(newY - this.startY), - time = 0, - easing = ''; - - this.scrollTo(newX, newY); // ensures that the last position is rounded - - 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; - } - - // 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); - } - - 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) : { destination: newX, duration: 0 }; - momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0) : { destination: newY, duration: 0 }; - newX = momentumX.destination; - newY = momentumY.destination; - time = Math.max(momentumX.duration, momentumY.duration); - this.isInTransition = 1; - } - - - // If they enabled snapping (for page stuff), scroll to the next snap - 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; - } - - 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; - - this.scrollerWidth = this.scroller.offsetWidth; - this.scrollerHeight = this.scroller.offsetHeight; - - this.maxScrollX = this.wrapperWidth - this.scrollerWidth; - this.maxScrollY = this.wrapperHeight - this.scrollerHeight; - - 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(); - - }, - - on: function (type, fn) { - if ( !this._events[type] ) { - this._events[type] = []; - } - - this._events[type].push(fn); - }, - - _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].call(this); - } - }, - - 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; - - if ( !time || (this.options.useTransition && easing.style) ) { - 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) { - time = time || 0; - this.scrollerStyle[utils.style.transitionDuration] = time + 'ms'; - - - if ( this.indicator1 ) { - this.indicator1.transitionTime(time); - } - - if ( this.indicator2 ) { - this.indicator2.transitionTime(time); - } - - }, - - _transitionTimingFunction: function (easing) { - this.scrollerStyle[utils.style.transitionTimingFunction] = easing; - - - if ( this.indicator1 ) { - this.indicator1.transitionTimingFunction(easing); - } - - if ( this.indicator2 ) { - this.indicator2.transitionTimingFunction(easing); - } - - - }, - - _translate: function (x, y) { - if ( this.options.useTransform ) { - - this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ; - - } 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.indicator1 ) { // usually the vertical - this.indicator1.updatePosition(); - } - - if ( this.indicator2 ) { - this.indicator2.updatePosition(); - } - - }, - - _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); - - eventType(this.wrapper, 'mousedown', this); - eventType(target, 'mousemove', this); - eventType(target, 'mousecancel', this); - eventType(target, 'mouseup', this); - - if ( utils.hasPointer ) { - eventType(this.wrapper, 'MSPointerDown', this); - eventType(target, 'MSPointerMove', this); - eventType(target, 'MSPointerCancel', this); - eventType(target, 'MSPointerUp', this); - } - - if ( utils.hasTouch ) { - 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, - defaultScrollbars = typeof this.options.scrollbars != 'object', - customStyle = typeof this.options.scrollbars != 'string', - indicator1, - indicator2; - - if ( this.options.scrollbars ) { - // Vertical scrollbar - if ( this.options.scrollY ) { - indicator1 = { - el: createDefaultScrollbar('v', interactive, this.options.scrollbars), - interactive: interactive, - defaultScrollbars: true, - customStyle: customStyle, - resize: this.options.resizeIndicator, - listenX: false - }; - - this.wrapper.appendChild(indicator1.el); - } - - // Horizontal scrollbar - if ( this.options.scrollX ) { - indicator2 = { - el: createDefaultScrollbar('h', interactive, this.options.scrollbars), - interactive: interactive, - defaultScrollbars: true, - customStyle: customStyle, - resize: this.options.resizeIndicator, - listenY: false - }; - - this.wrapper.appendChild(indicator2.el); - } - } else { - indicator1 = this.options.indicators.length ? this.options.indicators[0] : this.options.indicators; - indicator2 = this.options.indicators[1] && this.options.indicators[1]; - } - - if ( indicator1 ) { - this.indicator1 = new Indicator(this, indicator1); - } - - if ( indicator2 ) { - this.indicator2 = new Indicator(this, indicator2); - } - - this.on('refresh', function () { - if ( this.indicator1 ) { - this.indicator1.refresh(); - } - - if ( this.indicator2 ) { - this.indicator2.refresh(); - } - }); - - this.on('destroy', function () { - if ( this.indicator1 ) { - this.indicator1.destroy(); - this.indicator1 = null; - } - - if ( this.indicator2 ) { - this.indicator2.destroy(); - this.indicator2 = null; - } - }); - }, - - _initWheel: function () { - utils.addEvent(this.wrapper, 'mousewheel', this); - utils.addEvent(this.wrapper, 'DOMMouseScroll', this); - - this.on('destroy', function () { - utils.removeEvent(this.wrapper, 'mousewheel', this); - utils.removeEvent(this.wrapper, 'DOMMouseScroll', this); - }); - }, - - _wheel: function (e) { - if ( !this.enabled ) { - return; - } - - var wheelDeltaX, wheelDeltaY, - newX, newY, - that = this; - - // Execute the scrollEnd event after 400ms the wheel stopped scrolling - clearTimeout(this.wheelTimeout); - this.wheelTimeout = setTimeout(function () { - that._execEvent('scrollEnd'); - }, 400); - - e.preventDefault(); - - if ( 'wheelDeltaX' in e ) { - wheelDeltaX = e.wheelDeltaX / 120; - wheelDeltaY = e.wheelDeltaY / 120; - } else if ( 'wheelDelta' in e ) { - wheelDeltaX = wheelDeltaY = e.wheelDelta / 120; - } else if ( 'detail' in e ) { - wheelDeltaX = wheelDeltaY = -e.detail / 3; - } else { - return; - } - - wheelDeltaX *= this.options.mouseWheelSpeed; - wheelDeltaY *= this.options.mouseWheelSpeed; - - 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 + (this.hasHorizontalScroll ? wheelDeltaX * this.options.invertWheelDirection : 0); - newY = this.y + (this.hasVerticalScroll ? wheelDeltaY * this.options.invertWheelDirection : 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); - - }, - - _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; - } - - 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 'MSPointerDown': - case 'mousedown': - this._start(e); - break; - case 'touchmove': - case 'MSPointerMove': - case 'mousemove': - this._move(e); - break; - case 'touchend': - case 'MSPointerUp': - case 'mouseup': - case 'touchcancel': - 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 'DOMMouseScroll': - case 'mousewheel': - this._wheel(e); - break; - case 'keydown': - this._key(e); - 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'; - } - - 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.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, - 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 ) { - utils.addEvent(this.indicator, 'touchstart', this); - utils.addEvent(this.indicator, 'MSPointerDown', this); - utils.addEvent(this.indicator, 'mousedown', this); - - utils.addEvent(window, 'touchend', this); - utils.addEvent(window, 'MSPointerUp', this); - utils.addEvent(window, 'mouseup', this); - } -} - -Indicator.prototype = { - handleEvent: function (e) { - switch ( e.type ) { - case 'touchstart': - case 'MSPointerDown': - case 'mousedown': - this._start(e); - break; - case 'touchmove': - case 'MSPointerMove': - case 'mousemove': - this._move(e); - break; - case 'touchend': - case 'MSPointerUp': - case 'mouseup': - case 'touchcancel': - case 'MSPointerCancel': - case 'mousecancel': - this._end(e); - break; - } - }, - - destroy: function () { - if ( this.options.interactive ) { - utils.removeEvent(this.indicator, 'touchstart', this); - utils.removeEvent(this.indicator, 'MSPointerDown', this); - utils.removeEvent(this.indicator, 'mousedown', this); - - utils.removeEvent(window, 'touchmove', this); - utils.removeEvent(window, 'MSPointerMove', this); - utils.removeEvent(window, 'mousemove', this); - - utils.removeEvent(window, 'touchend', this); - utils.removeEvent(window, 'MSPointerUp', 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(0); - - this.initiated = true; - this.moved = false; - this.lastPointX = point.pageX; - this.lastPointY = point.pageY; - - this.startTime = utils.getTime(); - - utils.addEvent(window, 'touchmove', this); - utils.addEvent(window, 'MSPointerMove', this); - utils.addEvent(window, 'mousemove', this); - - this.scroller._execEvent('scrollStart'); - }, - - _move: function (e) { - var point = e.touches ? e.touches[0] : e, - deltaX, deltaY, - newX, newY, - timestamp = utils.getTime(); - - 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); - - 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, 'MSPointerMove', 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; - this.indicatorStyle[utils.style.transitionDuration] = time + 'ms'; - }, - - transitionTimingFunction: function (easing) { - this.indicatorStyle[utils.style.transitionTimingFunction] = easing; - }, - - refresh: function () { - this.transitionTime(0); - - 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; - 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; - this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY)); - } - - this.updatePosition(); - }, - - updatePosition: function () { - var x = Math.round(this.sizeRatioX * this.scroller.x) || 0, - y = Math.round(this.sizeRatioY * this.scroller.y) || 0; - - if ( !this.options.ignoreBoundaries ) { - 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; - } - } - - 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); - } -}; - -ionic.views.Scroll.ease = utils.ease; - -})(window, document, Math, ionic); -; (function(ionic) { 'use strict'; diff --git a/js/views/scrollerView.js b/js/views/scrollerView.js deleted file mode 100644 index c5f7f046b8..0000000000 --- a/js/views/scrollerView.js +++ /dev/null @@ -1,1799 +0,0 @@ -/** - * Adapted from the great iScroll 5 for Ionic. iScroll is licensed under MIT just like Ionic. - * iScroll v5.0.5 ~ (c) 2008-2013 Matteo Spinelli ~ http://cubiq.org/license - * - * Think of ionic.views.Scroll like a Javascript version of UIScrollView or any - * scroll container in any UI library. You could just use -webkit-overflow-scrolling: touch, - * but you lose control over scroll behavior that native developers have with things - * like UIScrollView, and you don't get events after the finger stops touching the - * device (after a flick, for example) - * - */ -(function (window, document, Math, ionic) { - 'use strict'; - -var rAF = window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function (callback) { window.setTimeout(callback, 1000 / 60); }; - -/** - * Utilities for calculating momentum, etc. - */ -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.momentum = function (current, start, time, lowerMargin, wrapperSize) { - var distance = current - start, - speed = Math.abs(distance) / time, - destination, - duration, - deceleration = 0.0006; - - 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: navigator.msPointerEnabled, - hasTransition: _prefixStyle('transition') in _elementStyle - }); - - me.isAndroidBrowser = /Android/.test(window.navigator.appVersion) && /Version\/\d/.test(window.navigator.appVersion); - - me.extend(me.style = {}, { - transform: _transform, - transitionTimingFunction: _prefixStyle('transitionTimingFunction'), - transitionDuration: _prefixStyle('transitionDuration'), - 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, - - 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 (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') { - ev = document.createEvent('MouseEvents'); - ev.initMouseEvent('click', true, true, e.view, 1, - target.screenX, target.screenY, target.clientX, target.clientY, - e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, - 0, null); - - ev._constructed = true; - target.dispatchEvent(ev); - } - }; - - return me; -})(); - -ionic.views.Scroll = function (options) { - var el = options.el; - - this.wrapper = el;//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 = { - - resizeIndicator: true, - - mouseWheelSpeed: 20, - - snapThreshold: 0.334, - - startX: 0, - startY: 0, - scrollY: true, - directionLockThreshold: 5, - momentum: true, - - bounce: true, - bounceTime: 600, - bounceEasing: '', - - preventDefault: true, - preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ }, - - HWCompositing: true, - useTransition: true, - useTransform: true - }; - - 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'; - } - - this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1; - - // Some defaults - this.x = 0; - this.y = 0; - this.directionX = 0; - this.directionY = 0; - this._events = {}; - - this._init(); - this.refresh(); - - this.scrollTo(this.options.startX, this.options.startY); - this.enable(); -} - -ionic.views.Scroll.prototype = { - version: '5.0.5', - - _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(); - } - - }, - - destroy: function () { - this._initEvents(true); - - this._execEvent('destroy'); - }, - - _transitionEnd: function (e) { - if ( e.target != this.scroller ) { - return; - } - - this._transitionTime(0); - if ( !this.resetPosition(this.options.bounceTime) ) { - this._execEvent('scrollEnd'); - } - }, - - _start: function (e) { - // React to left mouse button only - if ( utils.eventType[e.type] != 1 ) { - if ( e.button !== 0 ) { - return; - } - } - - if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) { - return; - } - - if ( this.options.preventDefault && !utils.isAndroidBrowser && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) { - e.preventDefault(); // This seems to break default Android browser - } - - 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._transitionTime(); - - this.isAnimating = false; - this.startTime = utils.getTime(); - - if ( this.options.useTransition && this.isInTransition ) { - pos = this.getComputedPosition(); - - this._translate(Math.round(pos.x), Math.round(pos.y)); - this.isInTransition = false; - } - - 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('scrollStart'); - }, - - _move: function (e) { - // Make sure scrolling is enabled - if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) { - return; - } - - // Prevent default if necessary - if ( this.options.preventDefault ) { // increases performance on Android? TODO: check! - e.preventDefault(); - } - - // Grab the current touch point - var point = e.touches ? e.touches[0] : e, - // Check how far we've scrolled in the X direction - deltaX = this.hasHorizontalScroll ? point.pageX - this.pointX : 0, - // Check how far we've scrolled in the Y direction - deltaY = this.hasVerticalScroll ? point.pageY - this.pointY : 0, - // Get current timestmap - timestamp = utils.getTime(), - newX, newY, - absDistX, absDistY; - - // Store the current finger points - this.pointX = point.pageX; - this.pointY = point.pageY; - - // Calculate the total distance travelled - 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; - } - - // The new scroll point is the current position plus the delta moved since - // last - newX = this.x + deltaX; - newY = this.y + deltaY; - - // Slow down if outside of the boundaries (rubber banding) - 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; - } - - // Calcualte the directional multiplier - this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; - this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; - - // We moved - this.moved = true; - - // Translate to the new position - this._translate(newX, newY); - - // Check if the difference between right now and when we started is bigger than 300ms. - // We then reset the start positions to reduce the possibility of large errors - if ( timestamp - this.startTime > 300 ) { - this.startTime = timestamp; - this.startX = this.x; - this.startY = this.y; - } - - }, - - _end: function (e) { - // Check if scrolling is enabled - if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) { - return; - } - - // Prevent default if enabled - if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) { - e.preventDefault(); - } - - // Get the current point - var point = e.changedTouches ? e.changedTouches[0] : e, - momentumX, - momentumY, - // Calculate how long we've been dragging for time wise - duration = utils.getTime() - this.startTime, - // Store the new position rounded - newX = Math.round(this.x), - newY = Math.round(this.y), - // Calculate the total distance travelled - distanceX = Math.abs(newX - this.startX), - distanceY = Math.abs(newY - this.startY), - time = 0, - easing = ''; - - this.scrollTo(newX, newY); // ensures that the last position is rounded - - 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; - } - - // 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); - } - - 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) : { destination: newX, duration: 0 }; - momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0) : { destination: newY, duration: 0 }; - newX = momentumX.destination; - newY = momentumY.destination; - time = Math.max(momentumX.duration, momentumY.duration); - this.isInTransition = 1; - } - - - // If they enabled snapping (for page stuff), scroll to the next snap - 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; - } - - 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; - - this.scrollerWidth = this.scroller.offsetWidth; - this.scrollerHeight = this.scroller.offsetHeight; - - this.maxScrollX = this.wrapperWidth - this.scrollerWidth; - this.maxScrollY = this.wrapperHeight - this.scrollerHeight; - - 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(); - - }, - - on: function (type, fn) { - if ( !this._events[type] ) { - this._events[type] = []; - } - - this._events[type].push(fn); - }, - - _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].call(this); - } - }, - - 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; - - if ( !time || (this.options.useTransition && easing.style) ) { - 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) { - time = time || 0; - this.scrollerStyle[utils.style.transitionDuration] = time + 'ms'; - - - if ( this.indicator1 ) { - this.indicator1.transitionTime(time); - } - - if ( this.indicator2 ) { - this.indicator2.transitionTime(time); - } - - }, - - _transitionTimingFunction: function (easing) { - this.scrollerStyle[utils.style.transitionTimingFunction] = easing; - - - if ( this.indicator1 ) { - this.indicator1.transitionTimingFunction(easing); - } - - if ( this.indicator2 ) { - this.indicator2.transitionTimingFunction(easing); - } - - - }, - - _translate: function (x, y) { - if ( this.options.useTransform ) { - - this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ; - - } 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.indicator1 ) { // usually the vertical - this.indicator1.updatePosition(); - } - - if ( this.indicator2 ) { - this.indicator2.updatePosition(); - } - - }, - - _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); - - eventType(this.wrapper, 'mousedown', this); - eventType(target, 'mousemove', this); - eventType(target, 'mousecancel', this); - eventType(target, 'mouseup', this); - - if ( utils.hasPointer ) { - eventType(this.wrapper, 'MSPointerDown', this); - eventType(target, 'MSPointerMove', this); - eventType(target, 'MSPointerCancel', this); - eventType(target, 'MSPointerUp', this); - } - - if ( utils.hasTouch ) { - 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, - defaultScrollbars = typeof this.options.scrollbars != 'object', - customStyle = typeof this.options.scrollbars != 'string', - indicator1, - indicator2; - - if ( this.options.scrollbars ) { - // Vertical scrollbar - if ( this.options.scrollY ) { - indicator1 = { - el: createDefaultScrollbar('v', interactive, this.options.scrollbars), - interactive: interactive, - defaultScrollbars: true, - customStyle: customStyle, - resize: this.options.resizeIndicator, - listenX: false - }; - - this.wrapper.appendChild(indicator1.el); - } - - // Horizontal scrollbar - if ( this.options.scrollX ) { - indicator2 = { - el: createDefaultScrollbar('h', interactive, this.options.scrollbars), - interactive: interactive, - defaultScrollbars: true, - customStyle: customStyle, - resize: this.options.resizeIndicator, - listenY: false - }; - - this.wrapper.appendChild(indicator2.el); - } - } else { - indicator1 = this.options.indicators.length ? this.options.indicators[0] : this.options.indicators; - indicator2 = this.options.indicators[1] && this.options.indicators[1]; - } - - if ( indicator1 ) { - this.indicator1 = new Indicator(this, indicator1); - } - - if ( indicator2 ) { - this.indicator2 = new Indicator(this, indicator2); - } - - this.on('refresh', function () { - if ( this.indicator1 ) { - this.indicator1.refresh(); - } - - if ( this.indicator2 ) { - this.indicator2.refresh(); - } - }); - - this.on('destroy', function () { - if ( this.indicator1 ) { - this.indicator1.destroy(); - this.indicator1 = null; - } - - if ( this.indicator2 ) { - this.indicator2.destroy(); - this.indicator2 = null; - } - }); - }, - - _initWheel: function () { - utils.addEvent(this.wrapper, 'mousewheel', this); - utils.addEvent(this.wrapper, 'DOMMouseScroll', this); - - this.on('destroy', function () { - utils.removeEvent(this.wrapper, 'mousewheel', this); - utils.removeEvent(this.wrapper, 'DOMMouseScroll', this); - }); - }, - - _wheel: function (e) { - if ( !this.enabled ) { - return; - } - - var wheelDeltaX, wheelDeltaY, - newX, newY, - that = this; - - // Execute the scrollEnd event after 400ms the wheel stopped scrolling - clearTimeout(this.wheelTimeout); - this.wheelTimeout = setTimeout(function () { - that._execEvent('scrollEnd'); - }, 400); - - e.preventDefault(); - - if ( 'wheelDeltaX' in e ) { - wheelDeltaX = e.wheelDeltaX / 120; - wheelDeltaY = e.wheelDeltaY / 120; - } else if ( 'wheelDelta' in e ) { - wheelDeltaX = wheelDeltaY = e.wheelDelta / 120; - } else if ( 'detail' in e ) { - wheelDeltaX = wheelDeltaY = -e.detail / 3; - } else { - return; - } - - wheelDeltaX *= this.options.mouseWheelSpeed; - wheelDeltaY *= this.options.mouseWheelSpeed; - - 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 + (this.hasHorizontalScroll ? wheelDeltaX * this.options.invertWheelDirection : 0); - newY = this.y + (this.hasVerticalScroll ? wheelDeltaY * this.options.invertWheelDirection : 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); - - }, - - _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; - } - - 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 'MSPointerDown': - case 'mousedown': - this._start(e); - break; - case 'touchmove': - case 'MSPointerMove': - case 'mousemove': - this._move(e); - break; - case 'touchend': - case 'MSPointerUp': - case 'mouseup': - case 'touchcancel': - 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 'DOMMouseScroll': - case 'mousewheel': - this._wheel(e); - break; - case 'keydown': - this._key(e); - 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'; - } - - 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.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, - 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 ) { - utils.addEvent(this.indicator, 'touchstart', this); - utils.addEvent(this.indicator, 'MSPointerDown', this); - utils.addEvent(this.indicator, 'mousedown', this); - - utils.addEvent(window, 'touchend', this); - utils.addEvent(window, 'MSPointerUp', this); - utils.addEvent(window, 'mouseup', this); - } -} - -Indicator.prototype = { - handleEvent: function (e) { - switch ( e.type ) { - case 'touchstart': - case 'MSPointerDown': - case 'mousedown': - this._start(e); - break; - case 'touchmove': - case 'MSPointerMove': - case 'mousemove': - this._move(e); - break; - case 'touchend': - case 'MSPointerUp': - case 'mouseup': - case 'touchcancel': - case 'MSPointerCancel': - case 'mousecancel': - this._end(e); - break; - } - }, - - destroy: function () { - if ( this.options.interactive ) { - utils.removeEvent(this.indicator, 'touchstart', this); - utils.removeEvent(this.indicator, 'MSPointerDown', this); - utils.removeEvent(this.indicator, 'mousedown', this); - - utils.removeEvent(window, 'touchmove', this); - utils.removeEvent(window, 'MSPointerMove', this); - utils.removeEvent(window, 'mousemove', this); - - utils.removeEvent(window, 'touchend', this); - utils.removeEvent(window, 'MSPointerUp', 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(0); - - this.initiated = true; - this.moved = false; - this.lastPointX = point.pageX; - this.lastPointY = point.pageY; - - this.startTime = utils.getTime(); - - utils.addEvent(window, 'touchmove', this); - utils.addEvent(window, 'MSPointerMove', this); - utils.addEvent(window, 'mousemove', this); - - this.scroller._execEvent('scrollStart'); - }, - - _move: function (e) { - var point = e.touches ? e.touches[0] : e, - deltaX, deltaY, - newX, newY, - timestamp = utils.getTime(); - - 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); - - 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, 'MSPointerMove', 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; - this.indicatorStyle[utils.style.transitionDuration] = time + 'ms'; - }, - - transitionTimingFunction: function (easing) { - this.indicatorStyle[utils.style.transitionTimingFunction] = easing; - }, - - refresh: function () { - this.transitionTime(0); - - 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; - 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; - this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY)); - } - - this.updatePosition(); - }, - - updatePosition: function () { - var x = Math.round(this.sizeRatioX * this.scroller.x) || 0, - y = Math.round(this.sizeRatioY * this.scroller.y) || 0; - - if ( !this.options.ignoreBoundaries ) { - 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; - } - } - - 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); - } -}; - -ionic.views.Scroll.ease = utils.ease; - -})(window, document, Math, ionic);