diff --git a/dist/css/ionic-ios7.css b/dist/css/ionic-ios7.css
index dbfd0c240b..4027e743f2 100644
--- a/dist/css/ionic-ios7.css
+++ b/dist/css/ionic-ios7.css
@@ -772,10 +772,13 @@ address {
box-shadow: none;
font-size: 17px; }
+.bar [class^="icon-"],
+.bar [class*=" icon-"] {
+ font-size: 24px; }
+
.bar .button-icon {
border: 1px solid transparent;
- background-color: transparent;
- font-size: 24px; }
+ background-color: transparent; }
.bar-header {
top: 0; }
@@ -805,6 +808,7 @@ address {
bottom: 0;
width: 100%;
height: 49px;
+ z-index: 5;
box-orient: horizontal;
-webkit-box-orient: horizontal;
-moz-box-orient: horizontal;
@@ -848,6 +852,9 @@ address {
border-color: #c73927;
color: white; }
+.tabs-top {
+ top: 44px; }
+
.tab-item {
display: block;
width: 0;
@@ -1182,7 +1189,7 @@ button.list-icon-right .list-item-content:after {
.list-thumbnail .list-item-content {
padding-left: 95px;
min-height: 80px; }
- .list-thumbnail .list-item-content img {
+ .list-thumbnail .list-item-content .thumbnail {
position: absolute;
top: 0;
left: 0;
@@ -2010,15 +2017,86 @@ a.button {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
-@keyframes fadein {
+.slide-in-slide-out.ng-enter, .slide-in-slide-out > .ng-enter {
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(100%, 0, 0);
+ box-shadow: -1px 0px 10px rgba(0, 0, 0, 0.6); }
+.slide-in-slide-out.ng-enter-active, .slide-in-slide-out > .ng-enter-active {
+ -webkit-transform: translate3d(0, 0, 0); }
+.slide-in-slide-out.ng-leave, .slide-in-slide-out > .ng-leave {
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(0%, 0, 0); }
+.slide-in-slide-out.ng-leave-active, .slide-in-slide-out > .ng-leave-active {
+ -webkit-transform: translate3d(-10%, 0, 0);
+ opacity: 0.8; }
+
+.slide-in-slide-out-reverse.ng-enter, .slide-in-slide-out-reverse > .ng-enter {
+ z-index: 1;
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ box-shadow: -1px 0px 10px rgba(0, 0, 0, 0.6); }
+.slide-in-slide-out-reverse.ng-enter-active, .slide-in-slide-out-reverse > .ng-enter-active {
+ -webkit-transform: translate3d(0, 0, 0); }
+.slide-in-slide-out-reverse.ng-leave, .slide-in-slide-out-reverse > .ng-leave {
+ z-index: 0;
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(0%, 0, 0); }
+.slide-in-slide-out-reverse.ng-leave-active, .slide-in-slide-out-reverse > .ng-leave-active {
+ -webkit-transform: translate3d(10%, 0, 0);
+ opacity: 0.8; }
+
+/**
+ * An animation that fades out one content area and fades in another
+ */
+.fade-in-out.ng-enter, .fade-in-out > .ng-enter {
+ opacity: 0;
+ -webkit-transition: opacity 0.3s ease-in-out; }
+.fade-in-out.ng-enter-active, .fade-in-out > .ng-enter-active {
+ opacity: 1; }
+.fade-in-out.ng-leave, .fade-in-out > .ng-leave {
+ opacity: 1;
+ -webkit-transition: opacity 0.3s ease-in-out; }
+.fade-in-out.ng-leave-active, .fade-in-out > .ng-leave-active {
+ opacity: 0; }
+
+.slide-left-fade-add {
+ -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 0.5s ease-in-out; }
+
+.slide-left-fade-add-active {
+ -webkit-transform: translate3d(-120px, 0, 0);
+ opacity: 0; }
+
+.slide-left-fade-remove {
+ -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 0.5s ease-in-out;
+ -webkit-transform: translate3d(120px, 0, 0);
+ opacity: 0; }
+
+.slide-left-fade-remove-active {
+ -webkit-transform: translate3d(0, 0, 0);
+ opacity: 1; }
+
+@-webkit-keyframes fadeOut {
from {
- opacity: 0.7; }
+ opacity: 1; }
+
+ to {
+ opacity: 0; } }
+
+@keyframes fadeOut {
+ from {
+ opacity: 1; }
+
+ to {
+ opacity: 0; } }
+
+@-webkit-keyframes fadeIn {
+ from {
+ opacity: 0; }
to {
opacity: 1; } }
-@-webkit-keyframes fadein {
- /* Safari and Chrome */
+@keyframes fadeIn {
from {
opacity: 0; }
@@ -2026,9 +2104,11 @@ a.button {
opacity: 1; } }
.fade-in {
- opacity: 0;
- animation: fadein 0.5s;
- -webkit-animation: fadein 0.5s; }
+ -webkit-animation: fadeOut 0.3s;
+ animation: fadeOut 0.3s; }
+ .fade-in.active {
+ -webkit-animation: fadeIn 0.3s;
+ animation: fadeIn 0.3s; }
.fill-icon {
color: white !important; }
diff --git a/dist/css/ionic-scoped.css b/dist/css/ionic-scoped.css
index 9ed925c27f..261842f15e 100644
--- a/dist/css/ionic-scoped.css
+++ b/dist/css/ionic-scoped.css
@@ -190,15 +190,31 @@
/* the handle when the toggle is "on" */
/* make sure list item content have enough padding on right to fit the toggle */
/* position the toggle to the right within a list item */
- @keyframes fadein {
+ /**
+ * An animation that fades out one content area and fades in another
+ */
+ @-webkit-keyframes fadeOut {
from {
- opacity: 0.7; }
+ opacity: 1; }
+
+ to {
+ opacity: 0; } }
+
+ @keyframes fadeOut {
+ from {
+ opacity: 1; }
+
+ to {
+ opacity: 0; } }
+
+ @-webkit-keyframes fadeIn {
+ from {
+ opacity: 0; }
to {
opacity: 1; } }
- @-webkit-keyframes fadein {
- /* Safari and Chrome */
+ @keyframes fadeIn {
from {
opacity: 0; }
@@ -1515,10 +1531,12 @@
background: none;
box-shadow: none;
font-size: 17px; }
+ .ionic .bar [class^="icon-"],
+ .ionic .bar [class*=" icon-"] {
+ font-size: 24px; }
.ionic .bar .button-icon {
border: 1px solid transparent;
- background-color: transparent;
- font-size: 24px; }
+ background-color: transparent; }
.ionic .bar-header {
top: 0; }
.ionic .bar-footer {
@@ -1537,6 +1555,7 @@
bottom: 0;
width: 100%;
height: 49px;
+ z-index: 5;
box-orient: horizontal;
-webkit-box-orient: horizontal;
-moz-box-orient: horizontal;
@@ -1579,6 +1598,8 @@
background-color: #ef4e3a;
border-color: #c73927;
color: white; }
+ .ionic .tabs-top {
+ top: 44px; }
.ionic .tab-item {
display: block;
width: 0;
@@ -1872,7 +1893,7 @@
.ionic .list-thumbnail .list-item-content {
padding-left: 95px;
min-height: 80px; }
- .ionic .list-thumbnail .list-item-content img {
+ .ionic .list-thumbnail .list-item-content .thumbnail {
position: absolute;
top: 0;
left: 0;
@@ -2664,10 +2685,60 @@
opacity: 1;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
- .ionic .fade-in {
+ .ionic .slide-in-slide-out.ng-enter, .ionic .slide-in-slide-out > .ng-enter {
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(100%, 0, 0);
+ box-shadow: -1px 0px 10px rgba(0, 0, 0, 0.6); }
+ .ionic .slide-in-slide-out.ng-enter-active, .ionic .slide-in-slide-out > .ng-enter-active {
+ -webkit-transform: translate3d(0, 0, 0); }
+ .ionic .slide-in-slide-out.ng-leave, .ionic .slide-in-slide-out > .ng-leave {
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(0%, 0, 0); }
+ .ionic .slide-in-slide-out.ng-leave-active, .ionic .slide-in-slide-out > .ng-leave-active {
+ -webkit-transform: translate3d(-10%, 0, 0);
+ opacity: 0.8; }
+ .ionic .slide-in-slide-out-reverse.ng-enter, .ionic .slide-in-slide-out-reverse > .ng-enter {
+ z-index: 1;
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ box-shadow: -1px 0px 10px rgba(0, 0, 0, 0.6); }
+ .ionic .slide-in-slide-out-reverse.ng-enter-active, .ionic .slide-in-slide-out-reverse > .ng-enter-active {
+ -webkit-transform: translate3d(0, 0, 0); }
+ .ionic .slide-in-slide-out-reverse.ng-leave, .ionic .slide-in-slide-out-reverse > .ng-leave {
+ z-index: 0;
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(0%, 0, 0); }
+ .ionic .slide-in-slide-out-reverse.ng-leave-active, .ionic .slide-in-slide-out-reverse > .ng-leave-active {
+ -webkit-transform: translate3d(10%, 0, 0);
+ opacity: 0.8; }
+ .ionic .fade-in-out.ng-enter, .ionic .fade-in-out > .ng-enter {
opacity: 0;
- animation: fadein 0.5s;
- -webkit-animation: fadein 0.5s; }
+ -webkit-transition: opacity 0.3s ease-in-out; }
+ .ionic .fade-in-out.ng-enter-active, .ionic .fade-in-out > .ng-enter-active {
+ opacity: 1; }
+ .ionic .fade-in-out.ng-leave, .ionic .fade-in-out > .ng-leave {
+ opacity: 1;
+ -webkit-transition: opacity 0.3s ease-in-out; }
+ .ionic .fade-in-out.ng-leave-active, .ionic .fade-in-out > .ng-leave-active {
+ opacity: 0; }
+ .ionic .slide-left-fade-add {
+ -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 0.5s ease-in-out; }
+ .ionic .slide-left-fade-add-active {
+ -webkit-transform: translate3d(-120px, 0, 0);
+ opacity: 0; }
+ .ionic .slide-left-fade-remove {
+ -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 0.5s ease-in-out;
+ -webkit-transform: translate3d(120px, 0, 0);
+ opacity: 0; }
+ .ionic .slide-left-fade-remove-active {
+ -webkit-transform: translate3d(0, 0, 0);
+ opacity: 1; }
+ .ionic .fade-in {
+ -webkit-animation: fadeOut 0.3s;
+ animation: fadeOut 0.3s; }
+ .ionic .fade-in.active {
+ -webkit-animation: fadeIn 0.3s;
+ animation: fadeIn 0.3s; }
.ionic .fill-icon {
color: white !important; }
.ionic .fill-icon:before {
diff --git a/dist/css/ionic.css b/dist/css/ionic.css
index 72c10dc2de..b14f1fe68c 100644
--- a/dist/css/ionic.css
+++ b/dist/css/ionic.css
@@ -1881,10 +1881,13 @@ address {
box-shadow: none;
font-size: 17px; }
+.bar [class^="icon-"],
+.bar [class*=" icon-"] {
+ font-size: 24px; }
+
.bar .button-icon {
border: 1px solid transparent;
- background-color: transparent;
- font-size: 24px; }
+ background-color: transparent; }
.bar-header {
top: 0; }
@@ -1914,6 +1917,7 @@ address {
bottom: 0;
width: 100%;
height: 49px;
+ z-index: 5;
box-orient: horizontal;
-webkit-box-orient: horizontal;
-moz-box-orient: horizontal;
@@ -1957,6 +1961,9 @@ address {
border-color: #c73927;
color: white; }
+.tabs-top {
+ top: 44px; }
+
.tab-item {
display: block;
width: 0;
@@ -2046,6 +2053,38 @@ address {
.modal.active {
height: 100%; }
+.popup {
+ position: fixed; }
+
+.popup-content {
+ padding: 10px; }
+
+.loading-backdrop {
+ position: fixed;
+ visibility: hidden;
+ -webkit-transition: visibility 0s linear 0.3s;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%; }
+ .loading-backdrop.enabled {
+ background-color: rgba(0, 0, 0, 0.7); }
+ .loading-backdrop.active {
+ visibility: visible;
+ -webkit-transition-delay: 0s; }
+
+.loading {
+ position: fixed;
+ left: 50%;
+ top: 50%;
+ border-radius: 5px;
+ padding: 20px;
+ background-color: rgba(0, 0, 0, 0.7);
+ color: #fff;
+ font-size: 20px; }
+ .loading h1, .loading h2, .loading h3, .loading h4, .loading h5 {
+ color: #fff; }
+
.list {
margin-bottom: 20px;
padding-left: 0;
@@ -2291,7 +2330,7 @@ button.list-icon-right .list-item-content:after {
.list-thumbnail .list-item-content {
padding-left: 95px;
min-height: 80px; }
- .list-thumbnail .list-item-content img {
+ .list-thumbnail .list-item-content .thumbnail {
position: absolute;
top: 0;
left: 0;
@@ -3216,6 +3255,12 @@ a.button {
-webkit-transition: opacity 0.4s ease-in;
opacity: 1; }
+.nav-content {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background-color: white; }
+
.slide-in-up {
opacity: 0;
-webkit-transform: translate3d(0, 100%, 0);
@@ -3231,15 +3276,86 @@ a.button {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
-@keyframes fadein {
+.slide-in-slide-out.ng-enter, .slide-in-slide-out > .ng-enter {
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(100%, 0, 0);
+ box-shadow: -1px 0px 10px rgba(0, 0, 0, 0.6); }
+.slide-in-slide-out.ng-enter-active, .slide-in-slide-out > .ng-enter-active {
+ -webkit-transform: translate3d(0, 0, 0); }
+.slide-in-slide-out.ng-leave, .slide-in-slide-out > .ng-leave {
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(0%, 0, 0); }
+.slide-in-slide-out.ng-leave-active, .slide-in-slide-out > .ng-leave-active {
+ -webkit-transform: translate3d(-10%, 0, 0);
+ opacity: 0.8; }
+
+.slide-in-slide-out-reverse.ng-enter, .slide-in-slide-out-reverse > .ng-enter {
+ z-index: 1;
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(-100%, 0, 0);
+ box-shadow: -1px 0px 10px rgba(0, 0, 0, 0.6); }
+.slide-in-slide-out-reverse.ng-enter-active, .slide-in-slide-out-reverse > .ng-enter-active {
+ -webkit-transform: translate3d(0, 0, 0); }
+.slide-in-slide-out-reverse.ng-leave, .slide-in-slide-out-reverse > .ng-leave {
+ z-index: 0;
+ -webkit-transition: 0.5s ease-in-out all;
+ -webkit-transform: translate3d(0%, 0, 0); }
+.slide-in-slide-out-reverse.ng-leave-active, .slide-in-slide-out-reverse > .ng-leave-active {
+ -webkit-transform: translate3d(10%, 0, 0);
+ opacity: 0.8; }
+
+/**
+ * An animation that fades out one content area and fades in another
+ */
+.fade-in-out.ng-enter, .fade-in-out > .ng-enter {
+ opacity: 0;
+ -webkit-transition: opacity 0.3s ease-in-out; }
+.fade-in-out.ng-enter-active, .fade-in-out > .ng-enter-active {
+ opacity: 1; }
+.fade-in-out.ng-leave, .fade-in-out > .ng-leave {
+ opacity: 1;
+ -webkit-transition: opacity 0.3s ease-in-out; }
+.fade-in-out.ng-leave-active, .fade-in-out > .ng-leave-active {
+ opacity: 0; }
+
+.slide-left-fade-add {
+ -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 0.5s ease-in-out; }
+
+.slide-left-fade-add-active {
+ -webkit-transform: translate3d(-120px, 0, 0);
+ opacity: 0; }
+
+.slide-left-fade-remove {
+ -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 0.5s ease-in-out;
+ -webkit-transform: translate3d(120px, 0, 0);
+ opacity: 0; }
+
+.slide-left-fade-remove-active {
+ -webkit-transform: translate3d(0, 0, 0);
+ opacity: 1; }
+
+@-webkit-keyframes fadeOut {
from {
- opacity: 0.7; }
+ opacity: 1; }
+
+ to {
+ opacity: 0; } }
+
+@keyframes fadeOut {
+ from {
+ opacity: 1; }
+
+ to {
+ opacity: 0; } }
+
+@-webkit-keyframes fadeIn {
+ from {
+ opacity: 0; }
to {
opacity: 1; } }
-@-webkit-keyframes fadein {
- /* Safari and Chrome */
+@keyframes fadeIn {
from {
opacity: 0; }
@@ -3247,9 +3363,11 @@ a.button {
opacity: 1; } }
.fade-in {
- opacity: 0;
- animation: fadein 0.5s;
- -webkit-animation: fadein 0.5s; }
+ -webkit-animation: fadeOut 0.3s;
+ animation: fadeOut 0.3s; }
+ .fade-in.active {
+ -webkit-animation: fadeIn 0.3s;
+ animation: fadeIn 0.3s; }
.fill-icon {
color: white !important; }
diff --git a/dist/js/ionic-angular.js b/dist/js/ionic-angular.js
index ce7930f7dd..f2eb0ee6eb 100644
--- a/dist/js/ionic-angular.js
+++ b/dist/js/ionic-angular.js
@@ -12,7 +12,7 @@ angular.module('ionic.ui', ['ionic.ui.content',
]);
;
-angular.module('ionic.service.actionSheet', ['ionic.service', 'ionic.ui.actionSheet'])
+angular.module('ionic.service.actionSheet', ['ionic.service.templateLoad', 'ionic.ui.actionSheet'])
.factory('ActionSheet', ['$rootScope', '$document', '$compile', 'TemplateLoader', function($rootScope, $document, $compile, TemplateLoader) {
return {
@@ -70,7 +70,6 @@ angular.module('ionic.service.actionSheet', ['ionic.service', 'ionic.ui.actionSh
};
}]);
;
-;
angular.module('ionic.service.gesture', [])
.factory('Gesture', [function() {
@@ -81,7 +80,56 @@ angular.module('ionic.service.gesture', [])
};
}]);
;
-angular.module('ionic.service.modal', ['ionic.service'])
+angular.module('ionic.service.loading', ['ionic.ui.loading'])
+
+.factory('Loading', ['$rootScope', '$document', '$compile', function($rootScope, $document, $compile) {
+ return {
+ /**
+ * Load an action sheet with the given template string.
+ *
+ * A new isolated scope will be created for the
+ * action sheet and the new element will be appended into the body.
+ *
+ * @param {object} opts the options for this ActionSheet (see docs)
+ */
+ show: function(opts) {
+ var defaults = {
+ content: '',
+ animation: 'fade-in',
+ showBackdrop: true
+ };
+
+ opts = angular.extend(defaults, opts);
+
+ var scope = $rootScope.$new(true);
+ angular.extend(scope, opts);
+
+ // Make sure there is only one loading element on the page at one point in time
+ var existing = angular.element($document[0].querySelector('.loading-backdrop'));
+ if(existing.length) {
+ var scope = existing.scope();
+ if(scope.loading) {
+ scope.loading.show();
+ return scope.loading;
+ }
+ }
+
+ // Compile the template
+ var element = $compile('' + opts.content + '')(scope);
+
+ $document[0].body.appendChild(element[0]);
+
+ var loading = new ionic.views.Loading({el: element[0] });
+ loading.show();
+
+ scope.loading = loading;
+
+ return loading;
+ }
+ };
+}]);
+;
+angular.module('ionic.service.modal', ['ionic.service.templateLoad'])
.factory('Modal', ['$rootScope', '$document', '$compile', 'TemplateLoader', function($rootScope, $document, $compile, TemplateLoader) {
@@ -122,7 +170,65 @@ angular.module('ionic.service.modal', ['ionic.service'])
};
}]);
;
-angular.module('ionic.service', [])
+angular.module('ionic.service.popup', ['ionic.service.templateLoad'])
+
+
+.factory('Popup', ['$rootScope', '$document', '$compile', 'TemplateLoader', function($rootScope, $document, $compile, TemplateLoader) {
+
+ var getPopup = function() {
+ // Make sure there is only one loading element on the page at one point in time
+ var existing = angular.element($document[0].querySelector('.popup'));
+ if(existing.length) {
+ var scope = existing.scope();
+ if(scope.popup) {
+ return scope;
+ }
+ }
+ };
+
+ return {
+ alert: function(message) {
+
+ // If there is an existing popup, just show that one
+ var existing = getPopup();
+ if(existing) {
+ return existing.popup.alert(message);
+ }
+
+ var defaults = {
+ title: message,
+ animation: 'fade-in',
+ };
+
+ opts = angular.extend(defaults, opts);
+
+ var scope = $rootScope.$new(true);
+ angular.extend(scope, opts);
+
+ // Compile the template
+ var element = $compile('' + opts.content + '')(scope);
+ $document[0].body.appendChild(element[0]);
+
+ var popup = new ionic.views.Popup({el: element[0] });
+ popup.alert(message);
+
+ scope.popup = popup;
+
+ return popup;
+ },
+ confirm: function(cb) {
+ },
+ prompt: function(cb) {
+ },
+ show: function(data) {
+ // data.title
+ // data.template
+ // data.buttons
+ }
+ };
+}]);
+;
+angular.module('ionic.service.templateLoad', [])
.factory('TemplateLoader', ['$q', '$http', '$templateCache', function($q, $http, $templateCache) {
return {
@@ -343,26 +449,38 @@ angular.module('ionic.ui.list', ['ionic.service', 'ngAnimate'])
(function() {
'use strict';
-angular.module('ionic.ui.nav', ['ionic.service'])
+angular.module('ionic.ui.loading', [])
-.controller('NavCtrl', ['$scope', '$element', '$compile', 'TemplateLoader', function($scope, $element, $compile, TemplateLoader) {
+.directive('loading', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ link: function($scope, $element){
+ $scope.$on('$destroy', function() {
+ $element.remove();
+ });
+ $element.addClass($scope.animation || '');
+ },
+ template: '
'
+ };
+});
+
+})();
+;
+(function() {
+'use strict';
+
+angular.module('ionic.ui.nav', ['ionic.service.templateLoad', 'ionic.service.gesture', 'ngAnimate'])
+
+.controller('NavCtrl', ['$scope', '$element', '$animate', '$compile', 'TemplateLoader', function($scope, $element, $animate, $compile, TemplateLoader) {
var _this = this;
angular.extend(this, ionic.controllers.NavController.prototype);
- this.pushFromTemplate = function(tmpl) {
- data = TemplateLoader.load(tmpl).then(function(data) {
- console.log('Nav loaded template', data);
-
- var childScope = $scope.$new();
- childScope.isVisible = true;
-
- $compile(data)(childScope, function(cloned, scope) {
- $element.append(cloned);
- });
- });
- };
-
ionic.controllers.NavController.call(this, {
content: {
},
@@ -383,14 +501,39 @@ angular.module('ionic.ui.nav', ['ionic.service'])
}
});
- $scope.pushController = function(scope) {
+ this.handleDrag = function(e) {
+ };
+
+ this.endDrag = function(e) {
+ };
+
+ this.pushFromTemplate = function(templateUrl) {
+ var childScope = $scope.$new();
+ childScope.isVisible = true;
+
+ TemplateLoader.load(templateUrl).then(function(templateString) {
+ var el = $compile(templateString)(childScope, function(cloned, scope) {
+ angular.element($element[0].children[1].firstElementChild).append(cloned);
+ });
+ });
+ };
+
+ $scope.pushController = function(scope, element) {
_this.push(scope);
+
+ /*
+ var old = angular.element($element[0].children[1]);
+ $animate.enter(element, $element, $element[0].firstElementChild, function() {
+ });
+ $animate.leave(old, function() {
+ });
+ */
};
$scope.navController = this;
}])
-.directive('navCtrl', function() {
+.directive('navs', function() {
return {
restrict: 'E',
replace: true,
@@ -404,7 +547,7 @@ angular.module('ionic.ui.nav', ['ionic.service'])
.directive('navBar', function() {
return {
restrict: 'E',
- require: '^navCtrl',
+ require: '^navs',
replace: true,
scope: true,
template: '',
link: function(scope, element, attrs, navCtrl) {
scope.navController = navCtrl;
+ scope.$watch('navController.controllers.length', function(value) {
+ });
scope.goBack = function() {
navCtrl.pop();
};
@@ -420,37 +565,66 @@ angular.module('ionic.ui.nav', ['ionic.service'])
};
})
-.directive('navContent', function() {
+.directive('navContent', ['Gesture', '$animate', function(Gesture, $animate) {
return {
restrict: 'ECA',
- require: '^navCtrl',
+ require: '^navs',
scope: true,
- link: function(scope, element, attrs, navCtrl) {
- scope.title = attrs.title;
+ transclude: 'element',
+ compile: function(element, attr, transclude) {
+ return function($scope, $element, $attr, navCtrl) {
+ var lastParent, lastIndex, childScope, childElement;
- if(attrs.navBar === "false") {
- navCtrl.hideNavBar();
- } else {
- navCtrl.showNavBar();
- }
+ $scope.title = $attr.title;
- scope.isVisible = true;
- scope.pushController(scope);
-
- scope.$watch('isVisible', function(value) {
- console.log('Visiblity changed', value);
- if(value) {
- element[0].classList.remove('hidden');
+ if($attr.navBar === "false") {
+ navCtrl.hideNavBar();
} else {
- element[0].classList.add('hidden');
+ navCtrl.showNavBar();
}
- });
+
+ $scope.pushController($scope, $element);
+
+
+ $scope.$watch('isVisible', function(value) {
+ if(childElement) {
+ $animate.leave(childElement);
+ childElement = undefined;
+ }
+ if(childScope) {
+ childScope.$destroy();
+ childScope = undefined;
+ }
+ if(value) {
+ childScope = $scope.$new();
+ transclude(childScope, function(clone) {
+ childElement = clone;
+ Gesture.on('drag', function(e) {
+ //navCtrl.handleDrag(e);
+ console.log('Content drag', e);
+ }, childElement[0]);
+
+ Gesture.on('release', function(e) {
+ //navCtrl._endDrag(e);
+ }, childElement[0]);
+
+ var title = $element.parent().parent().parent()[0].querySelector('.title');
+ $animate.enter(clone, $element.parent(), $element);
+ $animate.addClass(angular.element(title), 'slide-left-fade', function() {
+ $animate.removeClass(angular.element(title), 'slide-left-fade', function() {
+ });
+ });
+ });
+ }
+ });
+ }
}
};
-});
+}]);
})();
;
+;
(function() {
'use strict';
@@ -572,9 +746,89 @@ angular.module('ionic.ui.sideMenu', ['ionic.service.gesture'])
});
})();
;
-angular.module('ionic.ui.tabs', [])
+(function() {
+'use strict';
-.controller('TabsCtrl', function($scope) {
+/**
+ * @description
+ * The sideMenuCtrl lets you quickly have a draggable side
+ * left and/or right menu, which a center content area.
+ */
+
+angular.module('ionic.ui.slideBox', [])
+
+/**
+ * The internal controller for the side menu controller. This
+ * extends our core Ionic side menu controller and exposes
+ * some side menu stuff on the current scope.
+ */
+.controller('SlideBoxCtrl', ['$scope', '$element', function($scope, $element) {
+ $scope.slides = [];
+ this.slideAdded = function() {
+ $scope.slides.push({});
+ };
+}])
+
+.directive('slideBox', ['$compile', function($compile) {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ controller: 'SlideBoxCtrl',
+ scope: {},
+ template: '',
+
+ postLink: function() {
+ console.log('POST LINK');
+ },
+ link: function($scope, $element, $attr, slideBoxCtrl) {
+ // If the pager should show, append it to the slide box
+ if($attr.showPager !== "false") {
+ var childScope = $scope.$new();
+ var pager = $compile('')(childScope);
+ $element.append(pager);
+
+ $scope.slideBox = new ionic.views.SlideBox({
+ el: $element[0]
+ });
+ }
+ }
+ }
+}])
+
+.directive('slide', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ require: '^slideBox',
+ transclude: true,
+ template: '',
+ compile: function(element, attr, transclude) {
+ return function($scope, $element, $attr, slideBoxCtrl) {
+ slideBoxCtrl.slideAdded();
+ }
+ }
+ }
+})
+
+.directive('pager', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ require: '^slideBox',
+ template: ''
+ }
+
+});
+
+})();
+;
+angular.module('ionic.ui.tabs', ['ngAnimate'])
+
+.controller('TabsCtrl', ['$scope', '$element', '$animate', function($scope, $element, $animate) {
var _this = this;
angular.extend(this, ionic.controllers.TabBarController.prototype);
@@ -582,83 +836,119 @@ angular.module('ionic.ui.tabs', [])
ionic.controllers.TabBarController.call(this, {
tabBar: {
tryTabSelect: function() {},
- setSelectedItem: function(index) {
- console.log('TAB BAR SET SELECTED INDEX', index);
- },
- addItem: function(item) {
- console.log('TAB BAR ADD ITEM', item);
- }
+ setSelectedItem: function(index) {},
+ addItem: function(item) {}
}
});
+ this.add = function(controller) {
+ this.addController(controller);
+ this.select(0);
+ };
+
+ this.select = function(controllerIndex) {
+ var oldIndex = _this.getSelectedIndex();
+
+ $scope.activeAnimation = $scope.animation;
+ /*
+ if(controllerIndex > oldIndex) {
+ } else if(controllerIndex < oldIndex) {
+ $scope.activeAnimation = $scope.animation + '-reverse';
+ }
+ */
+ _this.selectController(controllerIndex);
+ };
+
$scope.controllers = this.controllers;
+}])
- $scope.$watch('controllers', function(newV, oldV) {
- console.log("CControlelrs changed", newV, oldV);
- //$scope.$apply();
- });
-})
-
-.directive('tabController', function() {
+.directive('tabs', function() {
return {
restrict: 'E',
replace: true,
- scope: {},
+ scope: {
+ animation: '@'
+ },
transclude: true,
controller: 'TabsCtrl',
//templateUrl: 'ext/angular/tmpl/ionicTabBar.tmpl.html',
- template: '',
+ template: '
',
compile: function(element, attr, transclude, tabsCtrl) {
return function($scope, $element, $attr) {
+ $scope.$watch('activeAnimation', function(value) {
+ //$element.removeClass($scope.animation + ' ' + $scope.animation + '-reverse');
+ $element.addClass($scope.activeAnimation);
+ });
+ transclude($scope, function(cloned) {
+ $element.prepend(cloned);
+ });
};
}
};
})
// Generic controller directive
-.directive('tabContent', function() {
- return {
- restrict: 'CA',
- replace: true,
- require: '^tabController',
- scope: true,
- link: function(scope, element, attrs, tabsCtrl) {
- scope.$watch('isVisible', function(value) {
- if(!value) {
- element[0].style.display = 'none';
- } else {
- element[0].style.display = 'block';
- }
- });
-
- scope.title = attrs.title;
- scope.icon = attrs.icon;
- scope.iconOn = attrs.iconOn;
- scope.iconOff = attrs.iconOff;
- tabsCtrl.addController(scope);
- }
- };
-})
-
-
-.directive('tabBar', function() {
+.directive('tab', ['$animate', function($animate) {
return {
restrict: 'E',
- require: '^tabController',
+ replace: true,
+ require: '^tabs',
+ scope: true,
+ transclude: 'element',
+ compile: function(element, attr, transclude) {
+ return function($scope, $element, $attr, tabsCtrl) {
+ var childScope, childElement;
+
+
+ $scope.$watch('isVisible', function(value) {
+ if(childElement) {
+ $animate.leave(childElement);
+ childElement = undefined;
+ }
+ if(childScope) {
+ childScope.$destroy();
+ childScope = undefined;
+ }
+ if(value) {
+ childScope = $scope.$new();
+ transclude(childScope, function(clone) {
+ childElement = clone;
+ childElement.addClass('view-full');
+ $animate.enter(clone, $element.parent(), $element);
+ });
+ }
+ });
+
+ $scope.title = $attr.title;
+ $scope.icon = $attr.icon;
+ $scope.iconOn = $attr.iconOn;
+ $scope.iconOff = $attr.iconOff;
+ tabsCtrl.add($scope);
+
+ }
+ }
+ };
+}])
+
+
+.directive('tabControllerBar', function() {
+ return {
+ restrict: 'E',
+ require: '^tabs',
transclude: true,
replace: true,
scope: true,
template: '' +
- '' +
+ '' +
'
'
};
})
-.directive('tabItem', function() {
+.directive('tabControllerItem', function() {
return {
restrict: 'E',
replace: true,
- require: '^tabController',
+ require: '^tabs',
scope: {
title: '@',
iconOn: '@',
@@ -668,9 +958,8 @@ angular.module('ionic.ui.tabs', [])
index: '='
},
link: function(scope, element, attrs, tabsCtrl) {
- console.log('Linked item', scope);
scope.selectTab = function(index) {
- tabsCtrl.selectController(scope.index);
+ tabsCtrl.select(scope.index);
};
},
template:
@@ -680,6 +969,39 @@ angular.module('ionic.ui.tabs', [])
' {{title}}' +
''
};
+})
+
+.directive('tabBar', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ template: '' +
+ '
'
+ }
+})
+
+.directive('tabItem', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ scope: {
+ title: '@',
+ iconOn: '@',
+ iconOff: '@',
+ active: '=',
+ tabSelected: '@',
+ index: '='
+ },
+ link: function(scope, element, attrs) {
+ },
+ template:
+ '' +
+ '' +
+ '' +
+ ' {{title}}' +
+ ''
+ };
});
;
angular.module('ionic.ui.toggle', [])
diff --git a/dist/js/ionic.js b/dist/js/ionic.js
index baca49d451..8165a7e15a 100644
--- a/dist/js/ionic.js
+++ b/dist/js/ionic.js
@@ -1842,6 +1842,8 @@ window.ionic = {
;
(function(ionic) {
'use strict';
+
+
var DragOp = function() {};
DragOp.prototype = {
start: function(e) {
@@ -1852,11 +1854,25 @@ window.ionic = {
}
};
+
+ /**
+ * The Pull To Refresh drag operation handles the well-known
+ * "pull to refresh" concept seen on various apps. This lets
+ * the user indicate they want to refresh a given list by dragging
+ * down.
+ *
+ * @param {object} opts the options for the pull to refresh drag.
+ */
var PullToRefreshDrag = function(opts) {
this.dragThresholdY = opts.dragThresholdY || 10;
+ this.onRefreshOpening = opts.onRefreshOpening || function() {};
+ this.onRefresh = opts.onRefresh || function() {};
+ this.onRefreshHolding = opts.onRefreshHolding || function() {};
this.el = opts.el;
};
+
PullToRefreshDrag.prototype = new DragOp();
+
PullToRefreshDrag.prototype.start = function(e) {
var content, refresher;
@@ -1869,17 +1885,23 @@ window.ionic = {
refresher = this._injectRefresher();
}
+ // Disable animations while dragging
+ refresher.classList.remove('list-refreshing');
+
this._currentDrag = {
refresher: refresher,
- content: content
+ content: content,
+ isHolding: false
};
};
+
PullToRefreshDrag.prototype._injectRefresher = function() {
var refresher = document.createElement('div');
refresher.className = 'list-refresher';
this.el.insertBefore(refresher, this.el.firstChild);
return refresher;
};
+
PullToRefreshDrag.prototype.drag = function(e) {
var _this = this;
@@ -1896,21 +1918,59 @@ window.ionic = {
}
if(_this._isDragging) {
- var currentHeight = parseFloat(_this._currentDrag.refresher.style.height);
- _this._currentDrag.refresher.style.height = e.gesture.deltaY + 'px';
+ var refresher = _this._currentDrag.refresher;
- var newHeight = parseFloat(_this._currentDrag.refresher.style.height = e.gesture.deltaY);
- var firstChildHeight = parseFloat(_this._currentDrag.refresher.firstElementChild.style.height);
- console.log('New Height must pass', firstChildHeight);
- if(newHeight > firstChildHeight) {
- console.log('PASSED', firstChildHeight);
+ var currentHeight = parseFloat(refresher.style.height);
+ refresher.style.height = e.gesture.deltaY + 'px';
+
+ var newHeight = parseFloat(refresher.style.height);
+ var firstChildHeight = parseFloat(refresher.firstElementChild.offsetHeight);
+
+ if(newHeight > firstChildHeight && !_this._currentDrag.isHolding) {
+ // The user is holding the refresh but hasn't let go of it
+ _this._currentDrag.isHolding = true;
+ _this.onRefreshHolding && _this.onRefreshHolding();
+ } else {
+ // Indicate what ratio of opening the list refresh drag is
+ var ratio = Math.min(1, newHeight / firstChildHeight);
+ _this.onRefreshOpening && _this.onRefreshOpening(ratio);
}
}
});
};
- PullToRefreshDrag.prototype.end = function(e) {
+
+ PullToRefreshDrag.prototype.end = function(e, doneCallback) {
+ var _this = this;
+
+ // There is no drag, just end immediately
+ if(!this._currentDrag) {
+ return;
+ }
+
+ var refresher = this._currentDrag.refresher;
+
+ var currentHeight = parseFloat(refresher.style.height);
+ refresher.style.height = e.gesture.deltaY + 'px';
+
+ var firstChildHeight = parseFloat(refresher.firstElementChild.offsetHeight);
+
+ if(currentHeight > firstChildHeight) {
+ //this.refreshing();
+ refresher.classList.add('list-refreshing');
+ refresher.style.height = firstChildHeight + 'px';
+ this.onRefresh && _this.onRefresh();
+ } else {
+ // Enable animations
+ refresher.classList.add('list-refreshing');
+ refresher.style.height = '0px';
+ this.onRefresh && _this.onRefresh();
+ }
+
+ this._currentDrag = null;
+ doneCallback && doneCallback();
};
+
var SlideDrag = function(opts) {
this.dragThresholdX = opts.dragThresholdX || 10;
this.el = opts.el;
@@ -2150,6 +2210,10 @@ window.ionic = {
// The amount of dragging required to start sliding the element over (in pixels)
this.dragThresholdX = opts.dragThresholdX || 10;
+
+ this.onRefresh = opts.onRefresh || function() {};
+ this.onRefreshOpening = opts.onRefreshOpening || function() {};
+ this.onRefreshHolding = opts.onRefreshHolding || function() {};
// Start the drag states
this._initDrag();
@@ -2164,6 +2228,14 @@ window.ionic = {
};
ionic.views.List.prototype = {
+ /**
+ * Called to tell the list to stop refreshing. This is useful
+ * if you are refreshing the list and are done with refreshing.
+ */
+ stopRefreshing: function() {
+ var refresher = this.el.querySelector('.list-refresher');
+ refresher.style.height = '0px';
+ },
_initDrag: function() {
this._isDragging = false;
@@ -2183,6 +2255,8 @@ window.ionic = {
_startDrag: function(e) {
+ var _this = this;
+
this._isDragging = false;
// Check if this is a reorder drag
@@ -2197,7 +2271,18 @@ window.ionic = {
// Check if this is a "pull down" drag for pull to refresh
else if(e.gesture.direction == 'down') {
- this._dragOp = new PullToRefreshDrag({ el: this.el });
+ this._dragOp = new PullToRefreshDrag({
+ el: this.el,
+ onRefresh: function() {
+ _this.onRefresh && _this.onRefresh();
+ },
+ onRefreshHolding: function() {
+ _this.onRefreshHolding && _this.onRefreshHolding();
+ },
+ onRefreshOpening: function(ratio) {
+ _this.onRefreshOpening && _this.onRefreshOpening(ratio);
+ }
+ });
this._dragOp.start(e);
}
@@ -2230,12 +2315,51 @@ window.ionic = {
if(!this._dragOp) {
this._startDrag(e);
+ if(!this._dragOp) { return; }
}
this._dragOp.drag(e);
}
};
+})(ionic);
+;
+(function(ionic) {
+'use strict';
+ /**
+ * An ActionSheet is the slide up menu popularized on iOS.
+ *
+ * You see it all over iOS apps, where it offers a set of options
+ * triggered after an action.
+ */
+ ionic.views.Loading = function(opts) {
+ var _this = this;
+
+ this.el = opts.el;
+ this._loadingBox = this.el.querySelector('.loading');
+ };
+
+ ionic.views.Loading.prototype = {
+ show: function() {
+ var _this = this;
+
+ if(this._loadingBox) {
+ window.requestAnimationFrame(function() {
+ _this.el.classList.add('active');
+
+ _this._loadingBox.style.marginLeft = (-_this._loadingBox.offsetWidth) / 2 + 'px';
+ _this._loadingBox.style.marginTop = (-_this._loadingBox.offsetHeight) / 2 + 'px';
+ });
+ }
+ },
+ hide: function() {
+ // Force a reflow so the animation will actually run
+ this.el.offsetWidth;
+
+ this.el.classList.remove('active');
+ }
+ };
+
})(ionic);
;
(function(ionic) {
@@ -2308,6 +2432,43 @@ window.ionic = {
}
};
+})(ionic);
+;
+(function(ionic) {
+'use strict';
+ /**
+ * An ActionSheet is the slide up menu popularized on iOS.
+ *
+ * You see it all over iOS apps, where it offers a set of options
+ * triggered after an action.
+ */
+ ionic.views.Popup = function(opts) {
+ var _this = this;
+
+ this.el = opts.el;
+ };
+
+ ionic.views.Popup.prototype = {
+ setTitle: function(title) {
+ var title = el.querySelector('.popup-title');
+ title && title.innerHTML = title;
+ },
+ alert: function(message) {
+ var _this = this;
+
+ window.requestAnimationFrame(function() {
+ _this.setTitle(message);
+ _this.el.classList.add('active');
+ });
+ },
+ hide: function() {
+ // Force a reflow so the animation will actually run
+ this.el.offsetWidth;
+
+ this.el.classList.remove('active');
+ }
+ };
+
})(ionic);
;
(function(ionic) {
diff --git a/scss/ionic/_bar.scss b/scss/ionic/_bar.scss
index d2707eb77b..d36307a597 100644
--- a/scss/ionic/_bar.scss
+++ b/scss/ionic/_bar.scss
@@ -175,12 +175,16 @@
}
}
-.bar .button-icon {
- border: 1px solid transparent;
- background-color: transparent;
+.bar [class^="icon-"],
+.bar [class*=" icon-"] {
font-size: 24px;
}
+.bar .button-icon {
+ border: $button-border-width solid transparent;
+ background-color: transparent;
+}
+
// Header at top
.bar-header {
top: 0;
diff --git a/scss/ionic/_listview.scss b/scss/ionic/_listview.scss
index fb0495db83..3d7b300d4e 100644
--- a/scss/ionic/_listview.scss
+++ b/scss/ionic/_listview.scss
@@ -190,7 +190,7 @@ button.list-icon-right {
padding-left: $list-thumbnail-width + $list-item-padding;
min-height: $list-thumbnail-width;
- img {
+ .thumbnail {
position: absolute;
top: 0;
left: 0;
diff --git a/scss/ionic/_mixins.scss b/scss/ionic/_mixins.scss
index d90de00573..f8cde9d8da 100644
--- a/scss/ionic/_mixins.scss
+++ b/scss/ionic/_mixins.scss
@@ -131,17 +131,6 @@
clear: both;
}
}
-
-// Webkit-style focus
-// ------------------
-@mixin tab-focus() {
- // Default
- outline: thin dotted #333;
- // Webkit
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
-}
-
// Center-align a block level element
// ----------------------------------
@mixin center-block() {
diff --git a/scss/ionic/_variables.scss b/scss/ionic/_variables.scss
index cb84ebf2de..1b0301b9b9 100644
--- a/scss/ionic/_variables.scss
+++ b/scss/ionic/_variables.scss
@@ -213,7 +213,7 @@ $info-border: darken(adjust-hue($info-bg, -10), 7%);
// Buttons
// -------------------------------
-$button-bar-button-line-height: 15px !default;
+$button-bar-button-line-height: 15px !default;
$button-color: #222;
$button-padding: 8px 12px;
diff --git a/test/headers.html b/test/headers.html
index 178c64e4c7..2e1d07f5f3 100644
--- a/test/headers.html
+++ b/test/headers.html
@@ -5,8 +5,7 @@
-
-
+