diff --git a/dist/css/ionic.css b/dist/css/ionic.css
index 722974e528..139675c54b 100644
--- a/dist/css/ionic.css
+++ b/dist/css/ionic.css
@@ -2282,8 +2282,37 @@ body, .ionic-body {
text-align: center;
position: relative; }
+.ionic-refresher-content {
+ height: 100%;
+ width: 100%;
+ text-align: center;
+ position: relative; }
+
+.ionic-refresher {
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ background-color: #4a87ee;
+ position: absolute;
+ left: 50%;
+ margin-left: -5px;
+ bottom: 25px; }
+
.scroll-refreshing {
-webkit-transition: height 0.1s ease-in-out; }
+ .scroll-refreshing .ionic-refresher {
+ -webkit-animation: refresher-pulsate 1.5s linear;
+ -webkit-animation-iteration-count: infinite; }
+
+@-webkit-keyframes refresher-pulsate {
+ 0% {
+ -webkit-transform: scale(2, 2); }
+
+ 50% {
+ -webkit-transform: scale(1.5, 1.5); }
+
+ 100% {
+ -webkit-transform: scale(2, 2); } }
.overflow-scroll {
overflow: auto;
@@ -4936,7 +4965,7 @@ a.button {
@-webkit-keyframes slideOutRight {
0% {
- -webkit-transform: translate3d(0%, 0, 0); }
+ -webkit-transform: translate3d(0, 0, 0); }
100% {
-webkit-transform: translate3d(100%, 0, 0); } }
@@ -4975,12 +5004,12 @@ a.button {
-webkit-animation-name: slideOutRight; }
.slide-out-right.ng-enter, .slide-out-right > .ng-enter {
- -webkit-animation-duration: 2250ms;
+ -webkit-animation-duration: 250ms;
-webkit-animation-fill-mode: both;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-name: slideOutRight; }
.slide-out-right.ng-leave, .slide-out-right > .ng-leave {
- -webkit-animation-duration: 2250ms;
+ -webkit-animation-duration: 250ms;
-webkit-animation-fill-mode: both;
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-name: slideInRight; }
diff --git a/dist/js/ionic-angular.js b/dist/js/ionic-angular.js
index 11199ec5a0..9b83827c3f 100644
--- a/dist/js/ionic-angular.js
+++ b/dist/js/ionic-angular.js
@@ -548,11 +548,39 @@ angular.module('ionic.ui.content', [])
return {
restrict: 'E',
replace: true,
- transclude: true,
- template: '
'
+ require: ['^?content', '^?list'],
+ template: '',
+ scope: true,
+ link: function($scope, $element, $attr, scrollCtrl) {
+ var icon = $element[0].querySelector('.ionic-refresher');
+
+ // Scale up the refreshing icon
+ var onRefreshOpening = ionic.throttle(function(e, amt) {
+ icon.style[ionic.CSS.TRANSFORM] = 'scale(' + Math.min((1 + amt), 2) + ')';
+ }, 100);
+
+ $scope.$on('onRefreshing', function(e) {
+ icon.style[ionic.CSS.TRANSFORM] = 'scale(2)';
+ });
+
+ $scope.$on('onRefresh', function(e) {
+ icon.style[ionic.CSS.TRANSFORM] = 'scale(1)';
+ });
+ $scope.$on('onRefreshOpening', onRefreshOpening);
+ }
}
})
+.directive('scroll-refresher', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ template: ''
+ }
+});
+
+
})();
;
(function() {
@@ -560,10 +588,10 @@ angular.module('ionic.ui.content', [])
angular.module('ionic.ui.list', ['ngAnimate'])
-.directive('listItem', ['$timeout', function($timeout) {
+.directive('item', ['$timeout', function($timeout) {
return {
restrict: 'E',
- require: ['?^list', '?^virtualList'],
+ require: ['?^list'],
replace: true,
transclude: true,
scope: {
@@ -574,8 +602,9 @@ angular.module('ionic.ui.list', ['ngAnimate'])
canReorder: '@',
canSwipe: '@',
buttons: '=',
+ type: '@'
},
- template: '\
+ template: '\
\
\
\
@@ -589,20 +618,6 @@ angular.module('ionic.ui.list', ['ngAnimate'])
\
',
- /*
- template: '\
- \
- \
-
\
- \
-
\
- \
- \
-
\
- \
- \
-
\
- ',*/
link: function($scope, $element, $attr, list) {
// Grab the parent list controller
if(list[0]) {
@@ -611,6 +626,13 @@ angular.module('ionic.ui.list', ['ngAnimate'])
list = list[1];
}
+ // Add the list item type class
+ $element.addClass($attr.type || 'item-slider');
+
+ if($attr.type !== 'item-slider') {
+ $scope.canSwipe = false;
+ }
+
$scope.isEditing = false;
$scope.deleteIcon = list.scope.deleteIcon;
$scope.reorderIcon = list.scope.reorderIcon;
@@ -647,6 +669,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
isEditing: '=',
deleteIcon: '@',
reorderIcon: '@',
+ hasPullToRefresh: '@',
onRefresh: '&',
onRefreshOpening: '&'
},
@@ -669,12 +692,14 @@ angular.module('ionic.ui.list', ['ngAnimate'])
var lv = new ionic.views.ListView({
el: $element[0],
listEl: $element[0].children[0],
- hasPullToRefresh: (typeof $scope.onRefresh !== 'undefined'),
+ hasPullToRefresh: ($scope.hasPullToRefresh !== 'false'),
onRefresh: function() {
$scope.onRefresh();
+ $scope.$parent.$broadcast('onRefresh');
},
onRefreshOpening: function(amt) {
$scope.onRefreshOpening({amount: amt});
+ $scope.$parent.$broadcast('onRefreshOpening', amt);
}
});
@@ -723,6 +748,31 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
angular.extend(this, ionic.controllers.NavController.prototype);
+ /**
+ * Push a template onto the navigation stack.
+ * @param {string} templateUrl the URL of the template to load.
+ */
+ this.pushFromTemplate = ionic.debounce(function(templateUrl) {
+ var childScope = $scope.$new();
+ childScope.isVisible = true;
+
+ // Load the given template
+ TemplateLoader.load(templateUrl).then(function(templateString) {
+
+ // Compile the template with the new scrope, and append it to the navigation's content area
+ var el = $compile(templateString)(childScope, function(cloned, scope) {
+ var content = angular.element($element[0].querySelector('.content, .scroll'));
+ $animate.enter(cloned, angular.element(content));
+ });
+ });
+ }, 300, true);
+
+ // Pop function, debounced
+ this.popController = ionic.debounce(function() {
+ _this.pop();
+ }, 300, true);
+
+
ionic.controllers.NavController.call(this, {
content: {
},
@@ -746,7 +796,7 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
// Support Android hardware back button (native only, not mobile web)
var onHardwareBackButton = function(e) {
$scope.$apply(function() {
- _this.pop();
+ _this.popController();
});
}
Platform.onHardwareBackButton(onHardwareBackButton);
@@ -759,28 +809,6 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
this.endDrag = function(e) {
};
- /**
- * Push a template onto the navigation stack.
- * @param {string} templateUrl the URL of the template to load.
- */
- this.pushFromTemplate = function(templateUrl) {
- var childScope = $scope.$new();
- childScope.isVisible = true;
-
- // Load the given template
- TemplateLoader.load(templateUrl).then(function(templateString) {
-
- // Compile the template with the new scrope, and append it to the navigation's content area
- var el = $compile(templateString)(childScope, function(cloned, scope) {
- var content = angular.element($element[0].querySelector('.content'));
-
- //content.append(cloned);
- //angular.element(content).append(cloned);
- $animate.enter(cloned, angular.element(content));
- });
- });
- };
-
/**
* Push a controller to the stack. This is called by the child
* nav-content directive when it is linked to a scope on the page.
@@ -827,7 +855,7 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
scope.$watch('navController.controllers.length', function(value) {
});
scope.goBack = function() {
- navCtrl.pop();
+ navCtrl.popController();
};
}
};
@@ -884,7 +912,7 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
// Store that we should go forwards on the animation. This toggles
// based on the visibility sequence (to support reverse transitions)
- var wasVisible = null;
+ var lastDirection = null;
$scope.title = $attr.title;
$scope.pushAnimation = $attr.pushAnimation || 'slide-in-left';
@@ -900,6 +928,24 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
navCtrl.showNavBar();
}
+ $scope.visibilityChanged = function(direction) {
+ lastDirection = direction;
+
+ if(!childElement) {
+ return;
+ }
+
+ var clone = childElement;
+
+ if(direction == 'push') {
+ clone.addClass(childScope.pushAnimation);
+ clone.removeClass(childScope.popAnimation);
+ } else if(direction == 'pop') {
+ clone.addClass(childScope.popAnimation);
+ clone.removeClass(childScope.pushAnimation);
+ }
+ };
+
// Push this controller onto the stack
$scope.pushController($scope, $element);
@@ -911,39 +957,33 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
transclude(childScope, function(clone) {
childElement = clone;
- // Check if this is visible, and if so, create it and show it
- if(wasVisible === false) {
- clone.removeClass(childScope.pushAnimation);
- clone.addClass(childScope.popAnimation);
- } else {
+ if(lastDirection == 'push') {
clone.addClass(childScope.pushAnimation);
- clone.removeClass(childScope.popAnimation);
+ } else if(lastDirection == 'pop') {
+ clone.addClass(childScope.popAnimation);
}
- $animate.enter(clone, $element.parent(), $element);
- wasVisible = true;
+ $animate.enter(clone, $element.parent(), $element, function() {
+ clone.removeClass(childScope.pushAnimation);
+ clone.removeClass(childScope.popAnimation);
+ });
});
} else {
// Taken from ngIf
if(childElement) {
- var clone = childElement;
// Check if this is visible, and if so, create it and show it
- if(wasVisible === false) {
- clone.removeClass(childScope.pushAnimation);
- clone.addClass(childScope.popAnimation);
- } else {
- clone.addClass(childScope.pushAnimation);
- clone.removeClass(childScope.popAnimation);
- }
- $animate.leave(childElement);
+ $animate.leave(childElement, function() {
+ if(childScope) {
+ childElement.removeClass(childScope.pushAnimation);
+ childElement.removeClass(childScope.popAnimation);
+ }
+ });
childElement = undefined;
- wasVisible = false;
}
if(childScope) {
childScope.$destroy();
childScope = undefined;
}
-
}
});
}
@@ -975,27 +1015,18 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
angular.extend(this, ionic.controllers.SideMenuController.prototype);
ionic.controllers.SideMenuController.call(this, {
+ // Our quick implementation of the left side menu
left: {
width: 270,
- pushDown: function() {
- $scope.leftZIndex = -1;
- },
- bringUp: function() {
- $scope.leftZIndex = 0;
- }
},
+
+ // Our quick implementation of the right side menu
right: {
width: 270,
- pushDown: function() {
- $scope.rightZIndex = -1;
- },
- bringUp: function() {
- $scope.rightZIndex = 0;
- }
}
});
- $scope.contentTranslateX = 0;
+ $scope.sideMenuContentTranslateX = 0;
$scope.sideMenuCtrl = this;
})
@@ -1027,28 +1058,32 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
defaultPrevented = e.defaultPrevented;
});
- Gesture.on('drag', function(e) {
+ var dragFn = function(e) {
if(defaultPrevented) {
return;
}
sideMenuCtrl._handleDrag(e);
- }, $element[0]);
+ };
- Gesture.on('release', function(e) {
+ Gesture.on('drag', dragFn, $element[0]);
+
+ var dragReleaseFn = function(e) {
if(!defaultPrevented) {
sideMenuCtrl._endDrag(e);
}
defaultPrevented = false;
- }, $element[0]);
+ };
+
+ Gesture.on('release', dragReleaseFn, $element[0]);
sideMenuCtrl.setContent({
onDrag: function(e) {},
endDrag: function(e) {},
getTranslateX: function() {
- return $scope.contentTranslateX || 0;
+ return $scope.sideMenuContentTranslateX || 0;
},
setTranslateX: function(amount) {
- $scope.contentTranslateX = amount;
+ $scope.sideMenuContentTranslateX = amount;
$element[0].style.webkitTransform = 'translate3d(' + amount + 'px, 0, 0)';
},
enableAnimation: function() {
@@ -1062,6 +1097,12 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
$element[0].classList.remove('menu-animated');
}
});
+
+ // Cleanup
+ $scope.$on('$destroy', function() {
+ Gesture.off('drag', dragFn);
+ Gesture.off('release', dragReleaseFn);
+ });
};
}
};
@@ -1080,10 +1121,23 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
return function($scope, $element, $attr, sideMenuCtrl) {
$scope.side = $attr.side;
+
if($scope.side == 'left') {
sideMenuCtrl.left.isEnabled = true;
+ sideMenuCtrl.left.pushDown = function() {
+ $element[0].style.zIndex = -1;
+ };
+ sideMenuCtrl.left.bringUp = function() {
+ $element[0].style.zIndex = 0;
+ };
} else if($scope.side == 'right') {
sideMenuCtrl.right.isEnabled = true;
+ sideMenuCtrl.right.pushDown = function() {
+ $element[0].style.zIndex = -1;
+ };
+ sideMenuCtrl.right.bringUp = function() {
+ $element[0].style.zIndex = 0;
+ };
}
$element.append(transclude($scope));
diff --git a/dist/js/ionic.js b/dist/js/ionic.js
index b12d4a9664..f5067571ea 100644
--- a/dist/js/ionic.js
+++ b/dist/js/ionic.js
@@ -8,6 +8,8 @@ http://ionicframework.com/
By @maxlynch, @helloimben, @adamdbradley <3
Licensed under the MIT license. Please see LICENSE for more information.
+
+Make awesome shit.
*/
;
@@ -1835,7 +1837,58 @@ window.ionic = {
;
(function(ionic) {
+ /**
+ * Various utilities used throughout Ionic
+ *
+ * Some of these are adopted from underscore.js and backbone.js, both also MIT licensed.
+ */
ionic.Utils = {
+
+ /**
+ * Return a function that will be called with the given context
+ */
+ proxy: function(func, context) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ return func.apply(context, args.concat(Array.prototype.slice.call(arguments)));
+ };
+ },
+
+ /**
+ * Only call a function once in the given interval.
+ *
+ * @param func {Function} the function to call
+ * @param wait {int} how long to wait before/after to allow function calls
+ * @param immediate {boolean} whether to call immediately or after the wait interval
+ */
+ debounce: function(func, wait, immediate) {
+ var timeout, args, context, timestamp, result;
+ return function() {
+ context = this;
+ args = arguments;
+ timestamp = new Date();
+ var later = function() {
+ var last = (new Date()) - timestamp;
+ if (last < wait) {
+ timeout = setTimeout(later, wait - last);
+ } else {
+ timeout = null;
+ if (!immediate) result = func.apply(context, args);
+ }
+ };
+ var callNow = immediate && !timeout;
+ if (!timeout) {
+ timeout = setTimeout(later, wait);
+ }
+ if (callNow) result = func.apply(context, args);
+ return result;
+ };
+ },
+
+ /**
+ * Throttle the given fun, only allowing it to be
+ * called at most every `wait` ms.
+ */
throttle: function(func, wait, options) {
var context, args, result;
var timeout = null;
@@ -1915,9 +1968,12 @@ window.ionic = {
}
};
+ // Bind a few of the most useful functions to the ionic scope
ionic.inherit = ionic.Utils.inherit;
ionic.extend = ionic.Utils.extend;
ionic.throttle = ionic.Utils.throttle;
+ ionic.proxy = ionic.Utils.proxy;
+ ionic.debounce = ionic.Utils.debounce;
})(window.ionic);
;
@@ -1977,7 +2033,7 @@ window.ionic = {
dragThreshold: 10,
// Resistance when scrolling too far up or down
- rubberBandResistance: 3,
+ rubberBandResistance: 2,
// Scroll event names. These are custom so can be configured
scrollEventName: 'momentumScrolled',
@@ -1985,6 +2041,10 @@ window.ionic = {
hasPullToRefresh: true,
+ // Whether to disable overflow rubber banding when content is small
+ // enough to fit in the viewport (i.e. doesn't need scrolling)
+ disableNonOverflowRubberBand: false,
+
// Called as the refresher is opened, an amount is passed
onRefreshOpening: function() {},
// Called when let go and is refreshing
@@ -2178,6 +2238,10 @@ window.ionic = {
}
},
+ /**
+ * Check if the current scroll bounds needs to be brought back to the min/max
+ * allowable given the total scrollable area.
+ */
needsWrapping: function() {
var _this = this;
@@ -2400,14 +2464,6 @@ window.ionic = {
var parentWidth = this.el.parentNode.offsetWidth;
var parentHeight = this.el.parentNode.offsetHeight;
- var maxX = Math.min(0, (-totalWidth + parentWidth));
- var maxY = Math.min(0, (-totalHeight + parentHeight));
-
- // Check if we even have enough content to scroll, if not, don't start the drag
- if((this.isHorizontalEnabled && maxX == 0) || (this.isVerticalEnabled && maxY == 0)) {
- return;
- }
-
this.x = scrollLeft;
this.y = scrollTop;
@@ -2444,6 +2500,16 @@ window.ionic = {
resist: 1,
startTime: Date.now()
};
+
+ if(this.disableNonOverflowRubberBand === true) {
+ var maxX = Math.min(0, (-totalWidth + parentWidth));
+ var maxY = Math.min(0, (-totalHeight + parentHeight));
+
+ // Check if we even have enough content to scroll, if not, don't start the drag
+ if((this.isHorizontalEnabled && maxX == 0) || (this.isVerticalEnabled && maxY == 0)) {
+ this._drag.noRubberBand = true;
+ }
+ }
},
/**
@@ -2511,9 +2577,22 @@ window.ionic = {
var newX = _this.x + deltaX;
var newY = _this.y + deltaY;
- // Check if the dragging is beyond the bottom or top
- if(newY > 0 || (-newY + parentHeight) > totalHeight) {
- newY = _this.y + deltaY / _this.rubberBandResistance;
+ if(drag.noRubberBand === true) {
+ if(newY > 0) {
+ newY = 0;
+ } else if(newY < maxY) {
+ newY = maxY;
+ }
+ if(newX > 0) {
+ newX = 0;
+ } else if(newX < maxX) {
+ newX = maxX;
+ }
+ } else {
+ // Check if the dragging is beyond the bottom or top
+ if(newY > 0 || (-newY + parentHeight) > totalHeight) {
+ newY = _this.y + deltaY / _this.rubberBandResistance;
+ }
}
if(!_this.isHorizontalEnabled) {
@@ -2525,7 +2604,6 @@ window.ionic = {
if(_this._refresher && newY > 0) {
// We are pulling to refresh, so update the refresher
- //_this._refresher.style[ionic.CSS.TRANSFORM] = 'translate3d(0, ' + newY + 'px, 0)';
if(_this._isRefresherHidden) {
// Show it only in a drag and if we haven't showed it yet
_this._refresher.style.display = 'block';
@@ -2542,7 +2620,7 @@ window.ionic = {
}
// Update the new translated Y point of the container
- _this.el.style.webkitTransform = 'translate3d(' + newX + 'px,' + newY + 'px, 0)';
+ _this.el.style[ionic.CSS.TRANSFORM] = 'translate3d(' + newX + 'px,' + newY + 'px, 0)';
} else {
_this._isHoldingRefresh = false;
@@ -2553,7 +2631,7 @@ window.ionic = {
_this._isRefresherHidden = true;
}
// Update the new translated Y point of the container
- _this.el.style.webkitTransform = 'translate3d(' + newX + 'px,' + newY + 'px, 0)';
+ _this.el.style[ionic.CSS.TRANSFORM] = 'translate3d(' + newX + 'px,' + newY + 'px, 0)';
}
// Store the last points
@@ -3165,8 +3243,6 @@ window.ionic = {
this._isDragging = false;
- console.log(e.gesture.direction);
-
// Check if this is a reorder drag
if(ionic.DomUtil.getParentOrSelfWithClass(e.target, ITEM_DRAG_CLASS) && (e.gesture.direction == 'up' || e.gesture.direction == 'down')) {
var item = this._getItem(e.target);
@@ -3467,6 +3543,39 @@ window.ionic = {
}
});
+ ionic.views.SideMenuContent = ionic.views.View.inherit({
+ initialize: function(opts) {
+ var _this = this;
+
+ ionic.extend(this, {
+ animationClass: 'menu-animated',
+ onDrag: function(e) {},
+ onEndDrag: function(e) {},
+ }, opts);
+
+ ionic.onGesture('drag', ionic.proxy(this._onDrag, this), this.el);
+ ionic.onGesture('release', ionic.proxy(this._onEndDrag, this), this.el);
+ },
+ _onDrag: function(e) {
+ this.onDrag && this.onDrag(e);
+ },
+ _onEndDrag: function(e) {
+ this.onEndDrag && this.onEndDrag(e);
+ },
+ disableAnimation: function() {
+ this.el.classList.remove(this.animationClass);
+ },
+ enableAnimation: function() {
+ this.el.classList.add(this.animationClass);
+ },
+ getTranslateX: function() {
+ return parseFloat(this.el.style.webkitTransform.replace('translate3d(', '').split(',')[0]);
+ },
+ setTranslateX: function(x) {
+ this.el.style.webkitTransform = 'translate3d(' + x + 'px, 0, 0)';
+ }
+ });
+
})(ionic);
;
/**
@@ -4142,21 +4251,17 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
return;
// Actually switch the active controllers
-
- // Remove the old one
- //last && last.detach();
if(last) {
last.isVisible = false;
- last.visibilityChanged && last.visibilityChanged();
+ last.visibilityChanged && last.visibilityChanged('push');
}
// Grab the top controller on the stack
var next = this.controllers[this.controllers.length - 1];
- // TODO: No DOM stuff here
- //this.content.el.appendChild(next.el);
next.isVisible = true;
- next.visibilityChanged && next.visibilityChanged();
+ // Trigger visibility change, but send 'first' if this is the first page
+ next.visibilityChanged && next.visibilityChanged(last ? 'push' : 'first');
this._updateNavBar();
@@ -4181,7 +4286,7 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
last = this.controllers.pop();
if(last) {
last.isVisible = false;
- last.visibilityChanged && last.visibilityChanged();
+ last.visibilityChanged && last.visibilityChanged('pop');
}
// Remove the old one
@@ -4192,7 +4297,7 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
// TODO: No DOM stuff here
//this.content.el.appendChild(next.el);
next.isVisible = true;
- next.visibilityChanged && next.visibilityChanged();
+ next.visibilityChanged && next.visibilityChanged('pop');
// Switch to it (TODO: Animate or such things here)
@@ -4264,7 +4369,7 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
self._handleDrag(e);
};
- this.content.endDrag = function(e) {
+ this.content.onEndDrag =function(e) {
self._endDrag(e);
};
}
@@ -4354,12 +4459,12 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
*/
openPercentage: function(percentage) {
var p = percentage / 100;
- var maxLeft = this.left.width;
- var maxRight = this.right.width;
- if(percentage >= 0) {
- this.openAmount(maxLeft * p);
- } else {
- this.openAmount(maxRight * p);
+
+ if(this.left && percentage >= 0) {
+ this.openAmount(this.left.width * p);
+ } else if(this.right && percentage < 0) {
+ var maxRight = this.right.width;
+ this.openAmount(this.right.width * p);
}
},
@@ -4369,11 +4474,11 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
* negative value for right menu (only one menu will be visible at a time).
*/
openAmount: function(amount) {
- var maxLeft = this.left.width;
- var maxRight = this.right.width;
+ var maxLeft = this.left && this.left.width || 0;
+ var maxRight = this.right && this.right.width || 0;
// Check if we can move to that side, depending if the left/right panel is enabled
- if((!this.left.isEnabled && amount > 0) || (!this.right.isEnabled && amount < 0)) {
+ if((!(this.left && this.left.isEnabled) && amount > 0) || (!(this.right && this.right.isEnabled) && amount < 0)) {
return;
}
@@ -4388,17 +4493,17 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
this._rightShowing = false;
// Push the z-index of the right menu down
- this.right.pushDown();
+ this.right && this.right.pushDown();
// Bring the z-index of the left menu up
- this.left.bringUp();
+ this.left && this.left.bringUp();
} else {
this._rightShowing = true;
this._leftShowing = false;
// Bring the z-index of the right menu up
- this.right.bringUp();
+ this.right && this.right.bringUp();
// Push the z-index of the left menu down
- this.left.pushDown();
+ this.left && this.left.pushDown();
}
},
diff --git a/js/_license.js b/js/_license.js
index bac744f1fb..6e4dc6a37b 100644
--- a/js/_license.js
+++ b/js/_license.js
@@ -8,4 +8,6 @@ http://ionicframework.com/
By @maxlynch, @helloimben, @adamdbradley <3
Licensed under the MIT license. Please see LICENSE for more information.
+
+Make awesome shit.
*/
diff --git a/js/controllers/navController.js b/js/controllers/navController.js
index b56b7f9d97..29460f5670 100644
--- a/js/controllers/navController.js
+++ b/js/controllers/navController.js
@@ -60,21 +60,17 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
return;
// Actually switch the active controllers
-
- // Remove the old one
- //last && last.detach();
if(last) {
last.isVisible = false;
- last.visibilityChanged && last.visibilityChanged();
+ last.visibilityChanged && last.visibilityChanged('push');
}
// Grab the top controller on the stack
var next = this.controllers[this.controllers.length - 1];
- // TODO: No DOM stuff here
- //this.content.el.appendChild(next.el);
next.isVisible = true;
- next.visibilityChanged && next.visibilityChanged();
+ // Trigger visibility change, but send 'first' if this is the first page
+ next.visibilityChanged && next.visibilityChanged(last ? 'push' : 'first');
this._updateNavBar();
@@ -99,7 +95,7 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
last = this.controllers.pop();
if(last) {
last.isVisible = false;
- last.visibilityChanged && last.visibilityChanged();
+ last.visibilityChanged && last.visibilityChanged('pop');
}
// Remove the old one
@@ -110,7 +106,7 @@ ionic.controllers.NavController = ionic.controllers.ViewController.inherit({
// TODO: No DOM stuff here
//this.content.el.appendChild(next.el);
next.isVisible = true;
- next.visibilityChanged && next.visibilityChanged();
+ next.visibilityChanged && next.visibilityChanged('pop');
// Switch to it (TODO: Animate or such things here)
diff --git a/js/controllers/sideMenuController.js b/js/controllers/sideMenuController.js
index e196d946ac..dd1aff5e15 100644
--- a/js/controllers/sideMenuController.js
+++ b/js/controllers/sideMenuController.js
@@ -25,7 +25,7 @@
self._handleDrag(e);
};
- this.content.endDrag = function(e) {
+ this.content.onEndDrag =function(e) {
self._endDrag(e);
};
}
@@ -115,12 +115,12 @@
*/
openPercentage: function(percentage) {
var p = percentage / 100;
- var maxLeft = this.left.width;
- var maxRight = this.right.width;
- if(percentage >= 0) {
- this.openAmount(maxLeft * p);
- } else {
- this.openAmount(maxRight * p);
+
+ if(this.left && percentage >= 0) {
+ this.openAmount(this.left.width * p);
+ } else if(this.right && percentage < 0) {
+ var maxRight = this.right.width;
+ this.openAmount(this.right.width * p);
}
},
@@ -130,11 +130,11 @@
* negative value for right menu (only one menu will be visible at a time).
*/
openAmount: function(amount) {
- var maxLeft = this.left.width;
- var maxRight = this.right.width;
+ var maxLeft = this.left && this.left.width || 0;
+ var maxRight = this.right && this.right.width || 0;
// Check if we can move to that side, depending if the left/right panel is enabled
- if((!this.left.isEnabled && amount > 0) || (!this.right.isEnabled && amount < 0)) {
+ if((!(this.left && this.left.isEnabled) && amount > 0) || (!(this.right && this.right.isEnabled) && amount < 0)) {
return;
}
@@ -149,17 +149,17 @@
this._rightShowing = false;
// Push the z-index of the right menu down
- this.right.pushDown();
+ this.right && this.right.pushDown();
// Bring the z-index of the left menu up
- this.left.bringUp();
+ this.left && this.left.bringUp();
} else {
this._rightShowing = true;
this._leftShowing = false;
// Bring the z-index of the right menu up
- this.right.bringUp();
+ this.right && this.right.bringUp();
// Push the z-index of the left menu down
- this.left.pushDown();
+ this.left && this.left.pushDown();
}
},
diff --git a/js/ext/angular/src/directive/ionicContent.js b/js/ext/angular/src/directive/ionicContent.js
index c15d895daf..97d9796083 100644
--- a/js/ext/angular/src/directive/ionicContent.js
+++ b/js/ext/angular/src/directive/ionicContent.js
@@ -76,9 +76,37 @@ angular.module('ionic.ui.content', [])
return {
restrict: 'E',
replace: true,
- transclude: true,
- template: ''
+ require: ['^?content', '^?list'],
+ template: '',
+ scope: true,
+ link: function($scope, $element, $attr, scrollCtrl) {
+ var icon = $element[0].querySelector('.ionic-refresher');
+
+ // Scale up the refreshing icon
+ var onRefreshOpening = ionic.throttle(function(e, amt) {
+ icon.style[ionic.CSS.TRANSFORM] = 'scale(' + Math.min((1 + amt), 2) + ')';
+ }, 100);
+
+ $scope.$on('onRefreshing', function(e) {
+ icon.style[ionic.CSS.TRANSFORM] = 'scale(2)';
+ });
+
+ $scope.$on('onRefresh', function(e) {
+ icon.style[ionic.CSS.TRANSFORM] = 'scale(1)';
+ });
+ $scope.$on('onRefreshOpening', onRefreshOpening);
+ }
}
})
+.directive('scroll-refresher', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ template: ''
+ }
+});
+
+
})();
diff --git a/js/ext/angular/src/directive/ionicList.js b/js/ext/angular/src/directive/ionicList.js
index c5fd09d2ec..64e84b8080 100644
--- a/js/ext/angular/src/directive/ionicList.js
+++ b/js/ext/angular/src/directive/ionicList.js
@@ -3,10 +3,10 @@
angular.module('ionic.ui.list', ['ngAnimate'])
-.directive('listItem', ['$timeout', function($timeout) {
+.directive('item', ['$timeout', function($timeout) {
return {
restrict: 'E',
- require: ['?^list', '?^virtualList'],
+ require: ['?^list'],
replace: true,
transclude: true,
scope: {
@@ -17,8 +17,9 @@ angular.module('ionic.ui.list', ['ngAnimate'])
canReorder: '@',
canSwipe: '@',
buttons: '=',
+ type: '@'
},
- template: '\
+ template: '\
\
\
\
@@ -32,20 +33,6 @@ angular.module('ionic.ui.list', ['ngAnimate'])
\
',
- /*
- template: '\
- \
- \
-
\
- \
-
\
- \
- \
-
\
- \
- \
-
\
- ',*/
link: function($scope, $element, $attr, list) {
// Grab the parent list controller
if(list[0]) {
@@ -54,6 +41,13 @@ angular.module('ionic.ui.list', ['ngAnimate'])
list = list[1];
}
+ // Add the list item type class
+ $element.addClass($attr.type || 'item-slider');
+
+ if($attr.type !== 'item-slider') {
+ $scope.canSwipe = false;
+ }
+
$scope.isEditing = false;
$scope.deleteIcon = list.scope.deleteIcon;
$scope.reorderIcon = list.scope.reorderIcon;
@@ -90,6 +84,7 @@ angular.module('ionic.ui.list', ['ngAnimate'])
isEditing: '=',
deleteIcon: '@',
reorderIcon: '@',
+ hasPullToRefresh: '@',
onRefresh: '&',
onRefreshOpening: '&'
},
@@ -112,12 +107,14 @@ angular.module('ionic.ui.list', ['ngAnimate'])
var lv = new ionic.views.ListView({
el: $element[0],
listEl: $element[0].children[0],
- hasPullToRefresh: (typeof $scope.onRefresh !== 'undefined'),
+ hasPullToRefresh: ($scope.hasPullToRefresh !== 'false'),
onRefresh: function() {
$scope.onRefresh();
+ $scope.$parent.$broadcast('onRefresh');
},
onRefreshOpening: function(amt) {
$scope.onRefreshOpening({amount: amt});
+ $scope.$parent.$broadcast('onRefreshOpening', amt);
}
});
diff --git a/js/ext/angular/src/directive/ionicNav.js b/js/ext/angular/src/directive/ionicNav.js
index 128ff4cbba..380ada41be 100644
--- a/js/ext/angular/src/directive/ionicNav.js
+++ b/js/ext/angular/src/directive/ionicNav.js
@@ -8,6 +8,31 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
angular.extend(this, ionic.controllers.NavController.prototype);
+ /**
+ * Push a template onto the navigation stack.
+ * @param {string} templateUrl the URL of the template to load.
+ */
+ this.pushFromTemplate = ionic.debounce(function(templateUrl) {
+ var childScope = $scope.$new();
+ childScope.isVisible = true;
+
+ // Load the given template
+ TemplateLoader.load(templateUrl).then(function(templateString) {
+
+ // Compile the template with the new scrope, and append it to the navigation's content area
+ var el = $compile(templateString)(childScope, function(cloned, scope) {
+ var content = angular.element($element[0].querySelector('.content, .scroll'));
+ $animate.enter(cloned, angular.element(content));
+ });
+ });
+ }, 300, true);
+
+ // Pop function, debounced
+ this.popController = ionic.debounce(function() {
+ _this.pop();
+ }, 300, true);
+
+
ionic.controllers.NavController.call(this, {
content: {
},
@@ -31,7 +56,7 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
// Support Android hardware back button (native only, not mobile web)
var onHardwareBackButton = function(e) {
$scope.$apply(function() {
- _this.pop();
+ _this.popController();
});
}
Platform.onHardwareBackButton(onHardwareBackButton);
@@ -44,28 +69,6 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
this.endDrag = function(e) {
};
- /**
- * Push a template onto the navigation stack.
- * @param {string} templateUrl the URL of the template to load.
- */
- this.pushFromTemplate = function(templateUrl) {
- var childScope = $scope.$new();
- childScope.isVisible = true;
-
- // Load the given template
- TemplateLoader.load(templateUrl).then(function(templateString) {
-
- // Compile the template with the new scrope, and append it to the navigation's content area
- var el = $compile(templateString)(childScope, function(cloned, scope) {
- var content = angular.element($element[0].querySelector('.content'));
-
- //content.append(cloned);
- //angular.element(content).append(cloned);
- $animate.enter(cloned, angular.element(content));
- });
- });
- };
-
/**
* Push a controller to the stack. This is called by the child
* nav-content directive when it is linked to a scope on the page.
@@ -112,7 +115,7 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
scope.$watch('navController.controllers.length', function(value) {
});
scope.goBack = function() {
- navCtrl.pop();
+ navCtrl.popController();
};
}
};
@@ -169,7 +172,7 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
// Store that we should go forwards on the animation. This toggles
// based on the visibility sequence (to support reverse transitions)
- var wasVisible = null;
+ var lastDirection = null;
$scope.title = $attr.title;
$scope.pushAnimation = $attr.pushAnimation || 'slide-in-left';
@@ -185,6 +188,24 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
navCtrl.showNavBar();
}
+ $scope.visibilityChanged = function(direction) {
+ lastDirection = direction;
+
+ if(!childElement) {
+ return;
+ }
+
+ var clone = childElement;
+
+ if(direction == 'push') {
+ clone.addClass(childScope.pushAnimation);
+ clone.removeClass(childScope.popAnimation);
+ } else if(direction == 'pop') {
+ clone.addClass(childScope.popAnimation);
+ clone.removeClass(childScope.pushAnimation);
+ }
+ };
+
// Push this controller onto the stack
$scope.pushController($scope, $element);
@@ -196,39 +217,33 @@ angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.ges
transclude(childScope, function(clone) {
childElement = clone;
- // Check if this is visible, and if so, create it and show it
- if(wasVisible === false) {
- clone.removeClass(childScope.pushAnimation);
- clone.addClass(childScope.popAnimation);
- } else {
+ if(lastDirection == 'push') {
clone.addClass(childScope.pushAnimation);
- clone.removeClass(childScope.popAnimation);
+ } else if(lastDirection == 'pop') {
+ clone.addClass(childScope.popAnimation);
}
- $animate.enter(clone, $element.parent(), $element);
- wasVisible = true;
+ $animate.enter(clone, $element.parent(), $element, function() {
+ clone.removeClass(childScope.pushAnimation);
+ clone.removeClass(childScope.popAnimation);
+ });
});
} else {
// Taken from ngIf
if(childElement) {
- var clone = childElement;
// Check if this is visible, and if so, create it and show it
- if(wasVisible === false) {
- clone.removeClass(childScope.pushAnimation);
- clone.addClass(childScope.popAnimation);
- } else {
- clone.addClass(childScope.pushAnimation);
- clone.removeClass(childScope.popAnimation);
- }
- $animate.leave(childElement);
+ $animate.leave(childElement, function() {
+ if(childScope) {
+ childElement.removeClass(childScope.pushAnimation);
+ childElement.removeClass(childScope.popAnimation);
+ }
+ });
childElement = undefined;
- wasVisible = false;
}
if(childScope) {
childScope.$destroy();
childScope = undefined;
}
-
}
});
}
diff --git a/js/ext/angular/src/directive/ionicSideMenu.js b/js/ext/angular/src/directive/ionicSideMenu.js
index d596846bcf..07841e7062 100644
--- a/js/ext/angular/src/directive/ionicSideMenu.js
+++ b/js/ext/angular/src/directive/ionicSideMenu.js
@@ -20,27 +20,18 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
angular.extend(this, ionic.controllers.SideMenuController.prototype);
ionic.controllers.SideMenuController.call(this, {
+ // Our quick implementation of the left side menu
left: {
width: 270,
- pushDown: function() {
- $scope.leftZIndex = -1;
- },
- bringUp: function() {
- $scope.leftZIndex = 0;
- }
},
+
+ // Our quick implementation of the right side menu
right: {
width: 270,
- pushDown: function() {
- $scope.rightZIndex = -1;
- },
- bringUp: function() {
- $scope.rightZIndex = 0;
- }
}
});
- $scope.contentTranslateX = 0;
+ $scope.sideMenuContentTranslateX = 0;
$scope.sideMenuCtrl = this;
})
@@ -72,28 +63,32 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
defaultPrevented = e.defaultPrevented;
});
- Gesture.on('drag', function(e) {
+ var dragFn = function(e) {
if(defaultPrevented) {
return;
}
sideMenuCtrl._handleDrag(e);
- }, $element[0]);
+ };
- Gesture.on('release', function(e) {
+ Gesture.on('drag', dragFn, $element[0]);
+
+ var dragReleaseFn = function(e) {
if(!defaultPrevented) {
sideMenuCtrl._endDrag(e);
}
defaultPrevented = false;
- }, $element[0]);
+ };
+
+ Gesture.on('release', dragReleaseFn, $element[0]);
sideMenuCtrl.setContent({
onDrag: function(e) {},
endDrag: function(e) {},
getTranslateX: function() {
- return $scope.contentTranslateX || 0;
+ return $scope.sideMenuContentTranslateX || 0;
},
setTranslateX: function(amount) {
- $scope.contentTranslateX = amount;
+ $scope.sideMenuContentTranslateX = amount;
$element[0].style.webkitTransform = 'translate3d(' + amount + 'px, 0, 0)';
},
enableAnimation: function() {
@@ -107,6 +102,12 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
$element[0].classList.remove('menu-animated');
}
});
+
+ // Cleanup
+ $scope.$on('$destroy', function() {
+ Gesture.off('drag', dragFn);
+ Gesture.off('release', dragReleaseFn);
+ });
};
}
};
@@ -125,10 +126,23 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
return function($scope, $element, $attr, sideMenuCtrl) {
$scope.side = $attr.side;
+
if($scope.side == 'left') {
sideMenuCtrl.left.isEnabled = true;
+ sideMenuCtrl.left.pushDown = function() {
+ $element[0].style.zIndex = -1;
+ };
+ sideMenuCtrl.left.bringUp = function() {
+ $element[0].style.zIndex = 0;
+ };
} else if($scope.side == 'right') {
sideMenuCtrl.right.isEnabled = true;
+ sideMenuCtrl.right.pushDown = function() {
+ $element[0].style.zIndex = -1;
+ };
+ sideMenuCtrl.right.bringUp = function() {
+ $element[0].style.zIndex = 0;
+ };
}
$element.append(transclude($scope));
diff --git a/js/ext/angular/test/list.html b/js/ext/angular/test/list.html
index acf6b55c85..915f46cf13 100644
--- a/js/ext/angular/test/list.html
+++ b/js/ext/angular/test/list.html
@@ -64,19 +64,31 @@
-
-
-
-
+
+
+
+
+
+
+ -
+
{{item.text}}
-
+
@@ -86,32 +98,6 @@
-
-
+
+
+