/** * @private */ IonicModule .factory('$$scrollValueCache', function() { return {}; }) .controller('$ionicScroll', [ '$scope', 'scrollViewOptions', '$timeout', '$window', '$$scrollValueCache', '$location', '$rootScope', '$document', '$ionicScrollDelegate', function($scope, scrollViewOptions, $timeout, $window, $$scrollValueCache, $location, $rootScope, $document, $ionicScrollDelegate) { var self = this; // for testing this.__timeout = $timeout; this._scrollViewOptions = scrollViewOptions; //for testing var element = this.element = scrollViewOptions.el; var $element = this.$element = jqLite(element); var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions); //Attach self to element as a controller so other directives can require this controller //through `require: '$ionicScroll' //Also attach to parent so that sibling elements can require this ($element.parent().length ? $element.parent() : $element) .data('$$ionicScrollController', this); var deregisterInstance = $ionicScrollDelegate._registerInstance( this, scrollViewOptions.delegateHandle ); if (!angular.isDefined(scrollViewOptions.bouncing)) { ionic.Platform.ready(function() { scrollView.options.bouncing = true; if(ionic.Platform.isAndroid()) { // No bouncing by default on Android scrollView.options.bouncing = false; // Faster scroll decel scrollView.options.deceleration = 0.95; } else { } }); } var resize = angular.bind(scrollView, scrollView.resize); ionic.on('resize', resize, $window); // set by rootScope listener if needed var backListenDone = angular.noop; $scope.$on('$destroy', function() { deregisterInstance(); scrollView.__removeEventHandlers(); ionic.off('resize', resize, $window); $window.removeEventListener('resize', resize); backListenDone(); if (self._rememberScrollId) { $$scrollValueCache[self._rememberScrollId] = scrollView.getValues(); } }); $element.on('scroll', function(e) { var detail = (e.originalEvent || e).detail || {}; $scope.$onScroll && $scope.$onScroll({ event: e, scrollTop: detail.scrollTop || 0, scrollLeft: detail.scrollLeft || 0 }); }); $scope.$on('$viewContentLoaded', function(e, historyData) { //only the top-most scroll area under a view should remember that view's //scroll position if (e.defaultPrevented) { return; } e.preventDefault(); var viewId = historyData && historyData.viewId || $scope.$historyId; if (viewId) { $timeout(function() { self.rememberScrollPosition(viewId); self.scrollToRememberedPosition(); backListenDone = $rootScope.$on('$viewHistory.viewBack', function(e, fromViewId, toViewId) { //When going back from this view, forget its saved scroll position if (viewId === fromViewId) { self.forgetScrollPosition(); } }); }, 0, false); } }); $timeout(function() { scrollView.run(); }); this._rememberScrollId = null; this.getScrollView = function() { return this.scrollView; }; this.getScrollPosition = function() { return this.scrollView.getValues(); }; this.resize = function() { return $timeout(resize); }; this.scrollTop = function(shouldAnimate) { this.resize().then(function() { scrollView.scrollTo(0, 0, !!shouldAnimate); }); }; this.scrollBottom = function(shouldAnimate) { this.resize().then(function() { var max = scrollView.getScrollMax(); scrollView.scrollTo(max.left, max.top, !!shouldAnimate); }); }; this.scrollTo = function(left, top, shouldAnimate) { this.resize().then(function() { scrollView.scrollTo(left, top, !!shouldAnimate); }); }; this.scrollBy = function(left, top, shouldAnimate) { this.resize().then(function() { scrollView.scrollBy(left, top, !!shouldAnimate); }); }; this.anchorScroll = function(shouldAnimate) { this.resize().then(function() { var hash = $location.hash(); var elm = hash && $document[0].getElementById(hash); if (!(hash && elm)) { scrollView.scrollTo(0,0, !!shouldAnimate); return; } var curElm = elm; var scrollLeft = 0, scrollTop = 0, levelsClimbed = 0; do { if(curElm !== null)scrollLeft += curElm.offsetLeft; if(curElm !== null)scrollTop += curElm.offsetTop; curElm = curElm.offsetParent; levelsClimbed++; } while (curElm.attributes != self.element.attributes && curElm.offsetParent); scrollView.scrollTo(scrollLeft, scrollTop, !!shouldAnimate); }); }; this.rememberScrollPosition = function(id) { if (!id) { throw new Error("Must supply an id to remember the scroll by!"); } this._rememberScrollId = id; }; this.forgetScrollPosition = function() { delete $$scrollValueCache[this._rememberScrollId]; this._rememberScrollId = null; }; this.scrollToRememberedPosition = function(shouldAnimate) { var values = $$scrollValueCache[this._rememberScrollId]; if (values) { this.resize().then(function() { scrollView.scrollTo(+values.left, +values.top, shouldAnimate); }); } }; /** * @private */ this._setRefresher = function(refresherScope, refresherElement) { var refresher = this.refresher = refresherElement; var refresherHeight = self.refresher.clientHeight || 0; scrollView.activatePullToRefresh(refresherHeight, function() { // activateCallback refresher.classList.add('active'); refresherScope.$onPulling(); }, function() { // deactivateCallback $timeout(function(){ refresher.classList.remove('active'); refresher.classList.remove('refreshing'); refresher.classList.add('invisible'); },300); }, function() { // startCallback refresher.classList.add('refreshing'); refresherScope.$onRefresh(); },function(){ // showCallback refresher.classList.remove('invisible'); console.log('showing') },function(){ // hideCallback refresher.classList.add('invisible'); console.log('hiding'); }); }; }]);