diff --git a/config/karma.conf.js b/config/karma.conf.js
index 95eb989cc91..81915fffa0b 100644
--- a/config/karma.conf.js
+++ b/config/karma.conf.js
@@ -18,7 +18,7 @@ module.exports = function(config) {
'config/lib/js/angular/angular.js',
'config/lib/js/angular/angular-animate.js',
'config/lib/js/angular/angular-mocks.js',
- 'config/lib/js/angular-ui/angular-ui-router.js',
+ 'config/lib/js/angular-ui/angular-ui-router.js'
]
.concat(buildConfig.ionicFiles)
.concat(buildConfig.angularIonicFiles)
diff --git a/js/ext/angular/src/controller/ionicScrollController.js b/js/ext/angular/src/controller/ionicScrollController.js
index 928e334cf4f..8a5198de267 100644
--- a/js/ext/angular/src/controller/ionicScrollController.js
+++ b/js/ext/angular/src/controller/ionicScrollController.js
@@ -3,8 +3,8 @@
angular.module('ionic.ui.scroll')
-.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout',
- function($scope, scrollViewOptions, $timeout) {
+.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout', '$ionicScrollDelegate',
+ function($scope, scrollViewOptions, $timeout, $ionicScrollDelegate) {
scrollViewOptions.bouncing = angular.isDefined(scrollViewOptions.bouncing) ?
scrollViewOptions.bouncing :
@@ -15,11 +15,14 @@ angular.module('ionic.ui.scroll')
var element = this.element = scrollViewOptions.el;
var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
- this.$element = angular.element(element);
+ var $element = this.$element = angular.element(element);
//Attach self to element as a controller so other directives can require this controller
//through `require: '$ionicScroll'
- this.$element.data('$$ionicScrollController', this);
+ $element.data('$$ionicScrollController', this);
+
+ //Register delegate for event handling
+ $ionicScrollDelegate.register($scope, $element, scrollView);
$timeout(function() {
scrollView.run();
diff --git a/js/ext/angular/src/directive/ionicContent.js b/js/ext/angular/src/directive/ionicContent.js
index ce206736072..0012e6f8474 100644
--- a/js/ext/angular/src/directive/ionicContent.js
+++ b/js/ext/angular/src/directive/ionicContent.js
@@ -120,9 +120,6 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
};
}
- // Register for scroll delegate event handling
- $ionicScrollDelegate.register($scope, $element);
-
// Check if this supports infinite scrolling and listen for scroll events
// to trigger the infinite scrolling
// TODO(ajoslin): move functionality out of this function and make testable
diff --git a/js/ext/angular/src/directive/ionicScroll.js b/js/ext/angular/src/directive/ionicScroll.js
index 63513750783..1eb88e2f418 100644
--- a/js/ext/angular/src/directive/ionicScroll.js
+++ b/js/ext/angular/src/directive/ionicScroll.js
@@ -33,8 +33,6 @@ angular.module('ionic.ui.scroll', [])
function prelink($scope, $element, $attr) {
var scrollView, scrollCtrl, sc = $element[0].children[0];
- // Create the internal scroll div
- sc.className = 'scroll';
if(attr.padding == "true") {
sc.classList.add('padding');
}
@@ -63,25 +61,6 @@ angular.module('ionic.ui.scroll', [])
scrollViewOptions: scrollViewOptions
});
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
-
- $element.bind('scroll', function(e) {
- $scope.onScroll({
- event: e,
- scrollTop: e.detail ? e.detail.scrollTop : e.originalEvent ? e.originalEvent.detail.scrollTop : 0,
- scrollLeft: e.detail ? e.detail.scrollLeft: e.originalEvent ? e.originalEvent.detail.scrollLeft : 0
- });
- });
-
- $scope.$parent.$on('scroll.resize', function(e) {
- // Run the resize after this digest
- $timeout(function() {
- scrollView && scrollView.resize();
- });
- });
-
- $scope.$parent.$on('scroll.refreshComplete', function(e) {
- scrollView && scrollView.finishPullToRefresh();
- });
}
}
};
diff --git a/js/ext/angular/src/service/delegates/ionicScrollDelegate.js b/js/ext/angular/src/service/delegates/ionicScrollDelegate.js
index d4bb6aa5797..9b2ea289583 100644
--- a/js/ext/angular/src/service/delegates/ionicScrollDelegate.js
+++ b/js/ext/angular/src/service/delegates/ionicScrollDelegate.js
@@ -14,6 +14,9 @@ angular.module('ionic.ui.service.scrollDelegate', [])
scrollBottom: function(animate) {
$rootScope.$broadcast('scroll.scrollBottom', animate);
},
+ scrollTo: function(left, top, animate) {
+ $rootScope.$broadcast('scroll.scrollTo', left, top, animate);
+ },
resize: function() {
$rootScope.$broadcast('scroll.resize');
},
@@ -45,18 +48,14 @@ angular.module('ionic.ui.service.scrollDelegate', [])
getScrollView: function($scope) {
return $scope.scrollView;
},
+
/**
- * Register a scope for scroll event handling.
+ * Register a scope and scroll view for scroll event handling.
* $scope {Scope} the scope to register and listen for events
*/
- register: function($scope, $element) {
- //Get scroll controller from parent
- var scrollCtrl = $element.controller('$ionicScroll');
- if (!scrollCtrl) {
- return;
- }
- var scrollView = scrollCtrl.scrollView;
- var scrollEl = scrollCtrl.element;
+ register: function($scope, $element, scrollView) {
+
+ var scrollEl = $element[0];
function scrollViewResize() {
// Run the resize after this digest
@@ -93,14 +92,14 @@ angular.module('ionic.ui.service.scrollDelegate', [])
});
});
- /**
- * Called to scroll to the top of the content
- *
- * @param animate {boolean} whether to animate or just snap
- */
+ $scope.$parent.$on('scroll.scrollTo', function(e, left, top, animate) {
+ scrollViewResize().then(function() {
+ scrollView.scrollTo(left, top, !!animate);
+ });
+ });
$scope.$parent.$on('scroll.scrollTop', function(e, animate) {
scrollViewResize().then(function() {
- scrollView.scrollTo(0, 0, animate === false ? false : true);
+ scrollView.scrollTo(0, 0, !!animate);
});
});
$scope.$parent.$on('scroll.scrollBottom', function(e, animate) {
@@ -108,7 +107,7 @@ angular.module('ionic.ui.service.scrollDelegate', [])
var sv = scrollView;
if (sv) {
var max = sv.getScrollMax();
- sv.scrollTo(0, max.top, animate === false ? false : true);
+ sv.scrollTo(max.left, max.top, !!animate);
}
});
});
diff --git a/js/ext/angular/test/controller/ionicScrollController.unit.js b/js/ext/angular/test/controller/ionicScrollController.unit.js
index c8c93d030b9..ac11a8b789c 100644
--- a/js/ext/angular/test/controller/ionicScrollController.unit.js
+++ b/js/ext/angular/test/controller/ionicScrollController.unit.js
@@ -1,6 +1,6 @@
describe('$ionicScroll Controller', function() {
- beforeEach(module('ionic.ui.scroll'));
+ beforeEach(module('ionic'));
var scope, ctrl, timeout;
function setup(options) {
@@ -41,6 +41,12 @@ describe('$ionicScroll Controller', function() {
expect(ctrl.scrollView.run).toHaveBeenCalled();
});
+ it('should register with $ionicScrollDelegate', inject(function($ionicScrollDelegate) {
+ spyOn($ionicScrollDelegate, 'register');
+ setup();
+ expect($ionicScrollDelegate.register).toHaveBeenCalledWith(scope, ctrl.$element, ctrl.scrollView);
+ }));
+
it('should not setup if no child .scroll-refresher', function() {
setup();
timeout.flush();
@@ -70,7 +76,6 @@ describe('$ionicScroll Controller', function() {
});
scope.onRefresh = jasmine.createSpy('onRefresh');
- scope.$parent.$broadcast = jasmine.createSpy('$broadcast');
timeout.flush();
var refresher = ctrl.refresher;
@@ -87,13 +92,11 @@ describe('$ionicScroll Controller', function() {
expect(refresher.classList.contains('refreshing')).toBe(false);
expect(scope.onRefresh).not.toHaveBeenCalled();
- expect(scope.$parent.$broadcast).not.toHaveBeenCalledWith('scroll.onRefresh');
doneCb();
expect(refresher.classList.contains('active')).toBe(false);
expect(refresher.classList.contains('refreshing')).toBe(true);
expect(scope.onRefresh).toHaveBeenCalled();
- expect(scope.$parent.$broadcast).toHaveBeenCalledWith('scroll.onRefresh');
});
});
diff --git a/js/ext/angular/test/directive/ionicContent.unit.js b/js/ext/angular/test/directive/ionicContent.unit.js
index 6de09dd1eb1..a569679740c 100644
--- a/js/ext/angular/test/directive/ionicContent.unit.js
+++ b/js/ext/angular/test/directive/ionicContent.unit.js
@@ -1,7 +1,7 @@
describe('Ionic Content directive', function() {
var compile, element, scope;
- beforeEach(module('ionic.ui.content'));
+ beforeEach(module('ionic'));
beforeEach(inject(function($compile, $rootScope, $timeout, $window) {
compile = $compile;
@@ -11,6 +11,11 @@ describe('Ionic Content directive', function() {
ionic.Platform.setPlatform('Android');
}));
+ it('Has $ionicScroll controller', function() {
+ element = compile('')(scope);
+ expect(element.controller('$ionicScroll').element).toBe(element[0]);
+ });
+
it('Has content class', function() {
element = compile('')(scope);
expect(element.hasClass('scroll-content')).toBe(true);
diff --git a/js/ext/angular/test/directive/ionicScroll.unit.js b/js/ext/angular/test/directive/ionicScroll.unit.js
new file mode 100644
index 00000000000..cff691b81d6
--- /dev/null
+++ b/js/ext/angular/test/directive/ionicScroll.unit.js
@@ -0,0 +1,67 @@
+describe('Ionic Scroll Directive', function() {
+ var compile, element, scope;
+
+ beforeEach(module('ionic'));
+
+ beforeEach(inject(function($compile, $rootScope, $timeout, $window) {
+ compile = $compile;
+ scope = $rootScope;
+ timeout = $timeout;
+ window = $window;
+ ionic.Platform.setPlatform('Android');
+ }));
+
+ it('Has $ionicScroll controller', function() {
+ element = compile('')(scope);
+ expect(element.controller('$ionicScroll').element).toBe(element[0]);
+ });
+
+ it('Has scroll-view class', function() {
+ element = compile('')(scope);
+ expect(element.hasClass('scroll-view')).toBe(true);
+ });
+
+ it('should add padding classname', function() {
+ element = compile('')(scope);
+ expect(element.children().eq(0).hasClass('padding')).toEqual(true);
+ var scrollElement = element.find('.scroll');
+ expect(scrollElement.hasClass('padding')).toEqual(true);
+ });
+
+ it('Enables bouncing by default', function() {
+ ionic.Platform.setPlatform('iPhone');
+ element = compile('')(scope);
+ scope.$apply();
+ var newScope = element.isolateScope();
+ var scrollView = scope.scrollView;
+ expect(scrollView.options.bouncing).toBe(true);
+ });
+
+ it('Disables bouncing when has-bouncing = false', function() {
+ ionic.Platform.setPlatform('iPhone');
+ element = compile('')(scope);
+ scope.$apply();
+ var newScope = element.isolateScope();
+ var scrollView = scope.scrollView;
+ expect(scrollView.options.bouncing).toBe(false);
+ });
+
+ it('Disables bouncing by default on Android', function() {
+ ionic.Platform.setPlatform('Android');
+ element = compile('')(scope);
+ scope.$apply();
+ var newScope = element.isolateScope();
+ var scrollView = scope.scrollView;
+ expect(scrollView.options.bouncing).toBe(false);
+ });
+
+ it('Should set start x and y', function() {
+ element = compile('')(scope);
+ scope.$apply();
+ var newScope = element.isolateScope();
+ var scrollView = scope.scrollView;
+ var vals = scrollView.getValues();
+ expect(vals.left).toBe(100);
+ expect(vals.top).toBe(300);
+ });
+});
diff --git a/js/ext/angular/test/scroll.html b/js/ext/angular/test/scroll.html
index a581b3a1e1c..3e4488a6feb 100644
--- a/js/ext/angular/test/scroll.html
+++ b/js/ext/angular/test/scroll.html
@@ -52,20 +52,24 @@
Hourly Forecast
-
+
-
+
+ Scroll to 100
+
+
@@ -83,7 +87,7 @@ Hourly Forecast
}
})
- .controller('ThisCtrl', function($scope) {
+ .controller('ThisCtrl', function($scope, $ionicScrollDelegate) {
var header = document.getElementById('header');
var content = document.getElementById('container');
@@ -97,6 +101,10 @@ Hourly Forecast
$scope.onScrollComplete = function(event, scrollTop, scrollLeft) {
console.log('Scroll complete', scrollTop, scrollLeft);
}
+ $scope.scrollTo = function() {
+ console.log('scrollTo');
+ $ionicScrollDelegate.scrollTo(0, 100, true);
+ };
$scope.onScroll = function(event, scrollTop, scrollLeft) {
/*
if(scrollTop > startTop) {
diff --git a/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js b/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js
index 17f7562f262..3bf027b0675 100644
--- a/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js
+++ b/js/ext/angular/test/service/delegates/ionicScrollDelegate.unit.js
@@ -11,15 +11,6 @@ describe('Ionic ScrollDelegate Service', function() {
compile = $compile;
}));
- it('Should register', function() {
- spyOn(del, 'register');
-
- var scope = rootScope.$new();
- var el = compile('')(scope);
-
- expect(del.register).toHaveBeenCalled();
- });
-
it('Should get scroll view', function() {
var scope = rootScope.$new();
var el = compile('')(scope);
@@ -33,43 +24,64 @@ describe('Ionic ScrollDelegate Service', function() {
var sv = del.getScrollView(scope);
spyOn(sv, 'resize');
+ spyOn(sv, 'scrollTo');
del.resize();
timeout.flush();
expect(sv.resize).toHaveBeenCalled();
});
- it('Should resize & scroll top', function() {
- var scope = rootScope.$new();
- var el = compile('')(scope);
+ testWithAnimate(true);
+ testWithAnimate(false);
+ function testWithAnimate(animate) {
+ describe('with animate='+animate, function() {
+ it('should resize & scroll top', function() {
+ var scope = rootScope.$new();
+ var el = compile('')(scope);
- var sv = del.getScrollView(scope);
- spyOn(sv, 'resize');
+ var sv = del.getScrollView(scope);
+ spyOn(sv, 'resize');
+ spyOn(sv, 'scrollTo');
+ del.scrollTop(animate);
- expect(sv.getValues().top).toBe(100);
+ timeout.flush();
+ expect(sv.resize).toHaveBeenCalled();
+ expect(sv.scrollTo.mostRecentCall.args).toEqual([0, 0, animate]);
+ });
- del.scrollTop(false);
- timeout.flush();
- expect(sv.resize).toHaveBeenCalled();
+ it('should resize & scroll bottom', function() {
+ var scope = rootScope.$new();
+ var el = compile('
')(scope);
- expect(sv.getValues().top).toBe(0);
- });
-
- it('Should resize & scroll bottom', function() {
- var scope = rootScope.$new();
- var el = compile('')(scope);
+ var sv = del.getScrollView(scope);
+ spyOn(sv, 'getScrollMax').andCallFake(function() {
+ return { left: 10, top: 11 };
+ });
+ spyOn(sv, 'resize');
+ spyOn(sv, 'scrollTo');
+ var max = sv.getScrollMax();
+ del.scrollBottom(animate);
- var sv = del.getScrollView(scope);
- spyOn(sv, 'resize');
+ timeout.flush();
+ expect(sv.resize).toHaveBeenCalled();
+ expect(sv.scrollTo.mostRecentCall.args).toEqual([max.left, max.top, animate]);
+ });
- expect(sv.getValues().top).toBe(100);
+ it('should resize & scrollTo', function() {
+ var scope = rootScope.$new();
+ var el = compile('
')(scope);
- del.scrollBottom(false);
- timeout.flush();
- expect(sv.resize).toHaveBeenCalled();
+ var sv = del.getScrollView(scope);
+ spyOn(sv, 'scrollTo');
+ spyOn(sv, 'resize');
+ del.scrollTo(2, 3, animate);
- expect(sv.getValues().top).toBe(sv.getScrollMax().top);
- });
+ timeout.flush();
+ expect(sv.resize).toHaveBeenCalled();
+ expect(sv.scrollTo.mostRecentCall.args).toEqual([2, 3, animate]);
+ });
+ });
+ }
it('should finish refreshing', function() {
var scope = rootScope.$new();