diff --git a/.travis.yml b/.travis.yml
index 920910aa13..06d1c852fb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,7 @@ notifications:
rooms:
secure: mkHfRTsuxidtOOORbJJ0Jspb/DSa8jAiQwWWUljqLwefy1p4HGC9P/rLdXXg3vsjiulCzyjEkfvDWAHXvu34GhGWfQuD8U140Fon1Os3AO5Hbme+yRmjXmTcgH8XetSLQufyBBMqXHMd6o1tkxXql1p54G1IShhgAdPNe76d5ZE=
template:
- - '%{repository}: build#%{build_number} (%{duration%}) #{message} (%{branch} - %{commit} : %{author})'
+ - '%{repository}: build#%{build_number} #{message} (%{branch} - %{commit} : %{author})'
format: html
before_install:
diff --git a/config/build.js b/config/build.js
index f666afb762..bfa3694a3d 100644
--- a/config/build.js
+++ b/config/build.js
@@ -37,7 +37,8 @@ module.exports = {
'js/_license.js',
'js/ext/angular/src/ionicAngular.js',
'js/ext/angular/src/service/**/*.js',
- 'js/ext/angular/src/directive/**/*.js'
+ 'js/ext/angular/src/directive/**/*.js',
+ 'js/ext/angular/src/controller/**/*.js'
],
//Which vendor files to include in dist, used by build
//Matched relative to config/lib/
diff --git a/js/ext/angular/src/controller/ionicScrollController.js b/js/ext/angular/src/controller/ionicScrollController.js
new file mode 100644
index 0000000000..59aa59c481
--- /dev/null
+++ b/js/ext/angular/src/controller/ionicScrollController.js
@@ -0,0 +1,44 @@
+(function() {
+'use strict';
+
+angular.module('ionic.ui.scroll')
+
+.controller('$ionicScroll', ['$scope', 'scrollViewOptions', '$timeout',
+ function($scope, scrollViewOptions, $timeout) {
+
+ scrollViewOptions.bouncing = angular.isDefined(scrollViewOptions.bouncing) ?
+ scrollViewOptions.bouncing :
+ !ionic.Platform.isAndroid();
+
+ var element = this.element = scrollViewOptions.el;
+ var refresher = this.refresher = element.querySelector('.scroll-refresher');
+ var scrollView = this.scrollView = new ionic.views.Scroll(scrollViewOptions);
+
+ 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);
+
+ $timeout(function() {
+ scrollView.run();
+
+ // Activate pull-to-refresh
+ if(refresher) {
+ var refresherHeight = refresher.clientHeight || 0;
+ scrollView.activatePullToRefresh(refresherHeight, function() {
+ refresher.classList.add('active');
+ }, function() {
+ refresher.classList.remove('refreshing');
+ refresher.classList.remove('active');
+ }, function() {
+ refresher.classList.add('refreshing');
+ $scope.onRefresh && $scope.onRefresh();
+ $scope.$parent.$broadcast('scroll.onRefresh');
+ });
+ }
+ });
+
+}]);
+
+})();
diff --git a/js/ext/angular/src/directive/ionicContent.js b/js/ext/angular/src/directive/ionicContent.js
index 2e9f4c7b44..24880a62ff 100644
--- a/js/ext/angular/src/directive/ionicContent.js
+++ b/js/ext/angular/src/directive/ionicContent.js
@@ -1,7 +1,7 @@
(function() {
'use strict';
-angular.module('ionic.ui.content', ['ionic.ui.service'])
+angular.module('ionic.ui.content', ['ionic.ui.service', 'ionic.ui.scroll'])
/**
* Panel is a simple 100% width and height, fixed panel. It's meant for content to be
@@ -18,7 +18,7 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
// The content directive is a core scrollable content area
// that is part of many View hierarchies
-.directive('content', ['$parse', '$timeout', '$ionicScrollDelegate', function($parse, $timeout, $ionicScrollDelegate) {
+.directive('content', ['$parse', '$timeout', '$ionicScrollDelegate', '$controller', function($parse, $timeout, $ionicScrollDelegate, $controller) {
return {
restrict: 'E',
replace: true,
@@ -52,46 +52,33 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
if(attr.hasTabs == "true") { element.addClass('has-tabs'); }
if(attr.padding == "true") { element.find('div').addClass('padding'); }
- return function link($scope, $element, $attr, navViewCtrl) {
- var clone, sc, sv,
+ return {
+ //Prelink so it can compile before other directives compile.
+ //Then other directives can require ionicScrollCtrl
+ pre: prelink
+ };
+
+ function prelink($scope, $element, $attr, navViewCtrl) {
+ var clone, sc, scrollView, scrollCtrl,
c = angular.element($element.children()[0]);
if($scope.scroll === "false") {
// No scrolling
return;
- }
-
- if (navViewCtrl) {
- // If we do have a parent navView, wait for them to give us $viewContentLoaded event
- // before we fully initialize
- $scope.$on('$viewContentLoaded', function(e, viewHistoryData) {
- initScroll(viewHistoryData);
- });
- } else {
- // If we are standalone view, just initialize immediately.
- initScroll();
}
- function initScroll(viewHistoryData) {
- viewHistoryData || (viewHistoryData = {});
- var savedScroll = viewHistoryData.scrollValues || {};
+ if(attr.overflowScroll === "true") {
+ $element.addClass('overflow-scroll');
+ return;
+ }
- // If they want plain overflow scrolling, add that as a class
- if(attr.overflowScroll === "true") {
- $element.addClass('overflow-scroll');
- return;
- }
-
- // Otherwise, use our scroll system
- var hasBouncing = $scope.$eval($scope.hasBouncing);
- var enableBouncing = (!ionic.Platform.isAndroid() && hasBouncing !== false) || hasBouncing === true;
- // No bouncing by default for Android users, lest they take up pitchforks
- // to our bouncing goodness
- sv = new ionic.views.Scroll({
+ scrollCtrl = $controller('$ionicScroll', {
+ $scope: $scope,
+ scrollViewOptions: {
el: $element[0],
- bouncing: enableBouncing,
- startX: $scope.$eval($scope.startX) || savedScroll.left || 0,
- startY: $scope.$eval($scope.startY) || savedScroll.top || 0,
+ bouncing: $scope.$eval($scope.hasBouncing),
+ startX: $scope.$eval($scope.startX) || 0,
+ startY: $scope.$eval($scope.startY) || 0,
scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
scrollingX: $scope.$eval($scope.hasScrollX) === true,
@@ -103,54 +90,36 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
scrollLeft: this.__scrollLeft
});
}
- });
+ }
+ });
+ //Publish scrollView to parent so children can access it
+ scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
+
+ $scope.$on('$viewContentLoaded', function(e, viewHistoryData) {
+ viewHistoryData || (viewHistoryData = {});
+ if (viewHistoryData.scrollValues) {
+ scrollView.scrollTo(viewHistoryData.scrollValues);
+ }
//Save scroll onto viewHistoryData when scope is destroyed
$scope.$on('$destroy', function() {
- viewHistoryData.scrollValues = sv.getValues();
+ viewHistoryData.scrollValues = scrollView.getValues();
});
+ });
- var refresher = $element[0].querySelector('.scroll-refresher');
- var refresherHeight = refresher && refresher.clientHeight || 0;
-
- if(attr.refreshComplete) {
- $scope.refreshComplete = function() {
- if($scope.scrollView) {
- refresher && refresher.classList.remove('active');
- $scope.scrollView.finishPullToRefresh();
- $scope.$parent.$broadcast('scroll.onRefreshComplete');
- }
- };
- }
-
- // Activate pull-to-refresh
- if(refresher) {
- sv.activatePullToRefresh(50, function() {
- refresher.classList.add('active');
- }, function() {
- refresher.classList.remove('refreshing');
- refresher.classList.remove('active');
- }, function() {
- refresher.classList.add('refreshing');
- $scope.onRefresh();
- $scope.$parent.$broadcast('scroll.onRefresh');
- });
- }
-
- // Register for scroll delegate event handling
- $ionicScrollDelegate.register($scope, $element);
-
- // Let child scopes access this
- $scope.$parent.scrollView = sv;
-
- $timeout(function() {
- // Give child containers a chance to build and size themselves
- sv.run();
- });
-
- return sv;
+ if(attr.refreshComplete) {
+ $scope.refreshComplete = function() {
+ if($scope.scrollView) {
+ scrollCtrl.refresher && scrollCtrl.refresher.classList.remove('active');
+ scrollView.finishPullToRefresh();
+ $scope.$parent.$broadcast('scroll.onRefreshComplete');
+ }
+ };
}
+ // 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
@@ -163,20 +132,20 @@ angular.module('ionic.ui.content', ['ionic.ui.service'])
if(distance.indexOf('%')) {
// It's a multiplier
maxScroll = function() {
- return sv.getScrollMax().top * ( 1 - parseInt(distance, 10) / 100 );
+ return scrollView.getScrollMax().top * ( 1 - parseInt(distance, 10) / 100 );
};
} else {
// It's a pixel value
maxScroll = function() {
- return sv.getScrollMax().top - parseInt(distance, 10);
+ return scrollView.getScrollMax().top - parseInt(distance, 10);
};
}
$element.bind('scroll', function(e) {
- if( sv && !infiniteStarted && (sv.getValues().top > maxScroll() ) ) {
+ if( scrollView && !infiniteStarted && (scrollView.getValues().top > maxScroll() ) ) {
infiniteStarted = true;
infiniteScroll.addClass('active');
var cb = function() {
- sv.resize();
+ scrollView.resize();
infiniteStarted = false;
infiniteScroll.removeClass('active');
};
diff --git a/js/ext/angular/src/directive/ionicList.js b/js/ext/angular/src/directive/ionicList.js
index a7c8998cf3..548bf9627e 100644
--- a/js/ext/angular/src/directive/ionicList.js
+++ b/js/ext/angular/src/directive/ionicList.js
@@ -37,7 +37,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
link: function($scope, $element, $attr, list) {
if(!list) return;
-
+
var $parentScope = list.scope;
var $parentAttrs = list.attrs;
@@ -54,7 +54,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
$scope.itemClass = $scope.itemType;
- // Decide if this item can do stuff, and follow a certain priority
+ // Decide if this item can do stuff, and follow a certain priority
// depending on where the value comes from
if(($attr.canDelete ? $scope.canDelete : $parentScope.canDelete) !== "false") {
if($attr.onDelete || $parentAttrs.onDelete) {
@@ -100,7 +100,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
restrict: 'E',
replace: true,
transclude: true,
-
+ require: '^?$ionicScroll',
scope: {
itemType: '@',
canDelete: '@',
@@ -122,10 +122,12 @@ angular.module('ionic.ui.list', ['ngAnimate'])
this.attrs = $attrs;
}],
- link: function($scope, $element, $attr) {
+ link: function($scope, $element, $attr, ionicScrollCtrl) {
$scope.listView = new ionic.views.ListView({
el: $element[0],
listEl: $element[0].children[0],
+ scrollEl: ionicScrollCtrl && ionicScrollCtrl.element,
+ scrollView: ionicScrollCtrl && ionicScrollCtrl.scrollView,
onReorder: function(el, oldIndex, newIndex) {
$scope.$apply(function() {
$scope.onReorder({el: el, start: oldIndex, end: newIndex});
diff --git a/js/ext/angular/src/directive/ionicScroll.js b/js/ext/angular/src/directive/ionicScroll.js
index 999683fefa..6c825fe849 100644
--- a/js/ext/angular/src/directive/ionicScroll.js
+++ b/js/ext/angular/src/directive/ionicScroll.js
@@ -3,11 +3,11 @@
angular.module('ionic.ui.scroll', [])
-.directive('scroll', ['$parse', '$timeout', function($parse, $timeout) {
+.directive('scroll', ['$parse', '$timeout', '$controller', function($parse, $timeout, $controller) {
return {
restrict: 'E',
replace: true,
- template: '',
+ template: '',
transclude: true,
scope: {
direction: '@',
@@ -23,8 +23,15 @@ angular.module('ionic.ui.scroll', [])
controller: function() {},
compile: function(element, attr, transclude) {
- return function($scope, $element, $attr) {
- var clone, sv, sc = document.createElement('div');
+
+ return {
+ //Prelink so it can compile before other directives compile.
+ //Then other directives can require ionicScrollCtrl
+ pre: prelink
+ };
+
+ function prelink($scope, $element, $attr) {
+ var scrollView, scrollCtrl, sc = $element[0].children[0];
// Create the internal scroll div
sc.className = 'scroll';
@@ -34,72 +41,46 @@ angular.module('ionic.ui.scroll', [])
if($scope.$eval($scope.paging) === true) {
sc.classList.add('scroll-paging');
}
- $element.append(sc);
-
- // Pass the parent scope down to the child
- clone = transclude($scope.$parent);
- angular.element($element[0].firstElementChild).append(clone);
-
- // Get refresher size
- var refresher = $element[0].querySelector('.scroll-refresher');
- var refresherHeight = refresher && refresher.clientHeight || 0;
if(!$scope.direction) { $scope.direction = 'y'; }
- var hasScrollingX = $scope.direction.indexOf('x') >= 0;
- var hasScrollingY = $scope.direction.indexOf('y') >= 0;
+ var isPaging = $scope.$eval($scope.paging) === true;
- $timeout(function() {
- var options = {
- el: $element[0],
- paging: $scope.$eval($scope.paging) === true,
- scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
- scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
- scrollingX: hasScrollingX,
- scrollingY: hasScrollingY
- };
+ var scrollViewOptions= {
+ el: $element[0],
+ paging: isPaging,
+ scrollbarX: $scope.$eval($scope.scrollbarX) !== false,
+ scrollbarY: $scope.$eval($scope.scrollbarY) !== false,
+ scrollingX: $scope.direction.indexOf('x') >= 0,
+ scrollingY: $scope.direction.indexOf('y') >= 0
+ };
+ if (isPaging) {
+ scrollViewOptions.speedMultiplier = 0.8;
+ scrollViewOptions.bouncing = false;
+ }
- if(options.paging) {
- options.speedMultiplier = 0.8;
- options.bouncing = false;
- }
+ scrollCtrl = $controller('$ionicScroll', {
+ $scope: $scope,
+ scrollViewOptions: scrollViewOptions
+ });
+ scrollView = $scope.$parent.scrollView = scrollCtrl.scrollView;
- sv = new ionic.views.Scroll(options);
-
- // Activate pull-to-refresh
- if(refresher) {
- sv.activatePullToRefresh(refresherHeight, function() {
- refresher.classList.add('active');
- }, function() {
- refresher.classList.remove('refreshing');
- refresher.classList.remove('active');
- }, function() {
- refresher.classList.add('refreshing');
- $scope.onRefresh();
- $scope.$parent.$broadcast('scroll.onRefresh');
- });
- }
-
- $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
- });
+ $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() {
- sv && sv.resize();
- });
+ $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) {
- sv && sv.finishPullToRefresh();
- });
-
- // Let child scopes access this
- $scope.$parent.scrollView = sv;
+ $scope.$parent.$on('scroll.refreshComplete', function(e) {
+ scrollView && scrollView.finishPullToRefresh();
});
};
}
diff --git a/js/ext/angular/test/controller/ionicScrollController.unit.js b/js/ext/angular/test/controller/ionicScrollController.unit.js
new file mode 100644
index 0000000000..67d42c0bbd
--- /dev/null
+++ b/js/ext/angular/test/controller/ionicScrollController.unit.js
@@ -0,0 +1,90 @@
+describe('$ionicScroll Controller', function() {
+
+ beforeEach(module('ionic.ui.scroll'));
+
+ var scope, ctrl, timeout;
+ function setup(options) {
+ options = options || {};
+
+ options.el = options.el || document.createElement('div');
+
+ inject(function($controller, $rootScope, $timeout) {
+ scope = $rootScope.$new();
+ ctrl = $controller('$ionicScroll', {
+ $scope: scope,
+ scrollViewOptions: options
+ });
+ spyOn(ctrl.scrollView, 'run'); // don't actually call run, this is a dumb test
+ timeout = $timeout;
+ });
+ }
+
+ it('should set this.element and this.$element', function() {
+ setup();
+ expect(ctrl.element.tagName).toMatch(/div/i);
+ expect(ctrl.$element[0]).toBe(ctrl.element);
+ });
+
+ it('should register scrollView as this.scrollView', function() {
+ setup();
+ expect(ctrl.scrollView instanceof ionic.views.Scroll).toBe(true);
+ });
+
+ it('should register controller on element.data', function() {
+ setup();
+ expect(ctrl.$element.controller('$ionicScroll')).toBe(ctrl);
+ });
+
+ it('should run after a timeout', function() {
+ setup();
+ timeout.flush();
+ expect(ctrl.scrollView.run).toHaveBeenCalled();
+ });
+
+ it('should not setup if no child .scroll-refresher', function() {
+ setup();
+ expect(ctrl.refresher).toBeFalsy();
+ spyOn(ctrl.scrollView, 'activatePullToRefresh');
+ timeout.flush();
+ expect(ctrl.scrollView.activatePullToRefresh).not.toHaveBeenCalled();
+ });
+
+ it('should work with .scroll-refresher child and proper refresher', function() {
+ var startCb, refreshingCb, doneCb, refresherEl;
+ setup({
+ el: angular.element('')[0]
+ });
+ spyOn(ctrl.scrollView, 'activatePullToRefresh').andCallFake(function(height, start, refreshing, done) {
+ startCb = start;
+ refreshingCb = refreshing;
+ doneCb = done;
+ });
+
+ scope.onRefresh = jasmine.createSpy('onRefresh');
+ scope.$parent.$broadcast = jasmine.createSpy('$broadcast');
+ var refresher = ctrl.refresher;
+
+ timeout.flush();
+
+ expect(refresher.classList.contains('active')).toBe(false);
+ expect(refresher.classList.contains('refreshing')).toBe(false);
+
+ startCb();
+ expect(refresher.classList.contains('active')).toBe(true);
+ expect(refresher.classList.contains('refreshing')).toBe(false);
+
+ refreshingCb();
+ expect(refresher.classList.contains('active')).toBe(false);
+ 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 6ad0f30252..08e23a19c9 100644
--- a/js/ext/angular/test/directive/ionicContent.unit.js
+++ b/js/ext/angular/test/directive/ionicContent.unit.js
@@ -87,23 +87,14 @@ describe('Ionic Content directive', function() {
scope.$apply();
}
- it('should not initialize scroll until $viewContentLoaded if there is a parent view', function() {
- compileWithParent();
- expect(scope.scrollView).toBeUndefined();
- scope.$broadcast('$viewContentLoaded', {});
- expect(scope.scrollView instanceof ionic.views.Scroll).toBe(true);
- });
-
- it('should set start x and y with historyData.scroll passed in through $viewContentLoaded', function() {
+ it('should set x and y with historyData.scrollValues passed in through $viewContentLoaded', function() {
compileWithParent();
+ spyOn(scope.scrollView, 'scrollTo');
+ var scrollValues = { top: 40, left: -20, zoom: 3 };
scope.$broadcast('$viewContentLoaded', {
- scrollValues: { top: 40, left: -20 }
+ scrollValues: scrollValues
});
- timeout.flush();
- var scrollView = scope.scrollView;
- var vals = scrollView.getValues();
- expect(vals.top).toBe(40);
- expect(vals.left).toBe(-20);
+ expect(scope.scrollView.scrollTo).toHaveBeenCalledWith(scrollValues);
});
it('should save scroll on the historyData passed in on $destroy', function() {
diff --git a/js/views/listView.js b/js/views/listView.js
index 4545cb3c3c..761bf849f3 100644
--- a/js/views/listView.js
+++ b/js/views/listView.js
@@ -52,7 +52,7 @@
if(!buttons) {
return;
}
-
+
buttonsWidth = buttons.offsetWidth;
this._currentDrag = {
@@ -111,7 +111,7 @@
// 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
+ // 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)) {
@@ -145,7 +145,7 @@
_this._currentDrag.content.style.webkitTransform = 'translate3d(' + restingPoint + 'px, 0, 0)';
}
_this._currentDrag.content.style.webkitTransition = '';
-
+
// Kill the current drag
_this._currentDrag = null;
@@ -160,10 +160,17 @@
this.dragThresholdY = opts.dragThresholdY || 0;
this.onReorder = opts.onReorder;
this.el = opts.el;
+ this.scrollEl = opts.scrollEl;
+ this.scrollView = opts.scrollView;
};
ReorderDrag.prototype = new DragOp();
+ ReorderDrag.prototype._moveElement = function(e) {
+ var y = (e.gesture.center.pageY - this._currentDrag.elementHeight/2);
+ this.el.style.webkitTransform = 'translate3d(0, '+y+'px, 0)';
+ };
+
ReorderDrag.prototype.start = function(e) {
var content;
@@ -172,20 +179,31 @@
var offsetY = this.el.offsetTop;//parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[1]) || 0;
var startIndex = ionic.DomUtil.getChildIndex(this.el, this.el.nodeName.toLowerCase());
-
+ var elementHeight = this.el.offsetHeight;
var placeholder = this.el.cloneNode(true);
+ // If we have a scroll pane, move our draggable element outside of it
+ // We do this because when we drag our element down below the edge of the page
+ // and scroll the scroll-pane, if the element is *part* of the scroll-pane,
+ // it will scroll 'with' the scroll-pane's contents and change position.
+ var appendToElement = (this.scrollEl || this.el).parentNode;
+
placeholder.classList.add(ITEM_PLACEHOLDER_CLASS);
this.el.parentNode.insertBefore(placeholder, this.el);
-
this.el.classList.add(ITEM_REORDERING_CLASS);
+ appendToElement.parentNode.appendChild(this.el);
+
this._currentDrag = {
- startOffsetTop: offsetY,
+ elementHeight: elementHeight,
startIndex: startIndex,
- placeholder: placeholder
+ placeholder: placeholder,
+ scrollHeight: scroll,
+ list: placeholder.parentNode
};
+
+ this._moveElement(e);
};
ReorderDrag.prototype.drag = function(e) {
@@ -197,6 +215,29 @@
return;
}
+ var scrollY = 0;
+ var pageY = e.gesture.center.pageY;
+
+ //If we have a scrollView, check scroll boundaries for dragged element and scroll if necessary
+ if (_this.scrollView) {
+ var container = _this.scrollEl;
+
+ scrollY = _this.scrollView.getValues().top;
+
+ var containerTop = container.offsetTop;
+ var pixelsPastTop = containerTop - pageY + _this._currentDrag.elementHeight/2;
+ var pixelsPastBottom = pageY + _this._currentDrag.elementHeight/2 - containerTop - container.offsetHeight;
+
+ if (e.gesture.deltaY < 0 && pixelsPastTop > 0 && scrollY > 0) {
+ _this.scrollView.scrollBy(null, -pixelsPastTop);
+ }
+ if (e.gesture.deltaY > 0 && pixelsPastBottom > 0) {
+ if (scrollY < _this.scrollView.getScrollMax().top) {
+ _this.scrollView.scrollBy(null, pixelsPastBottom);
+ }
+ }
+ }
+
// 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) {
@@ -204,11 +245,9 @@
}
if(_this._isDragging) {
- var newY = _this._currentDrag.startOffsetTop + e.gesture.deltaY;
-
- _this.el.style.top = newY + 'px';
+ _this._moveElement(e);
- _this._currentDrag.currentY = newY;
+ _this._currentDrag.currentY = scrollY + pageY - _this._currentDrag.placeholder.parentNode.offsetTop;
_this._reorderItems();
}
@@ -219,9 +258,6 @@
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)];
@@ -244,12 +280,12 @@
}
var placeholder = this._currentDrag.placeholder;
+ var finalPosition = ionic.DomUtil.getChildIndex(placeholder, placeholder.nodeName.toLowerCase());
// Reposition the element
this.el.classList.remove(ITEM_REORDERING_CLASS);
- this.el.style.top = 0;
+ this.el.style.webkitTransform = '';
- var finalPosition = ionic.DomUtil.getChildIndex(placeholder, placeholder.nodeName.toLowerCase());
placeholder.parentNode.insertBefore(this.el, placeholder);
placeholder.parentNode.removeChild(placeholder);
@@ -294,7 +330,7 @@
window.ionic.onGesture('release', function(e) {
_this._handleEndDrag(e);
}, this.el);
-
+
window.ionic.onGesture('drag', function(e) {
_this._handleDrag(e);
}, this.el);
@@ -399,6 +435,8 @@
if(item) {
this._dragOp = new ReorderDrag({
el: item,
+ scrollEl: this.scrollEl,
+ scrollView: this.scrollView,
onReorder: function(el, start, end) {
_this.onReorder && _this.onReorder(el, start, end);
}
@@ -424,7 +462,7 @@
_handleEndDrag: function(e) {
var _this = this;
-
+
if(!this._dragOp) {
//ionic.views.ListView.__super__._handleEndDrag.call(this, e);
return;
@@ -447,7 +485,7 @@
*/
_handleDrag: function(e) {
var _this = this, content, buttons;
-
+
// If the user has a touch timeout to highlight an element, clear it if we
// get sufficient draggage
if(Math.abs(e.gesture.deltaX) > 10 || Math.abs(e.gesture.deltaY) > 10) {
@@ -462,7 +500,7 @@
}
// No drag still, pass it up
- if(!this._dragOp) {
+ if(!this._dragOp) {
//ionic.views.ListView.__super__._handleDrag.call(this, e);
return;
}
diff --git a/scss/_variables.scss b/scss/_variables.scss
index 952ec64b86..1955333040 100644
--- a/scss/_variables.scss
+++ b/scss/_variables.scss
@@ -559,7 +559,7 @@ $z-index-item-drag: 0;
$z-index-item-edit: 0;
$z-index-item-options: 1;
$z-index-item-radio: 3;
-$z-index-item-reordering: 20;
+$z-index-item-reordering: 9;
$z-index-item-toggle: 3;
$z-index-menu: 0;
$z-index-modal: 10;
diff --git a/test/js/views/listView.unit.js b/test/js/views/listView.unit.js
index 53abdb831a..998d2bb27a 100644
--- a/test/js/views/listView.unit.js
+++ b/test/js/views/listView.unit.js
@@ -37,7 +37,8 @@ describe('List View', function() {
expect(list.itemHeight).toEqual(50);
});
- xit('Should support virtual scrolling', function() {
+ /*
+ it('Should support virtual scrolling', function() {
var list = new ionic.views.ListView({
el: h,
listEl: listEl,
@@ -70,5 +71,6 @@ describe('List View', function() {
expect(list.renderViewport).toHaveBeenCalledWith(scrollTop + list.virtualRemoveThreshold,
scrollTop + viewportHeight + list.virtualAddThreshold, start, end);
});
+ */
});