mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(ionic): remove all delegates
BREAKING CHANGE: $ionicScrollDelegate, $ionicSlideBoxDelegate, and
$ionicSideMenuDelegate have been removed.
- $ionicScrollDelegate has been changed to $ionicScrollController.
Documentation:
[ionContent](
http://ajoslin.github.io/docs/nightly/api/directive/ionContent),
[ionScroll](
http://ajoslin.github.io/docs/nightly/api/directive/ionScroll)
Change your code from this:
```html
<ion-content ng-controller="MyCtrl">
<button ng-click="scrollBottom()">Scroll to bottom!</button>
</ion-content>
```
```js
function MyCtrl($scope, $ionicScrollDelegate) {
$scope.scrollBottom = function() {
$ionicScrollDelegate.scrollBottom();
};
}
```
To this:
```html
<!-- optional attr controller-bind, see docs -->
<ion-content ng-controller="MyCtrl">
<button ng-click="scrollBottom()">Scroll to bottom!</button>
</ion-content>
```
```js
function MyCtrl($scope) {
$scope.scrollBottom = function() {
$scope.$ionicScrollController.scrollBottom();
};
}
```
- $ionicSideMenuDelegate has been changed to
$ionicSideMenusController. Documentation:
[ionSideMenus](http://ajoslin.github.io/docs/nightly/api/directive/ionSideMenus)
Change your code from this:
```html
<ion-side-menus>
<ion-side-menu side="left">Side Menu Left</ion-side-menu>
<ion-pane ion-side-menu-content ng-controller="MyCtrl">
<button ng-click="toggleLeftMenu()">
Toggle Left Menu!
</button>
</ion-pane>
</ion-side-menus>
```
```js
function MyCtrl($scope, $ionicSideMenuDelegate) {
$scope.toggleLeftMenu = function() {
$ionicSideMenuDelegate.toggleLeft();
};
}
```
To this:
```html
<!-- optional attr controller-bind, see documentation -->
<ion-side-menus>
<ion-side-menu side="left">Side Menu Left</ion-side-menu>
<ion-pane ion-side-menu-content ng-controller="MyCtrl">
<button ng-click="toggleLeftMenu()">
Toggle Left Menu!
</button>
</ion-pane>
</ion-side-menus>
```
```js
function MyCtrl($scope) {
$scope.toggleLeftMenu = function() {
$scope.$ionicSideMenuController.toggleLeft();
};
}
```
- $ionicSlideBoxDelegate has been removed and upgraded to
$ionicSlideBoxController. It had only one method that
was unneeded. [Documentation](
http://ajoslin.github.io/docs/nightly/api/directive/ionSlideBox)
This commit is contained in:
@@ -6,15 +6,36 @@ angular.module('ionic.ui.scroll')
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout', '$ionicScrollDelegate', '$window', function($scope, scrollViewOptions, $timeout, $ionicScrollDelegate, $window) {
|
||||
.factory('$$scrollValueCache', function() {
|
||||
return {};
|
||||
})
|
||||
|
||||
.controller('$ionicScroll', [
|
||||
'$scope',
|
||||
'scrollViewOptions',
|
||||
'$timeout',
|
||||
'$window',
|
||||
'$$scrollValueCache',
|
||||
'$location',
|
||||
'$parse',
|
||||
'$rootScope',
|
||||
'$document',
|
||||
function($scope, scrollViewOptions, $timeout, $window, $$scrollValueCache, $location, $parse, $rootScope, $document) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var element = this.element = scrollViewOptions.el;
|
||||
var $element = this.$element = angular.element(element);
|
||||
var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
|
||||
|
||||
this.$scope = $scope;
|
||||
$scope.$parent.$$ionicScrollController = this;
|
||||
//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);
|
||||
|
||||
$parse(scrollViewOptions.controllerBind || '$ionicScrollController')
|
||||
.assign($scope.$parent, this);
|
||||
|
||||
if (!angular.isDefined(scrollViewOptions.bouncing)) {
|
||||
ionic.Platform.ready(function() {
|
||||
@@ -22,42 +43,107 @@ angular.module('ionic.ui.scroll')
|
||||
});
|
||||
}
|
||||
|
||||
var $element = this.$element = angular.element(element);
|
||||
|
||||
//Attach self to element as a controller so other directives can require this controller
|
||||
//through `require: '$ionicScroll'
|
||||
$element.data('$$ionicScrollController', this);
|
||||
|
||||
//Register delegate for event handling
|
||||
$ionicScrollDelegate.register($scope, $element, scrollView);
|
||||
|
||||
var resize = angular.bind(scrollView, scrollView.resize);
|
||||
$window.addEventListener('resize', resize);
|
||||
|
||||
$scope.$on('$viewContentLoaded', function(e, historyData) {
|
||||
if (e.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
//only the top-most scroll area under a view should remember that view's
|
||||
//scroll position
|
||||
e.preventDefault();
|
||||
|
||||
var values = historyData && historyData.rememberedScrollValues;
|
||||
if (values) {
|
||||
$timeout(function() {
|
||||
scrollView.scrollTo(+values.left || null, +values.top || null);
|
||||
}, 0, false);
|
||||
}
|
||||
$scope.$on('$destroy', function() {
|
||||
historyData && (historyData.rememberedScrollValues = scrollView.getValues());
|
||||
});
|
||||
});
|
||||
// set by rootScope listener if needed
|
||||
var backListenDone = angular.noop;
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
$window.removeEventListener('resize', resize);
|
||||
backListenDone();
|
||||
if (self._rememberScrollId) {
|
||||
$$scrollValueCache[self._rememberScrollId] = scrollView.getValues();
|
||||
}
|
||||
});
|
||||
|
||||
this.setRefresher = function(refresherScope, refresherElement) {
|
||||
$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.viewId;
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
scrollView.run();
|
||||
});
|
||||
|
||||
this._rememberScrollId = null;
|
||||
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) {
|
||||
scrollView.scrollTo(+values.left, +values.top, shouldAnimate);
|
||||
}
|
||||
};
|
||||
|
||||
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.anchorScroll = function(shouldAnimate) {
|
||||
this.resize().then(function() {
|
||||
var hash = $location.hash();
|
||||
var elm = hash && $document[0].getElementById(hash);
|
||||
if (hash && elm) {
|
||||
var scroll = ionic.DomUtil.getPositionInParent(elm, self.$element);
|
||||
scrollView.scrollTo(scroll.left, scroll.top, !!shouldAnimate);
|
||||
} else {
|
||||
scrollView.scrollTo(0,0, !!shouldAnimate);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this._setRefresher = function(refresherScope, refresherElement) {
|
||||
var refresher = this.refresher = refresherElement;
|
||||
var refresherHeight = self.refresher.clientHeight || 0;
|
||||
scrollView.activatePullToRefresh(refresherHeight, function() {
|
||||
@@ -71,10 +157,6 @@ angular.module('ionic.ui.scroll')
|
||||
refresherScope.$onRefresh();
|
||||
});
|
||||
};
|
||||
|
||||
$timeout(function() {
|
||||
scrollView.run();
|
||||
});
|
||||
}]);
|
||||
|
||||
})();
|
||||
|
||||
29
js/ext/angular/src/directive/ionicBar.js
vendored
29
js/ext/angular/src/directive/ionicBar.js
vendored
@@ -3,11 +3,36 @@
|
||||
|
||||
angular.module('ionic.ui.header', ['ngAnimate', 'ngSanitize'])
|
||||
|
||||
.directive('barHeader', ['$ionicScrollDelegate', function($ionicScrollDelegate) {
|
||||
.directive('barHeader', ['$document', function($document) {
|
||||
return {
|
||||
restrict: 'C',
|
||||
link: function($scope, $element, $attr) {
|
||||
$ionicScrollDelegate($scope).tapScrollToTop($element);
|
||||
ionic.requestAnimationFrame(function() {
|
||||
var scrollCtrl = $element.controller('$ionicScroll');
|
||||
if (!scrollCtrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ionic.on('tap', onTap, $element[0]);
|
||||
$scope.$on('$destroy', function() {
|
||||
ionic.off('tap', onTap, $element[0]);
|
||||
});
|
||||
|
||||
function onTap(e) {
|
||||
if (ionic.DomUtil.getParentOrSelfWithClass(e.target, 'button', 4)) {
|
||||
return;
|
||||
}
|
||||
var touch = e.gesture && e.gesture.touches[0] || e.detail.touches[0];
|
||||
var bounds = $element[0].getBoundingClientRect();
|
||||
if(ionic.DomUtil.rectContains(
|
||||
touch.pageX, touch.pageY,
|
||||
bounds.left, bounds.top - 20,
|
||||
bounds.left + bounds.width, bounds.top + bounds.height)
|
||||
) {
|
||||
scrollCtrl.scrollTop(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}])
|
||||
|
||||
21
js/ext/angular/src/directive/ionicContent.js
vendored
21
js/ext/angular/src/directive/ionicContent.js
vendored
@@ -1,7 +1,7 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
|
||||
angular.module('ionic.ui.content', ['ionic.ui.scroll'])
|
||||
|
||||
/**
|
||||
* Panel is a simple 100% width and height, fixed panel. It's meant for content to be
|
||||
@@ -28,6 +28,8 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
|
||||
* @ngdoc directive
|
||||
* @name ionContent
|
||||
* @module ionic
|
||||
* @controller ionicScroll as $scope.$ionicScrollController
|
||||
* @restrict E
|
||||
*
|
||||
* @description
|
||||
* The ionContent directive provides an easy to use content area that can be configured
|
||||
@@ -45,6 +47,9 @@ angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
|
||||
* Use the classes 'has-header', 'has-subheader', 'has-footer', and 'has-tabs'
|
||||
* to modify the positioning of the ion-content relative to surrounding elements.
|
||||
*
|
||||
* @param {string=} controller-bind The scope variable to bind this element's scrollView's
|
||||
* {@link ionic.controller:ionicScroll ionicScroll controller} to.
|
||||
* Default: $scope.$ionicScrollController.
|
||||
* @param {boolean=} padding Whether to add padding to the content.
|
||||
* of the content. Defaults to true on iOS, false on Android.
|
||||
* @param {boolean=} scroll Whether to allow scrolling of content. Defaults to true.
|
||||
@@ -67,6 +72,7 @@ function($parse, $timeout, $controller, $ionicBind) {
|
||||
transclude: true,
|
||||
require: '^?ionNavView',
|
||||
scope: true,
|
||||
priority: 501,
|
||||
template:
|
||||
'<div class="scroll-content">' +
|
||||
'<div class="scroll"></div>' +
|
||||
@@ -97,9 +103,11 @@ function($parse, $timeout, $controller, $ionicBind) {
|
||||
scrollEventInterval: '@'
|
||||
});
|
||||
|
||||
$scope.$watch($attr.padding, function(newVal) {
|
||||
scrollContent.toggleClass('padding', !!newVal);
|
||||
});
|
||||
if (angular.isDefined($attr.padding)) {
|
||||
$scope.$watch($attr.padding, function(newVal) {
|
||||
scrollContent.toggleClass('padding', !!newVal);
|
||||
});
|
||||
}
|
||||
|
||||
if ($scope.scroll === "false") {
|
||||
//do nothing
|
||||
@@ -111,6 +119,7 @@ function($parse, $timeout, $controller, $ionicBind) {
|
||||
$scope: $scope,
|
||||
scrollViewOptions: {
|
||||
el: $element[0],
|
||||
controllerBind: $attr.controllerBind,
|
||||
bouncing: $scope.$eval($scope.hasBouncing),
|
||||
startX: $scope.$eval($scope.startX) || 0,
|
||||
startY: $scope.$eval($scope.startY) || 0,
|
||||
@@ -128,7 +137,7 @@ function($parse, $timeout, $controller, $ionicBind) {
|
||||
}
|
||||
});
|
||||
//Publish scrollView to parent so children can access it
|
||||
scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
|
||||
scrollView = scrollCtrl.scrollView;
|
||||
}
|
||||
|
||||
transclude($scope, function(clone) {
|
||||
@@ -227,7 +236,7 @@ function($parse, $timeout, $controller, $ionicBind) {
|
||||
$onPulling: '&onPulling'
|
||||
});
|
||||
|
||||
scrollCtrl.setRefresher($scope, $element[0]);
|
||||
scrollCtrl._setRefresher($scope, $element[0]);
|
||||
$scope.$on('scroll.refreshComplete', function() {
|
||||
$element[0].classList.remove('active');
|
||||
scrollCtrl.scrollView.finishPullToRefresh();
|
||||
|
||||
5
js/ext/angular/src/directive/ionicScroll.js
vendored
5
js/ext/angular/src/directive/ionicScroll.js
vendored
@@ -7,11 +7,15 @@ angular.module('ionic.ui.scroll', [])
|
||||
* @ngdoc directive
|
||||
* @name ionScroll
|
||||
* @module ionic
|
||||
* @controller ionicScroll as $scope.$ionicScrollController
|
||||
* @restrict E
|
||||
*
|
||||
* @description
|
||||
* Creates a scrollable container for all content inside.
|
||||
*
|
||||
* @param {string=} controller-bind The scope variable to bind this element's scrollView's
|
||||
* {@link ionic.controller:ionicScroll ionicScroll controller} to.
|
||||
* Default: $scope.$ionicScrollController.
|
||||
* @param {string=} direction Which way to scroll. 'x' or 'y'. Default 'y'.
|
||||
* @param {boolean=} paging Whether to scroll with paging.
|
||||
* @param {expression=} on-refresh Called on pull-to-refresh, triggered by an {@link ionic.directive:ionRefresher}.
|
||||
@@ -61,6 +65,7 @@ angular.module('ionic.ui.scroll', [])
|
||||
|
||||
var scrollViewOptions= {
|
||||
el: $element[0],
|
||||
controllerBind: $attr.controllerBind,
|
||||
paging: isPaging,
|
||||
scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
|
||||
scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
|
||||
|
||||
@@ -94,7 +94,7 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture', 'ionic.service.vie
|
||||
* ```js
|
||||
* function ContentController($scope) {
|
||||
* $scope.toggleLeft = function() {
|
||||
* $scope.sideMenuController.toggleLeft();
|
||||
* $scope.$ionicSideMenusController.toggleLeft();
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
|
||||
3
js/ext/angular/src/directive/ionicTabBar.js
vendored
3
js/ext/angular/src/directive/ionicTabBar.js
vendored
@@ -270,6 +270,9 @@ function($rootScope, $animate, $ionicBind, $compile, $ionicViewService) {
|
||||
tabsCtrl = ctrls[0],
|
||||
tabCtrl = ctrls[1];
|
||||
|
||||
//Remove title attribute so browser-tooltip does not apear
|
||||
$element[0].removeAttribute('title');
|
||||
|
||||
$ionicBind($scope, $attr, {
|
||||
animate: '=',
|
||||
onSelect: '&',
|
||||
|
||||
6
js/ext/angular/src/ionicAngular.js
vendored
6
js/ext/angular/src/ionicAngular.js
vendored
@@ -23,11 +23,6 @@ angular.module('ionic.service', [
|
||||
'ionic.decorator.location'
|
||||
]);
|
||||
|
||||
// UI specific services and delegates
|
||||
angular.module('ionic.ui.service', [
|
||||
'ionic.ui.service.scrollDelegate'
|
||||
]);
|
||||
|
||||
angular.module('ionic.ui', [
|
||||
'ionic.ui.checkbox',
|
||||
'ionic.ui.content',
|
||||
@@ -47,7 +42,6 @@ angular.module('ionic.ui', [
|
||||
|
||||
angular.module('ionic', [
|
||||
'ionic.service',
|
||||
'ionic.ui.service',
|
||||
'ionic.ui',
|
||||
|
||||
// Angular deps
|
||||
|
||||
@@ -1,266 +0,0 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('ionic.ui.service.scrollDelegate', [])
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $ionicScrollDelegate
|
||||
* @module ionic
|
||||
* @description
|
||||
* Allows you to have some control over a scrollable area (created by an
|
||||
* {@link ionic.directive:ionContent} or {@link ionic.directive:ionScroll}
|
||||
* directive).
|
||||
*
|
||||
* Inject it into a controller, create a new instance based upon the current scope,
|
||||
* and its methods will send messages to the nearest scrollView and its children.
|
||||
*
|
||||
* @usage
|
||||
* ```js
|
||||
* function MyController($scope, $ionicScrollDelegate) {
|
||||
* var delegate = $ionicScrollDelegate($scope);
|
||||
* $scope.scrollToTop = function() {
|
||||
* delegate.scrollTop();
|
||||
* };
|
||||
* }
|
||||
* ```
|
||||
* ```html
|
||||
* <ion-content ng-controller="MyController">
|
||||
* <button class="button" ng-click="scrollToTop()">
|
||||
* Scroll To Top
|
||||
* </button>
|
||||
* </ion-content>
|
||||
* ```
|
||||
*/
|
||||
.factory('$ionicScrollDelegate', ['$rootScope', '$timeout', '$location', '$ionicViewService', function($rootScope, $timeout, $location, $ionicViewService) {
|
||||
//Exposed for testing
|
||||
var rememberedScrollValues = ionicScrollDelegate._rememberedScrollValues = {};
|
||||
|
||||
function getScrollCtrl($scope) {
|
||||
var ctrl;
|
||||
while ($scope) {
|
||||
if ( (ctrl = $scope.$$ionicScrollController) ) {
|
||||
return ctrl;
|
||||
}
|
||||
$scope = $scope.$parent;
|
||||
}
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
function ionicScrollDelegate($scope) {
|
||||
var scrollCtrl = getScrollCtrl($scope);
|
||||
var scrollScope = scrollCtrl && scrollCtrl.$scope || $rootScope;
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#scrollTop
|
||||
* @description
|
||||
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
||||
*/
|
||||
scrollTop: function(animate) {
|
||||
scrollScope.$broadcast('scroll.scrollTop', animate);
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#scrollBottom
|
||||
* @description
|
||||
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
||||
*/
|
||||
scrollBottom: function(animate) {
|
||||
scrollScope.$broadcast('scroll.scrollBottom', animate);
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#scroll
|
||||
* @description
|
||||
* @param {number} left The x-value to scroll to.
|
||||
* @param {number} top The y-value to scroll to.
|
||||
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
||||
*/
|
||||
scrollTo: function(left, top, animate) {
|
||||
scrollScope.$broadcast('scroll.scrollTo', left, top, animate);
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#anchorScroll
|
||||
* @description
|
||||
*
|
||||
* Tell the scrollView to scroll to the element with an id
|
||||
* matching window.location.hash.
|
||||
*
|
||||
* If no matching element is found, it will scroll to top.
|
||||
*
|
||||
* @param {boolean=} shouldAnimate Whether the scroll should animate.
|
||||
*/
|
||||
anchorScroll: function(animate) {
|
||||
scrollScope.$broadcast('scroll.anchorScroll', animate);
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#resize
|
||||
* @description
|
||||
*
|
||||
* Tell the scrollView to recalculate the size of its container.
|
||||
*/
|
||||
resize: function() {
|
||||
scrollScope.$broadcast('scroll.resize');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
tapScrollToTop: function(element, animate) {
|
||||
var _this = this;
|
||||
if (!angular.isDefined(animate)) {
|
||||
animate = true;
|
||||
}
|
||||
|
||||
ionic.on('tap', function(e) {
|
||||
var target = e.target;
|
||||
//Don't scroll to top for a button click
|
||||
if (ionic.DomUtil.getParentOrSelfWithClass(target, 'button')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var el = element[0];
|
||||
var bounds = el.getBoundingClientRect();
|
||||
|
||||
if(ionic.DomUtil.rectContains(e.gesture.touches[0].pageX, e.gesture.touches[0].pageY, bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + 20)) {
|
||||
_this.scrollTop(animate);
|
||||
}
|
||||
}, element[0]);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#rememberScrollPosition
|
||||
* @description
|
||||
*
|
||||
* When this scroll area is destroyed, its last scroll position will be
|
||||
* saved using the given id.
|
||||
*
|
||||
* @param {string} id The identifier for this saved scroll position.
|
||||
*/
|
||||
rememberScrollPosition: function(id) {
|
||||
if (!id) {
|
||||
throw new Error("Must supply a unique id!");
|
||||
}
|
||||
scrollScope.$broadcast('scroll.rememberPosition', id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ionicScrollDelegate#scrollToRememberedPosition
|
||||
* @description
|
||||
*
|
||||
* If a scroll position was remembered using the given id, loads the
|
||||
* remembered scroll position and scrolls there.
|
||||
*
|
||||
* @param {string} id The identifier for this saved scroll position.
|
||||
* @param {boolean=} shouldAnimate Whether to animate the scroll.
|
||||
*/
|
||||
scrollToRememberedPosition: function(id, animate) {
|
||||
if (!id) {
|
||||
throw new Error("Must supply a unique id!");
|
||||
}
|
||||
scrollScope.$broadcast('scroll.scrollToRememberedPosition', id, !!animate);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Attempt to get the current scroll view in scope (if any)
|
||||
*
|
||||
* Note: will not work in an isolated scope context.
|
||||
*/
|
||||
getScrollView: function() {
|
||||
return scrollCtrl && scrollCtrl.scrollView;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Register a scope and scroll view for scroll event handling.
|
||||
* $scope {Scope} the scope to register and listen for events
|
||||
*/
|
||||
ionicScrollDelegate.register = function($scope, $element, scrollView) {
|
||||
|
||||
var scrollEl = $element[0];
|
||||
|
||||
function scrollViewResize() {
|
||||
// Run the resize after this digest
|
||||
return $timeout(function() {
|
||||
scrollView.resize();
|
||||
});
|
||||
}
|
||||
|
||||
$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('scroll.resize', scrollViewResize);
|
||||
|
||||
$scope.$on('scroll.anchorScroll', function(e, animate) {
|
||||
scrollViewResize().then(function() {
|
||||
var hash = $location.hash();
|
||||
var elm;
|
||||
if (hash && (elm = document.getElementById(hash)) ) {
|
||||
var scroll = ionic.DomUtil.getPositionInParent(elm, scrollEl);
|
||||
scrollView.scrollTo(scroll.left, scroll.top, !!animate);
|
||||
} else {
|
||||
scrollView.scrollTo(0,0, !!animate);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('scroll.scrollTo', function(e, left, top, animate) {
|
||||
scrollViewResize().then(function() {
|
||||
scrollView.scrollTo(left, top, !!animate);
|
||||
});
|
||||
});
|
||||
$scope.$on('scroll.scrollTop', function(e, animate) {
|
||||
scrollViewResize().then(function() {
|
||||
scrollView.scrollTo(0, 0, !!animate);
|
||||
});
|
||||
});
|
||||
$scope.$on('scroll.scrollBottom', function(e, animate) {
|
||||
scrollViewResize().then(function() {
|
||||
var sv = scrollView;
|
||||
if (sv) {
|
||||
var max = sv.getScrollMax();
|
||||
sv.scrollTo(max.left, max.top, !!animate);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var rememberScrollId;
|
||||
$scope.$on('scroll.rememberPosition', function(e, id) {
|
||||
rememberScrollId = id;
|
||||
});
|
||||
$scope.$on('$destroy', function() {
|
||||
if (rememberScrollId) {
|
||||
rememberedScrollValues[rememberScrollId] = scrollView.getValues();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('scroll.scrollToRememberedPosition', function(e, id, animate) {
|
||||
var values = rememberedScrollValues[id];
|
||||
if (values) {
|
||||
scrollViewResize().then(function() {
|
||||
scrollView.scrollTo(+values.left || null, +values.top || null, animate);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return ionicScrollDelegate;
|
||||
}]);
|
||||
|
||||
})(ionic);
|
||||
8
js/ext/angular/src/service/ionicView.js
vendored
8
js/ext/angular/src/service/ionicView.js
vendored
@@ -159,8 +159,6 @@ angular.module('ionic.service.view', ['ui.router', 'ionic.service.platform'])
|
||||
rsp.viewId = backView.viewId;
|
||||
rsp.navAction = 'moveBack';
|
||||
rsp.viewId = backView.viewId;
|
||||
//when going back, erase scrollValues
|
||||
currentView.rememberedScrollValues = {};
|
||||
if(backView.historyId === currentView.historyId) {
|
||||
// went back in the same history
|
||||
rsp.navDirection = 'back';
|
||||
@@ -234,9 +232,13 @@ angular.module('ionic.service.view', ['ui.router', 'ionic.service.platform'])
|
||||
stateName: this.getCurrentStateName(),
|
||||
stateParams: this.getCurrentStateParams(),
|
||||
url: $location.url(),
|
||||
rememberedScrollValues: null
|
||||
});
|
||||
|
||||
if (rsp.navAction == 'moveBack') {
|
||||
//moveBack(from, to);
|
||||
$rootScope.$emit('$viewHistory.viewBack', currentView.viewId, rsp.viewId);
|
||||
}
|
||||
|
||||
// add the new view to this history's stack
|
||||
hist.stack.push(viewHistory.views[rsp.viewId]);
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function MyCtrl($scope, $location, $ionicScrollDelegate) {
|
||||
var delegate = $ionicScrollDelegate($scope);
|
||||
function MyCtrl($scope, $location) {
|
||||
$scope.items = [];
|
||||
for (var i=0; i < 100; i++) {
|
||||
$scope.items.push({id:i});
|
||||
@@ -44,7 +43,7 @@ function MyCtrl($scope, $location, $ionicScrollDelegate) {
|
||||
|
||||
$scope.doScroll = function() {
|
||||
$location.hash('foo-50');
|
||||
delegate.anchorScroll(true);
|
||||
$scope.$ionicScrollController.anchorScroll(true);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -116,18 +116,13 @@
|
||||
}
|
||||
})
|
||||
|
||||
.controller('TestCtrl', function($scope, $timeout, $ionicScrollDelegate) {
|
||||
var delegate = $ionicScrollDelegate($scope);
|
||||
.controller('TestCtrl', function($scope, $timeout) {
|
||||
console.log('CONSTRUCT');
|
||||
|
||||
$timeout(function() {
|
||||
var view = delegate.getScrollView($scope);
|
||||
console.log(view);
|
||||
});
|
||||
})
|
||||
|
||||
.controller('ThisCtrl', function($scope, $timeout, $ionicScrollDelegate) {
|
||||
var delegate = $ionicScrollDelegate($scope);
|
||||
.controller('ThisCtrl', function($scope, $timeout) {
|
||||
var header = document.getElementById('header');
|
||||
var content = document.getElementById('container');
|
||||
var startTop = header.offsetTop;
|
||||
@@ -139,7 +134,7 @@
|
||||
var last = 0;
|
||||
$scope.onRefresh = function() {
|
||||
$timeout(function() {
|
||||
delegate.finishRefreshing($scope);
|
||||
$scope.$broadcast('scroll.refreshComplete');
|
||||
}, 1000);
|
||||
};
|
||||
$scope.onScroll = function(event, scrollTop, scrollLeft) {
|
||||
|
||||
@@ -10,6 +10,7 @@ describe('$ionicScroll Controller', function() {
|
||||
//scrollView requires an outer container element and a child
|
||||
//content element
|
||||
angular.element('<div><div></div></div>')[0];
|
||||
angular.element('<div>').append(options.el);
|
||||
|
||||
inject(function($controller, $rootScope, $timeout) {
|
||||
scope = $rootScope.$new();
|
||||
@@ -22,15 +23,35 @@ describe('$ionicScroll Controller', function() {
|
||||
});
|
||||
}
|
||||
|
||||
it('should set this.$scope', function() {
|
||||
setup();
|
||||
//Just an arbitrary way of checking that it is indeed a scope
|
||||
expect(typeof ctrl.$scope.$id).toBe('string');
|
||||
it('should set $scope.$ionicScrollController by default', function() {
|
||||
setup()
|
||||
expect(scope.$ionicScrollController).toBe(ctrl);
|
||||
});
|
||||
|
||||
it('should set $scope.$$ionicScrollController', function() {
|
||||
it('should set options.controllerBind to ctrl', function() {
|
||||
setup({
|
||||
controllerBind: 'something'
|
||||
});
|
||||
expect(scope.something).toBe(ctrl);
|
||||
});
|
||||
|
||||
it('should set bounce to !isAndroid after platformReady, if not options.boucing', function() {
|
||||
var isAndroid;
|
||||
spyOn(ionic.Platform, 'isAndroid').andCallFake(function() {
|
||||
return isAndroid;
|
||||
});
|
||||
setup({
|
||||
bouncing: true
|
||||
});
|
||||
expect(ionic.Platform.isAndroid).not.toHaveBeenCalled();
|
||||
|
||||
isAndroid = true;
|
||||
setup();
|
||||
expect(ctrl.$scope.$$ionicScrollController).toBe(ctrl);
|
||||
expect(ctrl.scrollView.options.bouncing).toBe(false);
|
||||
|
||||
isAndroid = false;
|
||||
setup();
|
||||
expect(ctrl.scrollView.options.bouncing).toBe(true);
|
||||
});
|
||||
|
||||
it('should set this.element and this.$element', function() {
|
||||
@@ -46,7 +67,7 @@ describe('$ionicScroll Controller', function() {
|
||||
|
||||
it('should register controller on element.data', function() {
|
||||
setup();
|
||||
expect(ctrl.$element.controller('$ionicScroll')).toBe(ctrl);
|
||||
expect(ctrl.$element.parent().data('$$ionicScrollController')).toBe(ctrl);
|
||||
});
|
||||
|
||||
it('should run after a timeout', function() {
|
||||
@@ -64,24 +85,57 @@ describe('$ionicScroll Controller', function() {
|
||||
});
|
||||
|
||||
it('should remember scroll position on $viewContentLoaded event', function() {
|
||||
var historyData = { rememberedScrollValues: { left: 1, top: 2 } };
|
||||
var historyData = { viewId: '1' };
|
||||
setup();
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
spyOn(ctrl, 'rememberScrollPosition');
|
||||
spyOn(ctrl, 'scrollToRememberedPosition');
|
||||
scope.$broadcast('$viewContentLoaded', historyData);
|
||||
timeout.flush();
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(1, 2);
|
||||
expect(ctrl.rememberScrollPosition).toHaveBeenCalledWith('1');
|
||||
expect(ctrl.scrollToRememberedPosition).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should forget on $viewHistory.viewBack after $viewContentLoaded', inject(function($rootScope) {
|
||||
var historyData = { viewId: 'foo' };
|
||||
setup();
|
||||
spyOn($rootScope, '$on').andCallThrough();;
|
||||
scope.$broadcast('$viewContentLoaded', historyData);
|
||||
expect(scope.$on).toHaveBeenCalledWith('$viewHistory.viewBack', jasmine.any(Function));
|
||||
|
||||
//Should not forget unless backViewId is the same
|
||||
spyOn(ctrl, 'forgetScrollPosition');
|
||||
$rootScope.$broadcast('$viewHistory.viewBack', 'bar');
|
||||
expect(ctrl.forgetScrollPosition).not.toHaveBeenCalled();
|
||||
$rootScope.$broadcast('$viewHistory.viewBack', 'foo');
|
||||
expect(ctrl.forgetScrollPosition).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should not remember scrollValues on $destroy without id', inject(function($$scrollValueCache) {
|
||||
setup();
|
||||
spyOn(ctrl.scrollView, 'getValues');
|
||||
scope.$destroy();
|
||||
expect(ctrl.scrollView.getValues).not.toHaveBeenCalled();
|
||||
expect($$scrollValueCache).toEqual({});
|
||||
}));
|
||||
|
||||
it('should remember scrollValues on $destroy with id', inject(function($$scrollValueCache) {
|
||||
setup();
|
||||
ctrl.rememberScrollPosition('super');
|
||||
spyOn(ctrl.scrollView, 'getValues').andCallFake(function() {
|
||||
return {
|
||||
left: 33,
|
||||
top: 44
|
||||
};
|
||||
return 'scrollValues';
|
||||
});
|
||||
scope.$broadcast('$destroy');
|
||||
expect(historyData.rememberedScrollValues).toEqual({
|
||||
left: 33,
|
||||
top: 44
|
||||
scope.$destroy();
|
||||
expect(ctrl.scrollView.getValues).toHaveBeenCalled();
|
||||
expect($$scrollValueCache).toEqual({
|
||||
'super': 'scrollValues'
|
||||
});
|
||||
}));
|
||||
|
||||
it('rememberScrollPosition should throw without id', function() {
|
||||
setup();
|
||||
expect(function() {
|
||||
ctrl.rememberScrollPosition();
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('should unbind window event listener on scope destroy', function() {
|
||||
@@ -95,12 +149,119 @@ describe('$ionicScroll Controller', function() {
|
||||
expect(window.removeEventListener.mostRecentCall.args[0]).toBe('resize');
|
||||
});
|
||||
|
||||
it('should register with $ionicScrollDelegate', inject(function($ionicScrollDelegate) {
|
||||
spyOn($ionicScrollDelegate, 'register');
|
||||
it('rememberScrollPosition should set id', function() {
|
||||
setup();
|
||||
expect($ionicScrollDelegate.register).toHaveBeenCalledWith(scope, ctrl.$element, ctrl.scrollView);
|
||||
expect(ctrl._rememberScrollId).toBeFalsy();
|
||||
ctrl.rememberScrollPosition('banana');
|
||||
expect(ctrl._rememberScrollId).toBe('banana');
|
||||
});
|
||||
|
||||
it('forgetScrollPosition should remove from cache and unset id', inject(function($$scrollValueCache) {
|
||||
setup();
|
||||
ctrl._rememberScrollId = 'elephant';
|
||||
$$scrollValueCache.elephant = 'stampede';
|
||||
ctrl.forgetScrollPosition();
|
||||
expect(ctrl._rememberScrollId).toBeFalsy();
|
||||
expect($$scrollValueCache.elephant).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should listen to scroll event and call $onScroll', function() {
|
||||
setup();
|
||||
scope.$onScroll = jasmine.createSpy();
|
||||
|
||||
expect(scope.$onScroll).not.toHaveBeenCalled();
|
||||
|
||||
ionic.trigger('scroll', {target: ctrl.element});
|
||||
expect(scope.$onScroll).toHaveBeenCalled();
|
||||
expect(scope.$onScroll.mostRecentCall.args[0].scrollLeft).toBe(0);
|
||||
expect(scope.$onScroll.mostRecentCall.args[0].scrollTop).toBe(0);
|
||||
|
||||
scope.$onScroll.reset();
|
||||
ionic.trigger('scroll', {target: ctrl.element, scrollTop: 3, scrollLeft: 4});
|
||||
expect(scope.$onScroll.mostRecentCall.args[0].scrollLeft).toBe(4);
|
||||
expect(scope.$onScroll.mostRecentCall.args[0].scrollTop).toBe(3);
|
||||
});
|
||||
|
||||
it('.resize() should resize after timeout', inject(function($timeout) {
|
||||
setup();
|
||||
$timeout.flush();
|
||||
spyOn(ctrl.scrollView, 'resize');
|
||||
ctrl.resize();
|
||||
expect(ctrl.scrollView.resize).not.toHaveBeenCalled();
|
||||
$timeout.flush();
|
||||
expect(ctrl.scrollView.resize).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
[false, true].forEach(function(shouldAnimate) {
|
||||
describe('with animate='+shouldAnimate, function() {
|
||||
|
||||
it('scrollToRememberedPosition should work', inject(function($$scrollValueCache) {
|
||||
setup();
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
$$scrollValueCache.foo = { left: 3, top: 4 };
|
||||
ctrl._rememberScrollId = 'foo';
|
||||
expect(ctrl.scrollView.scrollTo).not.toHaveBeenCalled();
|
||||
ctrl.scrollToRememberedPosition(shouldAnimate);
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(3, 4, shouldAnimate);
|
||||
}));
|
||||
|
||||
describe('scroll action', function() {
|
||||
beforeEach(function() {
|
||||
setup();
|
||||
//Mock resize to insta-call through for easier tests
|
||||
ctrl.resize = function() {
|
||||
return { then: function(cb) { cb(); } };
|
||||
};
|
||||
});
|
||||
it('.scrollTop', function() {
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
ctrl.scrollTop(shouldAnimate);
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
||||
});
|
||||
it('.scrollBottom', function() {
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
spyOn(ctrl.scrollView, 'getScrollMax').andCallFake(function() {
|
||||
return { left: 33, top: 44 };
|
||||
});
|
||||
ctrl.scrollBottom(shouldAnimate);
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(33, 44, shouldAnimate);
|
||||
});
|
||||
it('.scrollTo', function() {
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
ctrl.scrollTo(1, 2, shouldAnimate);
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(1, 2, shouldAnimate);
|
||||
});
|
||||
it('.anchorScroll without hash should scrollTop', inject(function($location, $document) {
|
||||
$document[0].getElementById = jasmine.createSpy();
|
||||
$location.hash = function() { return null; };
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
ctrl.anchorScroll(shouldAnimate);
|
||||
expect($document[0].getElementById).not.toHaveBeenCalled();
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
||||
}));
|
||||
it('.anchorScroll with hash but no element should scrollTop', inject(function($location, $document) {
|
||||
$document[0].getElementById = jasmine.createSpy();
|
||||
$location.hash = function() { return 'foo'; };
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
ctrl.anchorScroll(shouldAnimate);
|
||||
expect($document[0].getElementById).toHaveBeenCalledWith('foo');
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(0, 0, shouldAnimate);
|
||||
}));
|
||||
it('.anchorScroll with el matching hash should scroll to it', inject(function($location, $document) {
|
||||
$document[0].getElementById = jasmine.createSpy('byId').andCallFake(function() {
|
||||
return { offsetLeft: 8, offsetTop: 9 };
|
||||
});
|
||||
spyOn($location, 'hash').andCallFake(function() {
|
||||
return 'foo';
|
||||
});
|
||||
spyOn(ctrl.scrollView, 'scrollTo');
|
||||
ctrl.anchorScroll(shouldAnimate);
|
||||
expect(ctrl.scrollView.scrollTo).toHaveBeenCalledWith(8, 9, shouldAnimate);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not activatePullToRefresh if setRefresher is not called', function() {
|
||||
setup();
|
||||
timeout.flush();
|
||||
@@ -119,7 +280,7 @@ describe('$ionicScroll Controller', function() {
|
||||
refreshingCb = refreshing;
|
||||
doneCb = done;
|
||||
});
|
||||
ctrl.setRefresher(scope, ctrl.element);
|
||||
ctrl._setRefresher(scope, ctrl.element);
|
||||
|
||||
var scrollOnRefreshSpy = jasmine.createSpy('scroll.onRefresh');
|
||||
|
||||
|
||||
@@ -1,6 +1,58 @@
|
||||
describe('bar directives', function() {
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
describe('barHeader tapScrollToTop', function() {
|
||||
function setup() {
|
||||
var el;
|
||||
inject(function($compile, $rootScope) {
|
||||
el = angular.element('<div class="bar-header">')
|
||||
el.data('$$ionicScrollController', {
|
||||
scrollTop: jasmine.createSpy('scrollTop')
|
||||
});
|
||||
ionic.requestAnimationFrame = function(cb) { cb(); };
|
||||
$compile(el)($rootScope.$new());
|
||||
$rootScope.$apply();
|
||||
});
|
||||
return el;
|
||||
}
|
||||
it('should listen for tap, unlisten on destroy', function() {
|
||||
var callback;
|
||||
spyOn(ionic, 'on').andCallFake(function(name, cb) {
|
||||
callback = cb;
|
||||
});
|
||||
spyOn(ionic, 'off');
|
||||
var el = setup();
|
||||
expect(ionic.on).toHaveBeenCalledWith('tap', jasmine.any(Function), el[0]);
|
||||
expect(ionic.off).not.toHaveBeenCalled();
|
||||
el.scope().$destroy();
|
||||
expect(ionic.off).toHaveBeenCalledWith('tap', callback, el[0]);
|
||||
});
|
||||
it('should ignore tap if it\'s in a button', function() {
|
||||
var el = setup();
|
||||
spyOn(ionic.DomUtil, 'rectContains');
|
||||
var child = angular.element('<div class="button">');
|
||||
el.append(child);
|
||||
ionic.trigger('tap', { target: child[0] }, true, true);
|
||||
expect(ionic.DomUtil.rectContains).not.toHaveBeenCalled();
|
||||
});
|
||||
it('should scrollTop if tap is inside headerBar', function() {
|
||||
var el = setup();
|
||||
spyOn(ionic.DomUtil, 'rectContains').andCallFake(function() {
|
||||
return true;
|
||||
});
|
||||
ionic.trigger('tap', { target: el[0], touches: [{pageX:0,pageY:0}] });
|
||||
expect(el.controller('$ionicScroll').scrollTop).toHaveBeenCalledWith(true);
|
||||
});
|
||||
it('should not scrollTop if tap isnt inside headerBar', function() {
|
||||
var el = setup();
|
||||
spyOn(ionic.DomUtil, 'rectContains').andCallFake(function() {
|
||||
return false;
|
||||
});
|
||||
ionic.trigger('tap', { target: el[0], touches: [{pageX:0,pageY:0}] });
|
||||
expect(el.controller('$ionicScroll').scrollTop).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
angular.forEach([{
|
||||
tag: 'ion-header-bar',
|
||||
element: 'header',
|
||||
|
||||
@@ -31,44 +31,21 @@ describe('Ionic Content directive', function() {
|
||||
expect(scrollElement.hasClass('padding')).toEqual(false);
|
||||
});
|
||||
|
||||
it('Disables bouncing when has-bouncing = false', function() {
|
||||
ionic.Platform.setPlatform('iPhone');
|
||||
var element = compile('<ion-content has-bouncing="false"></ion-content>')(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');
|
||||
var element = compile('<ion-content></ion-content>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
expect(scrollView.options.bouncing).toBe(false);
|
||||
});
|
||||
|
||||
it('Disables bouncing by default on Android unless', function() {
|
||||
ionic.Platform.setPlatform('Android');
|
||||
var element = compile('<ion-content has-bouncing="true"></ion-content>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
expect(scrollView.options.bouncing).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('Should set start x and y', function() {
|
||||
var element = compile('<ion-content start-x="100" start-y="300"></ion-content>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
var scrollView = scope.$ionicScrollController.scrollView;
|
||||
var vals = scrollView.getValues();
|
||||
expect(vals.left).toBe(100);
|
||||
expect(vals.top).toBe(300);
|
||||
});
|
||||
|
||||
it('should pass attr.controllerBind ionicScrollController', function() {
|
||||
var element = compile('<ion-content controller-bind="scrolly">')(scope);
|
||||
scope.$apply();
|
||||
expect(scope.scrolly).toBe(element.controller('$ionicScroll'));
|
||||
});
|
||||
|
||||
});
|
||||
/* Tests #555 */
|
||||
describe('Ionic Content Directive scoping', function() {
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('ionRefresher directive', function() {
|
||||
inject(function($compile, $rootScope) {
|
||||
var scope = $rootScope.$new();
|
||||
var ionicScrollCtrl = {
|
||||
setRefresher: jasmine.createSpy('setRefresher'),
|
||||
_setRefresher: jasmine.createSpy('setRefresher'),
|
||||
scrollView: {
|
||||
finishPullToRefresh: jasmine.createSpy('finishPullToRefresh')
|
||||
}
|
||||
@@ -50,8 +50,8 @@ describe('ionRefresher directive', function() {
|
||||
|
||||
it('should setRefresher on scrollCtrl', function() {
|
||||
var el = setup();
|
||||
expect(el.controller('$ionicScroll').setRefresher.callCount).toBe(1);
|
||||
expect(el.controller('$ionicScroll').setRefresher).toHaveBeenCalledWith(
|
||||
expect(el.controller('$ionicScroll')._setRefresher.callCount).toBe(1);
|
||||
expect(el.controller('$ionicScroll')._setRefresher).toHaveBeenCalledWith(
|
||||
el.scope(), el[0]
|
||||
)
|
||||
});
|
||||
|
||||
@@ -31,38 +31,16 @@ describe('Ionic Scroll Directive', function() {
|
||||
expect(scrollElement.hasClass('padding')).toEqual(true);
|
||||
});
|
||||
|
||||
// it('Enables bouncing by default', function() {
|
||||
// ionic.Platform.setPlatform('iPhone');
|
||||
// element = compile('<ion-content has-header="true"></ion-scroll>')(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('<ion-content has-header="true" has-bouncing="false"></ion-scroll>')(scope);
|
||||
it('should pass attr.controllerBind ionicScrollController', function() {
|
||||
var element = compile('<ion-scroll controller-bind="scrolly">')(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('<ion-content has-header="true"></ion-scroll>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
expect(scrollView.options.bouncing).toBe(false);
|
||||
expect(scope.scrolly).toBe(element.controller('$ionicScroll'));
|
||||
});
|
||||
|
||||
it('Should set start x and y', function() {
|
||||
element = compile('<ion-content start-x="100" start-y="300" has-header="true"></ion-scroll>')(scope);
|
||||
scope.$apply();
|
||||
var newScope = element.isolateScope();
|
||||
var scrollView = scope.scrollView;
|
||||
var scrollView = scope.$ionicScrollController.scrollView;
|
||||
var vals = scrollView.getValues();
|
||||
expect(vals.left).toBe(100);
|
||||
expect(vals.top).toBe(300);
|
||||
|
||||
@@ -272,7 +272,6 @@ describe('tabs', function() {
|
||||
expect($rootScope.elephant).toBe('banana');
|
||||
}));
|
||||
|
||||
|
||||
it('should add itself to tabsCtrl and remove on $destroy', function() {
|
||||
var el = setup();
|
||||
var tab = tabsCtrl.tabs[0];
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
.controller('ThisCtrl', function($scope, $ionicScrollDelegate) {
|
||||
.controller('ThisCtrl', function($scope) {
|
||||
var header = document.getElementById('header');
|
||||
var content = document.getElementById('container');
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
}
|
||||
$scope.scrollTo = function() {
|
||||
console.log('scrollTo');
|
||||
$ionicScrollDelegate.scrollTo(0, 100, true);
|
||||
$scope.$ionicScrollController.scrollTo(0, 100, true);
|
||||
};
|
||||
$scope.onScroll = function(event, scrollTop, scrollLeft) {
|
||||
/*
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
|
||||
describe('Ionic ScrollDelegate Service', function() {
|
||||
var $ionicScrollDelegate, rootScope, compile, timeout, document;
|
||||
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
beforeEach(inject(function(_$ionicScrollDelegate_, $rootScope, $timeout, $compile, $document) {
|
||||
$ionicScrollDelegate = _$ionicScrollDelegate_;
|
||||
document = $document;
|
||||
rootScope = $rootScope;
|
||||
timeout = $timeout;
|
||||
compile = $compile;
|
||||
}));
|
||||
|
||||
it('should just return rootScope if no scrollCtrl', inject(function($rootScope) {
|
||||
expect(function() {
|
||||
$ionicScrollDelegate($rootScope);
|
||||
}).not.toThrow();
|
||||
expect($ionicScrollDelegate($rootScope).getScrollView()).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('Should get scroll view', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content></ion-content>')(scope);
|
||||
var sv = $ionicScrollDelegate(scope).getScrollView();
|
||||
expect(sv).not.toBe(undefined);
|
||||
});
|
||||
|
||||
it('should resize', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content></ion-content>')(scope);
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
|
||||
var sv = del.getScrollView();
|
||||
spyOn(sv, 'resize');
|
||||
spyOn(sv, 'scrollTo');
|
||||
|
||||
del.resize();
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('scroll event', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content></ion-content>')(scope);
|
||||
scope = el.scope();
|
||||
scope.$apply();
|
||||
var top, left;
|
||||
scope.$onScroll = jasmine.createSpy('scroll').andCallFake(function(data) {
|
||||
top = data.scrollTop;
|
||||
left = data.scrollLeft;
|
||||
});
|
||||
ionic.trigger('scroll', {target: el[0]});
|
||||
expect(scope.$onScroll).toHaveBeenCalled();
|
||||
expect(top).toBe(0);
|
||||
expect(left).toBe(0);
|
||||
|
||||
ionic.trigger('scroll', {target: el[0], scrollTop: 3, scrollLeft: 4});
|
||||
expect(top).toBe(3);
|
||||
expect(left).toBe(4);
|
||||
});
|
||||
|
||||
testWithAnimate(true);
|
||||
testWithAnimate(false);
|
||||
function testWithAnimate(animate) {
|
||||
describe('with animate='+animate, function() {
|
||||
|
||||
it('should tapScrollToTop', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = angular.element('<div>' +
|
||||
'<div class="not-button"></div>' +
|
||||
'<div class="button"></div>' +
|
||||
'</div>');
|
||||
//ionic.trigger() REALLY doesnt want to work with tap,
|
||||
//so we just mock on to catch the callback and use that...
|
||||
var callback;
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
spyOn(ionic, 'on').andCallFake(function(eventName, cb) {
|
||||
callback = cb;
|
||||
});
|
||||
del.tapScrollToTop(el, animate);
|
||||
|
||||
spyOn(del, 'scrollTop');
|
||||
//Don't test the rectContains part, too much to mock
|
||||
spyOn(ionic.DomUtil, 'rectContains').andCallFake(function() {
|
||||
return true;
|
||||
});
|
||||
callback({
|
||||
target: el[0].querySelector('.not-button'),
|
||||
gesture:{ touches:[{}] }
|
||||
});
|
||||
expect(del.scrollTop).toHaveBeenCalledWith(animate);
|
||||
|
||||
del.scrollTop.reset();
|
||||
callback({
|
||||
target: el[0].querySelector('.button')
|
||||
});
|
||||
expect(del.scrollTop).not.toHaveBeenCalled();
|
||||
});
|
||||
it('should resize & scroll top', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content start-y="100"></ion-content>')(scope);
|
||||
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
var sv = del.getScrollView();
|
||||
spyOn(sv, 'resize');
|
||||
spyOn(sv, 'scrollTo');
|
||||
del.scrollTop(animate);
|
||||
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
expect(sv.scrollTo.mostRecentCall.args).toEqual([0, 0, animate]);
|
||||
});
|
||||
|
||||
it('should resize & scroll bottom', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content start-y="100"><br/><br/></ion-content>')(scope);
|
||||
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
var sv = del.getScrollView();
|
||||
spyOn(sv, 'getScrollMax').andCallFake(function() {
|
||||
return { left: 10, top: 11 };
|
||||
});
|
||||
spyOn(sv, 'resize');
|
||||
spyOn(sv, 'scrollTo');
|
||||
var max = sv.getScrollMax();
|
||||
del.scrollBottom(animate);
|
||||
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
expect(sv.scrollTo.mostRecentCall.args).toEqual([max.left, max.top, animate]);
|
||||
});
|
||||
|
||||
it('should resize & scrollTo', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content start-y="100"><br/><br/></ion-content>')(scope);
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
|
||||
var sv = del.getScrollView();
|
||||
spyOn(sv, 'scrollTo');
|
||||
spyOn(sv, 'resize');
|
||||
del.scrollTo(2, 3, animate);
|
||||
|
||||
timeout.flush();
|
||||
expect(sv.resize).toHaveBeenCalled();
|
||||
expect(sv.scrollTo.mostRecentCall.args).toEqual([2, 3, animate]);
|
||||
});
|
||||
|
||||
it('should throw error on rememberScroll if no id', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content></ion-content>');
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
expect(del.rememberScrollPosition).toThrow();
|
||||
});
|
||||
|
||||
it('scrollToRememberedPosition should scroll if exists', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content></ion-content>')(scope);
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
var sv = del.getScrollView();
|
||||
scope.$apply();
|
||||
$ionicScrollDelegate._rememberedScrollValues['1'] = {
|
||||
left: 3,
|
||||
top: 4
|
||||
};
|
||||
del.scrollToRememberedPosition('1', animate);
|
||||
spyOn(sv, 'scrollTo');
|
||||
timeout.flush();
|
||||
expect(sv.scrollTo).toHaveBeenCalledWith(3, 4, animate);
|
||||
});
|
||||
|
||||
it('should save on destroy for rememberScrollPosition', function() {
|
||||
var scope = rootScope.$new();
|
||||
var el = compile('<ion-content></ion-content>')(scope);
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
var sv = del.getScrollView();
|
||||
scope.$apply();
|
||||
$ionicScrollDelegate._rememberedScrollValues['1'] = {
|
||||
left: -1,
|
||||
top: -1
|
||||
};
|
||||
del.rememberScrollPosition('1', animate);
|
||||
spyOn(sv, 'getValues').andCallFake(function() {
|
||||
return { foo: 'bar' };
|
||||
});
|
||||
scope.$destroy();
|
||||
expect(sv.getValues).toHaveBeenCalled();
|
||||
expect($ionicScrollDelegate._rememberedScrollValues['1']).toEqual({
|
||||
foo: 'bar'
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('anchorScroll', function() {
|
||||
function setLocationHash(hash) {
|
||||
inject(function($location) {
|
||||
$location.hash = function() { return hash; };
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(module('ionic'));
|
||||
|
||||
testWithAnimate(true);
|
||||
testWithAnimate(false);
|
||||
|
||||
function testWithAnimate(animate) {
|
||||
describe('with animate=' + animate, function() {
|
||||
var contentEl, scope, $ionicScrollDelegate, timeout;
|
||||
beforeEach(inject(function($rootScope, $compile, $timeout, $document, _$ionicScrollDelegate_) {
|
||||
scope = $rootScope.$new();
|
||||
contentEl = $compile('<ion-content></ion-content>')(scope);
|
||||
|
||||
document.body.appendChild(contentEl[0]);
|
||||
$ionicScrollDelegate = _$ionicScrollDelegate_;
|
||||
timeout = $timeout;
|
||||
$rootScope.$apply();
|
||||
}));
|
||||
|
||||
it('should anchorScroll to an element with id', function() {
|
||||
var anchorMe = angular.element('<div id="anchorMe">');
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
var sv = del.getScrollView();
|
||||
spyOn(sv, 'scrollTo');
|
||||
spyOn(ionic.DomUtil, 'getPositionInParent').andCallFake(function() {
|
||||
return { left: 2, top: 1 };
|
||||
});
|
||||
|
||||
setLocationHash('anchorMe');
|
||||
contentEl.append(anchorMe);
|
||||
|
||||
del.anchorScroll(animate);
|
||||
timeout.flush();
|
||||
expect(sv.scrollTo).toHaveBeenCalledWith(2, 1, animate);
|
||||
});
|
||||
|
||||
it('should anchorScroll to top if !$location.hash()', function() {
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
var sv = del.getScrollView();
|
||||
spyOn(sv, 'scrollTo');
|
||||
spyOn(ionic.DomUtil, 'getPositionInParent');
|
||||
del.anchorScroll(animate);
|
||||
timeout.flush();
|
||||
|
||||
expect(sv.scrollTo).toHaveBeenCalledWith(0, 0, animate);
|
||||
expect(ionic.DomUtil.getPositionInParent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should anchorScroll to top if element with hash id doesnt exist', function() {
|
||||
var del = $ionicScrollDelegate(scope);
|
||||
var sv = del.getScrollView();
|
||||
spyOn(sv, 'scrollTo');
|
||||
spyOn(ionic.DomUtil, 'getPositionInParent');
|
||||
|
||||
setLocationHash('doesnotexist');
|
||||
del.anchorScroll(animate);
|
||||
timeout.flush();
|
||||
|
||||
expect(sv.scrollTo).toHaveBeenCalledWith(0, 0, animate);
|
||||
expect(ionic.DomUtil.getPositionInParent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -141,7 +141,6 @@ describe('Ionic View Service', function() {
|
||||
registerData = viewService.register({});
|
||||
currentView = viewService.getCurrentView();
|
||||
//Set test value for remembered scroll
|
||||
currentView.rememberedScrollValues = 'foo';
|
||||
backView = viewService.getBackView();
|
||||
forwardView = viewService.getForwardView();
|
||||
expect(backView.stateName).toEqual('about');
|
||||
@@ -159,8 +158,6 @@ describe('Ionic View Service', function() {
|
||||
currentView = viewService.getCurrentView();
|
||||
backView = viewService.getBackView();
|
||||
forwardView = viewService.getForwardView();
|
||||
expect(forwardView.rememberedScrollValues).toEqual({});
|
||||
expect(currentView.stateName).toEqual('about');
|
||||
expect(currentView.backViewId).toEqual(backView.viewId);
|
||||
expect(currentView.forwardViewId).toEqual(forwardView.viewId);
|
||||
expect(backView.stateName).toEqual('home');
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<script>
|
||||
angular.module('slideBoxTest', ['ionic'])
|
||||
|
||||
.controller('SlideCtrl', function($scope, $timeout, $ionicSlideBoxDelegate) {
|
||||
.controller('SlideCtrl', function($scope, $timeout) {
|
||||
|
||||
$timeout(function() {
|
||||
$scope.pages = [ {
|
||||
@@ -71,8 +71,6 @@
|
||||
This is a really long page text'
|
||||
}
|
||||
];
|
||||
|
||||
$ionicSlideBoxDelegate.update();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user