mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-07 06:57:02 +08:00
Toderp working with login and signup!
This commit is contained in:
@ -11,17 +11,58 @@
|
||||
transition: transform 1s ease-in-out;
|
||||
*/
|
||||
}
|
||||
.reveal-animation.ng-enter {
|
||||
.reveal-animation > .ng-enter {
|
||||
-webkit-transition: 0.2s linear all;
|
||||
-webkit-transform:translate3d(100%,0,0) ;
|
||||
}
|
||||
.reveal-animation.ng-enter-active {
|
||||
.reveal-animation > .ng-enter-active {
|
||||
-webkit-transform:translate3d(0,0,0) ;
|
||||
}
|
||||
.reveal-animation.ng-leave {
|
||||
.reveal-animation > .ng-leave {
|
||||
-webkit-transition: 0.2s linear all;
|
||||
-webkit-transform:translate3d(0,0,0);
|
||||
}
|
||||
.reveal-animation.ng-leave-active {
|
||||
.reveal-animation > .ng-leave-active {
|
||||
-webkit-transform:translate3d(-100%,0,0);
|
||||
}
|
||||
|
||||
|
||||
.animate-switch-container {
|
||||
position:relative;
|
||||
background:white;
|
||||
border:1px solid black;
|
||||
height:40px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.animate-switch-container > div {
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.animate-switch-container > .ng-enter,
|
||||
.animate-switch-container > .ng-leave {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
|
||||
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
}
|
||||
|
||||
.animate-switch-container > .ng-enter {
|
||||
top:-50px;
|
||||
}
|
||||
.animate-switch-container > .ng-enter.ng-enter-active {
|
||||
top:0;
|
||||
}
|
||||
|
||||
.animate-switch-container > .ng-leave {
|
||||
top:0;
|
||||
}
|
||||
.animate-switch-container > .ng-leave.ng-leave-active {
|
||||
top:50px;
|
||||
}
|
||||
|
||||
@ -1,60 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Example</title>
|
||||
|
||||
<!-- Sets initial viewport load and disables zooming -->
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link href="/vendor/font-awesome/css/font-awesome.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="../dist/ionic.css">
|
||||
<link rel="stylesheet" href="app.css">
|
||||
|
||||
<script src="/vendor/angular/1.2.0rc2/angular-1.2.0rc2.min.js"></script>
|
||||
<script src="/vendor/angular/1.2.0rc2/angular-touch.js"></script>
|
||||
<script src="/vendor/angular/1.2.0rc2/angular-resource.js"></script>
|
||||
<script src="../../js/ionic-list.js"></script>
|
||||
<script src="../../js/controllers/ionic-leftrightmenu.js"></script>
|
||||
<script src="menu.js"></script>
|
||||
|
||||
</head>
|
||||
<body ng-app="ionic.menu">
|
||||
<ionic-left-right-menu>
|
||||
<section id="page" class="full-section menu-animated">
|
||||
|
||||
<header class="bar bar-header bar-dark">
|
||||
<div class="buttons">
|
||||
<a id="left-button" class="button button-dark" href="#">
|
||||
<i class="icon-reorder"></i>
|
||||
</a>
|
||||
</div>
|
||||
<h1 class="title">Chats</h1>
|
||||
<div class="buttons">
|
||||
<button id="right-button" class="button button-dark">
|
||||
<i class="icon-cog"></i>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
</section>
|
||||
|
||||
<div class="menu menu-left">
|
||||
<ul class="list">
|
||||
<li class="list-divider">Left Nav Things</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="menu menu-right">
|
||||
<ul class="list">
|
||||
<li class="list-divider">Right Nav Things</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--
|
||||
<ionic-menu side="left">
|
||||
</ionic-menu>
|
||||
<ionic-content>
|
||||
<h2>ASDF</h2>
|
||||
<ionic-content>
|
||||
<ionic-menu side="right">
|
||||
</ionic-menu>
|
||||
-->
|
||||
</ionic-left-right-menu>
|
||||
</body>
|
||||
</html>
|
||||
41
example/angular/menu.js
vendored
41
example/angular/menu.js
vendored
@ -1,41 +0,0 @@
|
||||
angular.module('ionic.menu', [])
|
||||
|
||||
.controller('LeftRightMenuController', ['$scope', '$element',
|
||||
function LeftRightMenuCtrl($scope, $element) {
|
||||
var ctrl = ion.controllers.LeftRightMenuViewController;
|
||||
|
||||
$scope.controllerInitData = {};
|
||||
|
||||
$scope.initIonicController = function() {
|
||||
$scope._ionicController = new ctrl($scope.controllerInitData);
|
||||
};
|
||||
}])
|
||||
|
||||
.directive('ionicLeftRightMenu', function() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
controller: 'LeftRightMenuController',
|
||||
link: function($scope, element, attributes) {
|
||||
$scope
|
||||
console.log('link', $scope);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
.directive('ionicMenu', function() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
controller: '',
|
||||
compile: function(elm, attrs, transclude) {
|
||||
return function(scope, element, attrs, menuCtrl) {
|
||||
console.log('Compile');
|
||||
};
|
||||
},
|
||||
link: function(scope) {
|
||||
console.log('link');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
*/
|
||||
0
example/angular/panel.js
vendored
0
example/angular/panel.js
vendored
@ -11,7 +11,8 @@ label {
|
||||
input {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
#login {
|
||||
/*
|
||||
#signup-button {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
z-index: 4;
|
||||
@ -19,3 +20,36 @@ input {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
*/
|
||||
.pane {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.reveal-animation {
|
||||
/*
|
||||
-webkit-transform: translate3d(0%, 0, 0);
|
||||
transform: translate3d(0%, 0, 0);
|
||||
|
||||
-webkit-transition: -webkit-transform 1s ease-in-out;
|
||||
transition: transform 1s ease-in-out;
|
||||
*/
|
||||
}
|
||||
.reveal-animation > .ng-enter {
|
||||
-webkit-transition: .2s ease-in-out all;
|
||||
-webkit-transform:translate3d(100%,0,0) ;
|
||||
}
|
||||
.reveal-animation > .ng-enter-active {
|
||||
-webkit-transform:translate3d(0,0,0) ;
|
||||
}
|
||||
.reveal-animation > .ng-leave {
|
||||
-webkit-transition: .2s ease-in-out all;
|
||||
-webkit-transform:translate3d(0%,0,0);
|
||||
}
|
||||
.reveal-animation > .ng-leave-active {
|
||||
-webkit-transition: .2s ease-in-out all;
|
||||
-webkit-transform:translate3d(-100%,0,0);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
angular.module('toderp', [])
|
||||
angular.module('toderp', ['firebase'])
|
||||
|
||||
.factory('TaskStorageService', function() {
|
||||
return {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<html>
|
||||
<html ng-app="toderp">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>ToDerp</title>
|
||||
@ -9,37 +9,74 @@
|
||||
<link rel="stylesheet" href="../../dist/ionic.css">
|
||||
<link rel="stylesheet" href="app.css">
|
||||
|
||||
<script src="/vendor/angular/1.2.0rc1/angular-1.2.0rc1.min.js"></script>
|
||||
<script src="/vendor/angular/1.2.0rc1/angular-touch.js"></script>
|
||||
<script src="/vendor/angular/1.2.0rc2/angular-1.2.0rc2.min.js"></script>
|
||||
<script src="/vendor/angular/1.2.0rc2/angular-animate.js"></script>
|
||||
<script src="/vendor/angular/1.2.0rc2/angular-touch.js"></script>
|
||||
<script src="/vendor/angular/1.2.0rc2/angular-route.js"></script>
|
||||
|
||||
<script src='https://cdn.firebase.com/v0/firebase.js'></script>
|
||||
<script src="https://cdn.firebase.com/v0/firebase-simple-login.js"></script>
|
||||
<script src='https://cdn.firebase.com/libs/angularfire/0.3.0/angularfire.min.js'></script>
|
||||
|
||||
<script src="../../js/ionic-list.js"></script>
|
||||
<script src="../../js/ionic-events.js"></script>
|
||||
<script src="../../js/ionic-gestures.js"></script>
|
||||
<script src="../../js/controllers/ionic-leftrightmenu.js"></script>
|
||||
|
||||
<script src="js/toderp.js"></script>
|
||||
</head>
|
||||
<body ng-app="toderp">
|
||||
<div id="login">
|
||||
<main class="content content-padded has-header">
|
||||
<div class="container" style="text-align: center">
|
||||
<h1>ToDerp</h1>
|
||||
<h3>Finish your Top Three Tasks Today</h3>
|
||||
</div>
|
||||
<form class="form-horizontal">
|
||||
<div class="input-group inset">
|
||||
<label class="input-wrapper row">
|
||||
<span class="input-label col-xs-4">Email</span>
|
||||
<input class="col-xs-8" type="email" placeholder="">
|
||||
</label>
|
||||
<label class="input-wrapper row">
|
||||
<span class="input-label col-xs-4">Password</span>
|
||||
<input class="col-xs-8" type="password" placeholder="">
|
||||
</label>
|
||||
<body ng-controller="ToderpCtrl">
|
||||
<div ng-switch on="display.screen" class="reveal-animation">
|
||||
<div id="login" ng-switch-when="login"ng-controller="LoginCtrl" class="pane">
|
||||
<main class="content content-padded has-header">
|
||||
<div class="container" style="text-align: center">
|
||||
<h1>ToDerp</h1>
|
||||
<h3>Finish your Top Three Tasks Today</h3>
|
||||
</div>
|
||||
<button class="button button-info button-block">Log in</button>
|
||||
</form>
|
||||
<button id="login" class="button button-default button-block">Create an account</button>
|
||||
</main>
|
||||
</section>
|
||||
<form class="form-horizontal" ng-submit="tryLogin(loginForm)">
|
||||
<div class="input-group inset">
|
||||
<label class="input-wrapper row">
|
||||
<span class="input-label col-xs-4">Email</span>
|
||||
<input class="col-xs-8" type="email" placeholder="" ng-model="loginForm.email">
|
||||
</label>
|
||||
<label class="input-wrapper row">
|
||||
<span class="input-label col-xs-4">Password</span>
|
||||
<input class="col-xs-8" type="password" placeholder="" ng-model="loginForm.password">
|
||||
</label>
|
||||
</div>
|
||||
<div class="input-group inset">
|
||||
<button class="button button-info button-block">Log in</button>
|
||||
<div id="login-error" ng-show="loginError">Invalid email/password. Please try again.</div>
|
||||
</div>
|
||||
</form>
|
||||
<button ng-click="showSignup()" id="signup-button" class="button button-default button-block">Create an account</button>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="signup" ng-switch-when="signup" ng-controller="SignupCtrl" class="pane">
|
||||
<header class="bar bar-header bar-danger">
|
||||
<a href="#" class="button">Back</a>
|
||||
<h1 class="title">Sign up (it's free!)</h1>
|
||||
</header>
|
||||
<main class="content content-padded has-header">
|
||||
<form class="form-horizontal" ng-submit="trySignup(signupForm)">
|
||||
<div class="input-group inset">
|
||||
<label class="input-wrapper row">
|
||||
<span class="input-label col-xs-4">Email</span>
|
||||
<input class="col-xs-8" type="email" placeholder="" ng-model="signupForm.email">
|
||||
</label>
|
||||
<label class="input-wrapper row">
|
||||
<span class="input-label col-xs-4">Password</span>
|
||||
<input class="col-xs-8" type="password" placeholder="" ng-model="signupForm.password">
|
||||
</label>
|
||||
</div>
|
||||
<div class="input-group inset">
|
||||
<button class="button button-info button-block">Sign up</button>
|
||||
<div id="signup-error" ng-show="signupError">Unable to signup, please try again.</div>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<!--
|
||||
<div id="page" class="page" ng-controller="TodaysTaskListCtrl">
|
||||
@ -75,6 +112,5 @@
|
||||
</ul>
|
||||
</div>
|
||||
-->
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
81
example/toderp/js/toderp.js
Normal file
81
example/toderp/js/toderp.js
Normal file
@ -0,0 +1,81 @@
|
||||
angular.module('toderp', ['firebase', 'ngRoute', 'ngAnimate'])
|
||||
|
||||
.controller('ToderpCtrl', function($scope) {
|
||||
$scope.display = {
|
||||
screen: 'login'
|
||||
};
|
||||
$scope.setScreen = function(screen) {
|
||||
$scope.display.screen = screen;
|
||||
};
|
||||
})
|
||||
|
||||
.factory('AuthService', function(angularFireAuth, $rootScope) {
|
||||
var ref = new Firebase('https://ionic-todo-demo.firebaseio.com/');
|
||||
angularFireAuth.initialize(ref, {
|
||||
scope: $rootScope,
|
||||
name: 'user'
|
||||
});
|
||||
|
||||
$rootScope.$on('angularFireAuth:login', function(evt, user) {
|
||||
console.log('Logged in!', evt, user);
|
||||
});
|
||||
$rootScope.$on('angularFireAuth:logout', function(evt, user) {
|
||||
console.log('Logged out!', evt, user);
|
||||
});
|
||||
$rootScope.$on('angularFireAuth:error', function(evt, err) {
|
||||
console.log('Login Error!', evt, err);
|
||||
});
|
||||
|
||||
return {
|
||||
login: function(email, password) {
|
||||
if(!email || !password) {
|
||||
return;
|
||||
}
|
||||
console.log('Logging in', email, password);
|
||||
return angularFireAuth.login('password', {
|
||||
email: email,
|
||||
password: password
|
||||
});
|
||||
},
|
||||
signup: function(email, password) {
|
||||
if(!email || !password) {
|
||||
return;
|
||||
}
|
||||
console.log('Signing up', name, email, password);
|
||||
|
||||
angularFireAuth.createUser(email, password, function(err, user) {
|
||||
console.log('SIGED UP', err, user);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
.controller('LoginCtrl', function($scope, AuthService) {
|
||||
console.log('Created login Ctrl');
|
||||
$scope.loginForm = {
|
||||
email: 'max@drifty.com',
|
||||
password: 'test'
|
||||
};
|
||||
|
||||
$scope.tryLogin = function(data) {
|
||||
$scope.loginError = false;
|
||||
AuthService.login(data.email, data.password)
|
||||
.then(function(e) {
|
||||
$scope.loginError = false;
|
||||
}, function(e) {
|
||||
$scope.display.screen = 'signup';
|
||||
$scope.loginError = true;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.showSignup = function() {
|
||||
$scope.setScreen('signup');
|
||||
}
|
||||
})
|
||||
|
||||
.controller('SignupCtrl', function($scope, AuthService) {
|
||||
|
||||
$scope.trySignup = function(data) {
|
||||
AuthService.signup(data.email, data.password);
|
||||
};
|
||||
});
|
||||
@ -133,6 +133,7 @@
|
||||
line-height: $bar-button-line-height;
|
||||
font-size: 12px;
|
||||
padding: 4px 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.button-bar {
|
||||
|
||||
708
vendor/angular/1.2.0rc2/angular-animate.js
vendored
Normal file
708
vendor/angular/1.2.0rc2/angular-animate.js
vendored
Normal file
@ -0,0 +1,708 @@
|
||||
/**
|
||||
* @license AngularJS v1.2.0-rc.2
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngAnimate
|
||||
* @description
|
||||
*
|
||||
* # ngAnimate
|
||||
*
|
||||
* `ngAnimate` is an optional module that provides CSS and JavaScript animation hooks.
|
||||
*
|
||||
* {@installModule animate}
|
||||
*
|
||||
* # Usage
|
||||
*
|
||||
* To see animations in action, all that is required is to define the appropriate CSS classes
|
||||
* or to register a JavaScript animation via the $animation service. The directives that support animation automatically are:
|
||||
* `ngRepeat`, `ngInclude`, `ngSwitch`, `ngShow`, `ngHide` and `ngView`. Custom directives can take advantage of animation
|
||||
* by using the `$animate` service.
|
||||
*
|
||||
* Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives:
|
||||
*
|
||||
* | Directive | Supported Animations |
|
||||
* |---------------------------------------------------------- |----------------------------------------------------|
|
||||
* | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move |
|
||||
* | {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
|
||||
* | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
|
||||
* | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
|
||||
* | {@link ng.directive:ngIf#animations ngIf} | enter and leave |
|
||||
* | {@link ng.directive:ngShow#animations ngClass} | add and remove |
|
||||
* | {@link ng.directive:ngShow#animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
||||
*
|
||||
* You can find out more information about animations upon visiting each directive page.
|
||||
*
|
||||
* Below is an example of how to apply animations to a directive that supports animation hooks:
|
||||
*
|
||||
* <pre>
|
||||
* <style type="text/css">
|
||||
* .slide.ng-enter > div,
|
||||
* .slide.ng-leave > div {
|
||||
* -webkit-transition:0.5s linear all;
|
||||
* -moz-transition:0.5s linear all;
|
||||
* -o-transition:0.5s linear all;
|
||||
* transition:0.5s linear all;
|
||||
* }
|
||||
*
|
||||
* .slide.ng-enter { } /* starting animations for enter */
|
||||
* .slide.ng-enter-active { } /* terminal animations for enter */
|
||||
* .slide.ng-leave { } /* starting animations for leave */
|
||||
* .slide.ng-leave-active { } /* terminal animations for leave */
|
||||
* </style>
|
||||
*
|
||||
* <!--
|
||||
* the animate service will automatically add .ng-enter and .ng-leave to the element
|
||||
* to trigger the CSS transition/animations
|
||||
* -->
|
||||
* <ANY class="slide" ng-include="..."></ANY>
|
||||
* </pre>
|
||||
*
|
||||
* Keep in mind that if an animation is running, any child elements cannot be animated until the parent element's
|
||||
* animation has completed.
|
||||
*
|
||||
* <h2>CSS-defined Animations</h2>
|
||||
* The animate service will automatically apply two CSS classes to the animated element and these two CSS classes
|
||||
* are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported
|
||||
* and can be used to play along with this naming structure.
|
||||
*
|
||||
* The following code below demonstrates how to perform animations using **CSS transitions** with Angular:
|
||||
*
|
||||
* <pre>
|
||||
* <style type="text/css">
|
||||
* /*
|
||||
* The animate class is apart of the element and the ng-enter class
|
||||
* is attached to the element once the enter animation event is triggered
|
||||
* */
|
||||
* .reveal-animation.ng-enter {
|
||||
* -webkit-transition: 1s linear all; /* Safari/Chrome */
|
||||
* -moz-transition: 1s linear all; /* Firefox */
|
||||
* -o-transition: 1s linear all; /* Opera */
|
||||
* transition: 1s linear all; /* IE10+ and Future Browsers */
|
||||
*
|
||||
* /* The animation preparation code */
|
||||
* opacity: 0;
|
||||
* }
|
||||
*
|
||||
* /*
|
||||
* Keep in mind that you want to combine both CSS
|
||||
* classes together to avoid any CSS-specificity
|
||||
* conflicts
|
||||
* */
|
||||
* .reveal-animation.ng-enter.ng-enter-active {
|
||||
* /* The animation code itself */
|
||||
* opacity: 1;
|
||||
* }
|
||||
* </style>
|
||||
*
|
||||
* <div class="view-container">
|
||||
* <div ng-view class="reveal-animation"></div>
|
||||
* </div>
|
||||
* </pre>
|
||||
*
|
||||
* The following code below demonstrates how to perform animations using **CSS animations** with Angular:
|
||||
*
|
||||
* <pre>
|
||||
* <style type="text/css">
|
||||
* .reveal-animation.ng-enter {
|
||||
* -webkit-animation: enter_sequence 1s linear; /* Safari/Chrome */
|
||||
* -moz-animation: enter_sequence 1s linear; /* Firefox */
|
||||
* -o-animation: enter_sequence 1s linear; /* Opera */
|
||||
* animation: enter_sequence 1s linear; /* IE10+ and Future Browsers */
|
||||
* }
|
||||
* @-webkit-keyframes enter_sequence {
|
||||
* from { opacity:0; }
|
||||
* to { opacity:1; }
|
||||
* }
|
||||
* @-moz-keyframes enter_sequence {
|
||||
* from { opacity:0; }
|
||||
* to { opacity:1; }
|
||||
* }
|
||||
* @-o-keyframes enter_sequence {
|
||||
* from { opacity:0; }
|
||||
* to { opacity:1; }
|
||||
* }
|
||||
* @keyframes enter_sequence {
|
||||
* from { opacity:0; }
|
||||
* to { opacity:1; }
|
||||
* }
|
||||
* </style>
|
||||
*
|
||||
* <div class="view-container">
|
||||
* <div ng-view class="reveal-animation"></div>
|
||||
* </div>
|
||||
* </pre>
|
||||
*
|
||||
* Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing.
|
||||
*
|
||||
* Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add
|
||||
* the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically
|
||||
* detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be
|
||||
* removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end
|
||||
* immediately resulting in a DOM element that is at its final state. This final state is when the DOM element
|
||||
* has no CSS transition/animation classes applied to it.
|
||||
*
|
||||
* <h2>JavaScript-defined Animations</h2>
|
||||
* In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not
|
||||
* yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module.
|
||||
*
|
||||
* <pre>
|
||||
* //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application.
|
||||
* var ngModule = angular.module('YourApp', []);
|
||||
* ngModule.animation('.my-crazy-animation', function() {
|
||||
* return {
|
||||
* enter: function(element, done) {
|
||||
* //run the animation
|
||||
* //!annotate Cancel Animation|This function (if provided) will perform the cancellation of the animation when another is triggered
|
||||
* return function(element, done) {
|
||||
* //cancel the animation
|
||||
* }
|
||||
* }
|
||||
* leave: function(element, done) { },
|
||||
* move: function(element, done) { },
|
||||
* show: function(element, done) { },
|
||||
* hide: function(element, done) { },
|
||||
* addClass: function(element, className, done) { },
|
||||
* removeClass: function(element, className, done) { },
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run
|
||||
* a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits
|
||||
* the element's CSS class attribute value and then run the matching animation event function (if found).
|
||||
* In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function
|
||||
* be executed. It should be also noted that only simple class selectors are allowed.
|
||||
*
|
||||
* Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned.
|
||||
* As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run,
|
||||
* and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation
|
||||
* or transition code that is defined via a stylesheet).
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('ngAnimate', ['ng'])
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngAnimate.$animateProvider
|
||||
* @description
|
||||
*
|
||||
* The `$AnimationProvider` allows developers to register and access custom JavaScript animations directly inside
|
||||
* of a module. When an animation is triggered, the $animate service will query the $animation function to find any
|
||||
* animations that match the provided name value.
|
||||
*
|
||||
* Requires the {@link ngAnimate `ngAnimate`} module to be installed.
|
||||
*
|
||||
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
|
||||
*
|
||||
*/
|
||||
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
|
||||
var noop = angular.noop;
|
||||
var forEach = angular.forEach;
|
||||
var selectors = $animateProvider.$$selectors;
|
||||
|
||||
var NG_ANIMATE_STATE = '$$ngAnimateState';
|
||||
var rootAnimateState = {running:true};
|
||||
$provide.decorator('$animate', ['$delegate', '$injector', '$sniffer', '$rootElement', '$timeout', '$rootScope',
|
||||
function($delegate, $injector, $sniffer, $rootElement, $timeout, $rootScope) {
|
||||
|
||||
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||
|
||||
function lookup(name) {
|
||||
if (name) {
|
||||
var matches = [],
|
||||
flagMap = {},
|
||||
classes = name.substr(1).split('.');
|
||||
|
||||
//the empty string value is the default animation
|
||||
//operation which performs CSS transition and keyframe
|
||||
//animations sniffing. This is always included for each
|
||||
//element animation procedure
|
||||
classes.push('');
|
||||
|
||||
for(var i=0; i < classes.length; i++) {
|
||||
var klass = classes[i],
|
||||
selectorFactoryName = selectors[klass];
|
||||
if(selectorFactoryName && !flagMap[klass]) {
|
||||
matches.push($injector.get(selectorFactoryName));
|
||||
flagMap[klass] = true;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngAnimate.$animate
|
||||
* @requires $timeout, $sniffer, $rootElement
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move)
|
||||
* as well as during addClass and removeClass operations. When any of these operations are run, the $animate service
|
||||
* will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object)
|
||||
* as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run.
|
||||
*
|
||||
* The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives
|
||||
* will work out of the box without any extra configuration.
|
||||
*
|
||||
* Requires the {@link ngAnimate `ngAnimate`} module to be installed.
|
||||
*
|
||||
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
|
||||
*
|
||||
*/
|
||||
return {
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ngAnimate.$animate#enter
|
||||
* @methodOf ngAnimate.$animate
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Appends the element to the parent element that resides in the document and then runs the enter animation. Once
|
||||
* the animation is started, the following CSS classes will be present on the element for the duration of the animation:
|
||||
*
|
||||
* Below is a breakdown of each step that occurs during enter animation:
|
||||
*
|
||||
* | Animation Step | What the element class attribute looks like |
|
||||
* |----------------------------------------------------------------------------------------------|-----------------------------------------------|
|
||||
* | 1. $animate.enter(...) is called | class="my-animation" |
|
||||
* | 2. element is inserted into the parent element or beside the after element | class="my-animation" |
|
||||
* | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation" |
|
||||
* | 4. the .ng-enter class is added to the element | class="my-animation ng-enter" |
|
||||
* | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-enter" |
|
||||
* | 6. the .ng-enter-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-enter ng-enter-active" |
|
||||
* | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-enter ng-enter-active" |
|
||||
* | 8. The animation ends and both CSS classes are removed from the element | class="my-animation" |
|
||||
* | 9. The done() callback is fired (if provided) | class="my-animation" |
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be the focus of the enter animation
|
||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the enter animation
|
||||
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the enter animation
|
||||
* @param {function()=} done callback function that will be called once the animation is complete
|
||||
*/
|
||||
enter : function(element, parent, after, done) {
|
||||
$delegate.enter(element, parent, after);
|
||||
$rootScope.$$postDigest(function() {
|
||||
performAnimation('enter', 'ng-enter', element, parent, after, function() {
|
||||
done && $timeout(done, 0, false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ngAnimate.$animate#leave
|
||||
* @methodOf ngAnimate.$animate
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Runs the leave animation operation and, upon completion, removes the element from the DOM. Once
|
||||
* the animation is started, the following CSS classes will be added for the duration of the animation:
|
||||
*
|
||||
* Below is a breakdown of each step that occurs during enter animation:
|
||||
*
|
||||
* | Animation Step | What the element class attribute looks like |
|
||||
* |----------------------------------------------------------------------------------------------|----------------------------------------------|
|
||||
* | 1. $animate.leave(...) is called | class="my-animation" |
|
||||
* | 2. $animate runs any JavaScript-defined animations on the element | class="my-animation" |
|
||||
* | 3. the .ng-leave class is added to the element | class="my-animation ng-leave" |
|
||||
* | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-leave" |
|
||||
* | 5. the .ng-leave-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-leave ng-leave-active |
|
||||
* | 6. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-leave ng-leave-active |
|
||||
* | 7. The animation ends and both CSS classes are removed from the element | class="my-animation" |
|
||||
* | 8. The element is removed from the DOM | ... |
|
||||
* | 9. The done() callback is fired (if provided) | ... |
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be the focus of the leave animation
|
||||
* @param {function()=} done callback function that will be called once the animation is complete
|
||||
*/
|
||||
leave : function(element, done) {
|
||||
$rootScope.$$postDigest(function() {
|
||||
performAnimation('leave', 'ng-leave', element, null, null, function() {
|
||||
$delegate.leave(element, done);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ngAnimate.$animate#move
|
||||
* @methodOf ngAnimate.$animate
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parent container or
|
||||
* add the element directly after the after element if present. Then the move animation will be run. Once
|
||||
* the animation is started, the following CSS classes will be added for the duration of the animation:
|
||||
*
|
||||
* Below is a breakdown of each step that occurs during move animation:
|
||||
*
|
||||
* | Animation Step | What the element class attribute looks like |
|
||||
* |----------------------------------------------------------------------------------------------|---------------------------------------------|
|
||||
* | 1. $animate.move(...) is called | class="my-animation" |
|
||||
* | 2. element is moved into the parent element or beside the after element | class="my-animation" |
|
||||
* | 3. $animate runs any JavaScript-defined animations on the element | class="my-animation" |
|
||||
* | 4. the .ng-move class is added to the element | class="my-animation ng-move" |
|
||||
* | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-move" |
|
||||
* | 6. the .ng-move-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-move ng-move-active" |
|
||||
* | 7. $animate waits for X milliseconds for the animation to complete | class="my-animation ng-move ng-move-active" |
|
||||
* | 8. The animation ends and both CSS classes are removed from the element | class="my-animation" |
|
||||
* | 9. The done() callback is fired (if provided) | class="my-animation" |
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be the focus of the move animation
|
||||
* @param {jQuery/jqLite element} parent the parent element of the element that will be the focus of the move animation
|
||||
* @param {jQuery/jqLite element} after the sibling element (which is the previous element) of the element that will be the focus of the move animation
|
||||
* @param {function()=} done callback function that will be called once the animation is complete
|
||||
*/
|
||||
move : function(element, parent, after, done) {
|
||||
$delegate.move(element, parent, after);
|
||||
$rootScope.$$postDigest(function() {
|
||||
performAnimation('move', 'ng-move', element, null, null, function() {
|
||||
done && $timeout(done, 0, false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ngAnimate.$animate#addClass
|
||||
* @methodOf ngAnimate.$animate
|
||||
*
|
||||
* @description
|
||||
* Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class.
|
||||
* Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide
|
||||
* the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions
|
||||
* or keyframes are defined on the -add CSS class).
|
||||
*
|
||||
* Below is a breakdown of each step that occurs during addClass animation:
|
||||
*
|
||||
* | Animation Step | What the element class attribute looks like |
|
||||
* |------------------------------------------------------------------------------------------------|---------------------------------------------|
|
||||
* | 1. $animate.addClass(element, 'super') is called | class="" |
|
||||
* | 2. $animate runs any JavaScript-defined animations on the element | class="" |
|
||||
* | 3. the .super-add class is added to the element | class="super-add" |
|
||||
* | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="super-add" |
|
||||
* | 5. the .super-add-active class is added (this triggers the CSS transition/animation) | class="super-add super-add-active" |
|
||||
* | 6. $animate waits for X milliseconds for the animation to complete | class="super-add super-add-active" |
|
||||
* | 7. The animation ends and both CSS classes are removed from the element | class="" |
|
||||
* | 8. The super class is added to the element | class="super" |
|
||||
* | 9. The done() callback is fired (if provided) | class="super" |
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be animated
|
||||
* @param {string} className the CSS class that will be animated and then attached to the element
|
||||
* @param {function()=} done callback function that will be called once the animation is complete
|
||||
*/
|
||||
addClass : function(element, className, done) {
|
||||
performAnimation('addClass', className, element, null, null, function() {
|
||||
$delegate.addClass(element, className, done);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ngAnimate.$animate#removeClass
|
||||
* @methodOf ngAnimate.$animate
|
||||
*
|
||||
* @description
|
||||
* Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value
|
||||
* from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in
|
||||
* order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if
|
||||
* no CSS transitions or keyframes are defined on the -remove CSS class).
|
||||
*
|
||||
* Below is a breakdown of each step that occurs during removeClass animation:
|
||||
*
|
||||
* | Animation Step | What the element class attribute looks like |
|
||||
* |-----------------------------------------------------------------------------------------------|-------------------------------------------------|
|
||||
* | 1. $animate.removeClass(element, 'super') is called | class="super" |
|
||||
* | 2. $animate runs any JavaScript-defined animations on the element | class="super" |
|
||||
* | 3. the .super-remove class is added to the element | class="super super-remove" |
|
||||
* | 4. $animate scans the element styles to get the CSS transition/animation duration and delay | class="super super-remove" |
|
||||
* | 5. the .super-remove-active class is added (this triggers the CSS transition/animation) | class="super super-remove super-remove-active" |
|
||||
* | 6. $animate waits for X milliseconds for the animation to complete | class="super super-remove super-remove-active" |
|
||||
* | 7. The animation ends and both CSS all three classes are removed from the element | class="" |
|
||||
* | 8. The done() callback is fired (if provided) | class="" |
|
||||
*
|
||||
* @param {jQuery/jqLite element} element the element that will be animated
|
||||
* @param {string} className the CSS class that will be animated and then removed from the element
|
||||
* @param {function()=} done callback function that will be called once the animation is complete
|
||||
*/
|
||||
removeClass : function(element, className, done) {
|
||||
performAnimation('removeClass', className, element, null, null, function() {
|
||||
$delegate.removeClass(element, className, done);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ngAnimate.$animate#enabled
|
||||
* @methodOf ngAnimate.$animate
|
||||
* @function
|
||||
*
|
||||
* @param {boolean=} value If provided then set the animation on or off.
|
||||
* @return {boolean} Current animation state.
|
||||
*
|
||||
* @description
|
||||
* Globally enables/disables animations.
|
||||
*
|
||||
*/
|
||||
enabled : function(value) {
|
||||
if (arguments.length) {
|
||||
rootAnimateState.running = !value;
|
||||
}
|
||||
return !rootAnimateState.running;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
all animations call this shared animation triggering function internally.
|
||||
The event variable refers to the JavaScript animation event that will be triggered
|
||||
and the className value is the name of the animation that will be applied within the
|
||||
CSS code. Element, parent and after are provided DOM elements for the animation
|
||||
and the onComplete callback will be fired once the animation is fully complete.
|
||||
*/
|
||||
function performAnimation(event, className, element, parent, after, onComplete) {
|
||||
var classes = (element.attr('class') || '') + ' ' + className;
|
||||
var animationLookup = (' ' + classes).replace(/\s+/g,'.'),
|
||||
animations = [];
|
||||
forEach(lookup(animationLookup), function(animation, index) {
|
||||
animations.push({
|
||||
start : animation[event]
|
||||
});
|
||||
});
|
||||
|
||||
if (!parent) {
|
||||
parent = after ? after.parent() : element.parent();
|
||||
}
|
||||
var disabledAnimation = { running : true };
|
||||
|
||||
//skip the animation if animations are disabled, a parent is already being animated
|
||||
//or the element is not currently attached to the document body.
|
||||
if ((parent.inheritedData(NG_ANIMATE_STATE) || disabledAnimation).running) {
|
||||
//avoid calling done() since there is no need to remove any
|
||||
//data or className values since this happens earlier than that
|
||||
//and also use a timeout so that it won't be asynchronous
|
||||
$timeout(onComplete || noop, 0, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var ngAnimateState = element.data(NG_ANIMATE_STATE) || {};
|
||||
|
||||
//if an animation is currently running on the element then lets take the steps
|
||||
//to cancel that animation and fire any required callbacks
|
||||
if(ngAnimateState.running) {
|
||||
cancelAnimations(ngAnimateState.animations);
|
||||
ngAnimateState.done();
|
||||
}
|
||||
|
||||
element.data(NG_ANIMATE_STATE, {
|
||||
running:true,
|
||||
animations:animations,
|
||||
done:done
|
||||
});
|
||||
|
||||
forEach(animations, function(animation, index) {
|
||||
var fn = function() {
|
||||
progress(index);
|
||||
};
|
||||
|
||||
if(animation.start) {
|
||||
if(event == 'addClass' || event == 'removeClass') {
|
||||
animation.endFn = animation.start(element, className, fn);
|
||||
} else {
|
||||
animation.endFn = animation.start(element, fn);
|
||||
}
|
||||
} else {
|
||||
fn();
|
||||
}
|
||||
});
|
||||
|
||||
function cancelAnimations(animations) {
|
||||
var isCancelledFlag = true;
|
||||
forEach(animations, function(animation) {
|
||||
(animation.endFn || noop)(isCancelledFlag);
|
||||
});
|
||||
}
|
||||
|
||||
function progress(index) {
|
||||
animations[index].done = true;
|
||||
(animations[index].endFn || noop)();
|
||||
for(var i=0;i<animations.length;i++) {
|
||||
if(!animations[i].done) return;
|
||||
}
|
||||
done();
|
||||
}
|
||||
|
||||
function done() {
|
||||
if(!done.hasBeenRun) {
|
||||
done.hasBeenRun = true;
|
||||
element.removeData(NG_ANIMATE_STATE);
|
||||
(onComplete || noop)();
|
||||
}
|
||||
}
|
||||
}
|
||||
}]);
|
||||
|
||||
$animateProvider.register('', ['$window','$sniffer', '$timeout', function($window, $sniffer, $timeout) {
|
||||
var noop = angular.noop;
|
||||
var forEach = angular.forEach;
|
||||
|
||||
//one day all browsers will have these properties
|
||||
var w3cAnimationProp = 'animation';
|
||||
var w3cTransitionProp = 'transition';
|
||||
|
||||
//but some still use vendor-prefixed styles
|
||||
var vendorAnimationProp = $sniffer.vendorPrefix + 'Animation';
|
||||
var vendorTransitionProp = $sniffer.vendorPrefix + 'Transition';
|
||||
|
||||
var durationKey = 'Duration',
|
||||
delayKey = 'Delay',
|
||||
propertyKey = 'Property',
|
||||
animationIterationCountKey = 'IterationCount',
|
||||
ELEMENT_NODE = 1;
|
||||
|
||||
function animate(element, className, done) {
|
||||
if (!($sniffer.transitions || $sniffer.animations)) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
else if(['ng-enter','ng-leave','ng-move'].indexOf(className) == -1) {
|
||||
var existingDuration = 0;
|
||||
forEach(element, function(element) {
|
||||
if (element.nodeType == ELEMENT_NODE) {
|
||||
var elementStyles = $window.getComputedStyle(element) || {};
|
||||
existingDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
|
||||
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]),
|
||||
existingDuration);
|
||||
}
|
||||
});
|
||||
if(existingDuration > 0) {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
element.addClass(className);
|
||||
|
||||
//we want all the styles defined before and after
|
||||
var duration = 0;
|
||||
forEach(element, function(element) {
|
||||
if (element.nodeType == ELEMENT_NODE) {
|
||||
var elementStyles = $window.getComputedStyle(element) || {};
|
||||
|
||||
var transitionDelay = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + delayKey]),
|
||||
parseMaxTime(elementStyles[vendorTransitionProp + delayKey]));
|
||||
|
||||
var animationDelay = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + delayKey]),
|
||||
parseMaxTime(elementStyles[vendorAnimationProp + delayKey]));
|
||||
|
||||
var transitionDuration = Math.max(parseMaxTime(elementStyles[w3cTransitionProp + durationKey]),
|
||||
parseMaxTime(elementStyles[vendorTransitionProp + durationKey]));
|
||||
|
||||
var animationDuration = Math.max(parseMaxTime(elementStyles[w3cAnimationProp + durationKey]),
|
||||
parseMaxTime(elementStyles[vendorAnimationProp + durationKey]));
|
||||
|
||||
if(animationDuration > 0) {
|
||||
animationDuration *= Math.max(parseInt(elementStyles[w3cAnimationProp + animationIterationCountKey]) || 0,
|
||||
parseInt(elementStyles[vendorAnimationProp + animationIterationCountKey]) || 0,
|
||||
1);
|
||||
}
|
||||
|
||||
duration = Math.max(animationDelay + animationDuration,
|
||||
transitionDelay + transitionDuration,
|
||||
duration);
|
||||
}
|
||||
});
|
||||
|
||||
/* there is no point in performing a reflow if the animation
|
||||
timeout is empty (this would cause a flicker bug normally
|
||||
in the page */
|
||||
if(duration > 0) {
|
||||
var node = element[0];
|
||||
|
||||
//temporarily disable the transition so that the enter styles
|
||||
//don't animate twice (this is here to avoid a bug in Chrome/FF).
|
||||
node.style[w3cTransitionProp + propertyKey] = 'none';
|
||||
node.style[vendorTransitionProp + propertyKey] = 'none';
|
||||
|
||||
var activeClassName = '';
|
||||
forEach(className.split(' '), function(klass, i) {
|
||||
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
|
||||
});
|
||||
|
||||
//this triggers a reflow which allows for the transition animation to kick in
|
||||
element.prop('clientWidth');
|
||||
node.style[w3cTransitionProp + propertyKey] = '';
|
||||
node.style[vendorTransitionProp + propertyKey] = '';
|
||||
element.addClass(activeClassName);
|
||||
|
||||
$timeout(done, duration * 1000, false);
|
||||
|
||||
//this will automatically be called by $animate so
|
||||
//there is no need to attach this internally to the
|
||||
//timeout done method
|
||||
return function onEnd(cancelled) {
|
||||
element.removeClass(className);
|
||||
element.removeClass(activeClassName);
|
||||
|
||||
//only when the animation is cancelled is the done()
|
||||
//function not called for this animation therefore
|
||||
//this must be also called
|
||||
if(cancelled) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
element.removeClass(className);
|
||||
done();
|
||||
}
|
||||
|
||||
function parseMaxTime(str) {
|
||||
var total = 0, values = angular.isString(str) ? str.split(/\s*,\s*/) : [];
|
||||
forEach(values, function(value) {
|
||||
total = Math.max(parseFloat(value) || 0, total);
|
||||
});
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
enter : function(element, done) {
|
||||
return animate(element, 'ng-enter', done);
|
||||
},
|
||||
leave : function(element, done) {
|
||||
return animate(element, 'ng-leave', done);
|
||||
},
|
||||
move : function(element, done) {
|
||||
return animate(element, 'ng-move', done);
|
||||
},
|
||||
addClass : function(element, className, done) {
|
||||
return animate(element, suffixClasses(className, '-add'), done);
|
||||
},
|
||||
removeClass : function(element, className, done) {
|
||||
return animate(element, suffixClasses(className, '-remove'), done);
|
||||
}
|
||||
};
|
||||
|
||||
function suffixClasses(classes, suffix) {
|
||||
var className = '';
|
||||
classes = angular.isArray(classes) ? classes : classes.split(/\s+/);
|
||||
forEach(classes, function(klass, i) {
|
||||
if(klass && klass.length > 0) {
|
||||
className += (i > 0 ? ' ' : '') + klass + suffix;
|
||||
}
|
||||
});
|
||||
return className;
|
||||
}
|
||||
}]);
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
871
vendor/angular/1.2.0rc2/angular-route.js
vendored
Normal file
871
vendor/angular/1.2.0rc2/angular-route.js
vendored
Normal file
@ -0,0 +1,871 @@
|
||||
/**
|
||||
* @license AngularJS v1.2.0-rc.2
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {'use strict';
|
||||
|
||||
var copy = angular.copy,
|
||||
equals = angular.equals,
|
||||
extend = angular.extend,
|
||||
forEach = angular.forEach,
|
||||
isDefined = angular.isDefined,
|
||||
isFunction = angular.isFunction,
|
||||
isString = angular.isString,
|
||||
jqLite = angular.element,
|
||||
noop = angular.noop,
|
||||
toJson = angular.toJson;
|
||||
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return extend(new (extend(function() {}, {prototype:parent}))(), extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ngRoute
|
||||
* @description
|
||||
*
|
||||
* # ngRoute
|
||||
*
|
||||
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
|
||||
*
|
||||
* {@installModule route}
|
||||
*
|
||||
*/
|
||||
|
||||
var ngRouteModule = angular.module('ngRoute', ['ng']).
|
||||
provider('$route', $RouteProvider);
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngRoute.$routeProvider
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Used for configuring routes. See {@link ngRoute.$route $route} for an example.
|
||||
*
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*/
|
||||
function $RouteProvider(){
|
||||
var routes = {};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngRoute.$routeProvider#when
|
||||
* @methodOf ngRoute.$routeProvider
|
||||
*
|
||||
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
|
||||
* contains redundant trailing slash or is missing one, the route will still match and the
|
||||
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
|
||||
* route definition.
|
||||
*
|
||||
* * `path` can contain named groups starting with a colon (`:name`). All characters up
|
||||
* to the next slash are matched and stored in `$routeParams` under the given `name`
|
||||
* when the route matches.
|
||||
* * `path` can contain named groups starting with a colon and ending with a star (`:name*`).
|
||||
* All characters are eagerly stored in `$routeParams` under the given `name`
|
||||
* when the route matches.
|
||||
* * `path` can contain optional named groups with a question mark (`:name?`).
|
||||
*
|
||||
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
|
||||
* `/color/brown/largecode/code/with/slashs/edit` and extract:
|
||||
*
|
||||
* * `color: brown`
|
||||
* * `largecode: code/with/slashs`.
|
||||
*
|
||||
*
|
||||
* @param {Object} route Mapping information to be assigned to `$route.current` on route
|
||||
* match.
|
||||
*
|
||||
* Object properties:
|
||||
*
|
||||
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
|
||||
* created scope or the name of a {@link angular.Module#controller registered controller}
|
||||
* if passed as a string.
|
||||
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
|
||||
* published to scope under the `controllerAs` name.
|
||||
* - `template` – `{string=|function()=}` – html template as a string or a function that
|
||||
* returns an html template as a string which should be used by {@link
|
||||
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
|
||||
* This property takes precedence over `templateUrl`.
|
||||
*
|
||||
* If `template` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
|
||||
* template that should be used by {@link ngRoute.directive:ngView ngView}.
|
||||
*
|
||||
* If `templateUrl` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
|
||||
* be injected into the controller. If any of these dependencies are promises, they will be
|
||||
* resolved and converted to a value before the controller is instantiated and the
|
||||
* `$routeChangeSuccess` event is fired. The map object is:
|
||||
*
|
||||
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
|
||||
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
|
||||
* Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
|
||||
* and the return value is treated as the dependency. If the result is a promise, it is resolved
|
||||
* before its value is injected into the controller. Be aware that `ngRoute.$routeParams` will
|
||||
* still refer to the previous route within these resolve functions. Use `$route.current.params`
|
||||
* to access the new route parameters, instead.
|
||||
*
|
||||
* - `redirectTo` – {(string|function())=} – value to update
|
||||
* {@link ng.$location $location} path with and trigger route redirection.
|
||||
*
|
||||
* If `redirectTo` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Object.<string>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route templateUrl.
|
||||
* - `{string}` - current `$location.path()`
|
||||
* - `{Object}` - current `$location.search()`
|
||||
*
|
||||
* The custom `redirectTo` function is expected to return a string which will be used
|
||||
* to update `$location.path()` and `$location.search()`.
|
||||
*
|
||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search()
|
||||
* changes.
|
||||
*
|
||||
* If the option is set to `false` and url in the browser changes, then
|
||||
* `$routeUpdate` event is broadcasted on the root scope.
|
||||
*
|
||||
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
|
||||
*
|
||||
* If the option is set to `true`, then the particular route can be matched without being
|
||||
* case sensitive
|
||||
*
|
||||
* @returns {Object} self
|
||||
*
|
||||
* @description
|
||||
* Adds a new route definition to the `$route` service.
|
||||
*/
|
||||
this.when = function(path, route) {
|
||||
routes[path] = extend(
|
||||
{reloadOnSearch: true},
|
||||
route,
|
||||
path && pathRegExp(path, route)
|
||||
);
|
||||
|
||||
// create redirection for trailing slashes
|
||||
if (path) {
|
||||
var redirectPath = (path[path.length-1] == '/')
|
||||
? path.substr(0, path.length-1)
|
||||
: path +'/';
|
||||
|
||||
routes[redirectPath] = extend(
|
||||
{redirectTo: path},
|
||||
pathRegExp(redirectPath, route)
|
||||
);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param path {string} path
|
||||
* @param opts {Object} options
|
||||
* @return {?Object}
|
||||
*
|
||||
* @description
|
||||
* Normalizes the given path, returning a regular expression
|
||||
* and the original path.
|
||||
*
|
||||
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
|
||||
*/
|
||||
function pathRegExp(path, opts) {
|
||||
var insensitive = opts.caseInsensitiveMatch,
|
||||
ret = {
|
||||
originalPath: path,
|
||||
regexp: path
|
||||
},
|
||||
keys = ret.keys = [];
|
||||
|
||||
path = path
|
||||
.replace(/([().])/g, '\\$1')
|
||||
.replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
|
||||
var optional = option === '?' ? option : null;
|
||||
var star = option === '*' ? option : null;
|
||||
keys.push({ name: key, optional: !!optional });
|
||||
slash = slash || '';
|
||||
return ''
|
||||
+ (optional ? '' : slash)
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (star && '(.+)?' || '([^/]+)?') + ')'
|
||||
+ (optional || '');
|
||||
})
|
||||
.replace(/([\/$\*])/g, '\\$1');
|
||||
|
||||
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngRoute.$routeProvider#otherwise
|
||||
* @methodOf ngRoute.$routeProvider
|
||||
*
|
||||
* @description
|
||||
* Sets route definition that will be used on route change when no other route definition
|
||||
* is matched.
|
||||
*
|
||||
* @param {Object} params Mapping information to be assigned to `$route.current`.
|
||||
* @returns {Object} self
|
||||
*/
|
||||
this.otherwise = function(params) {
|
||||
this.when(null, params);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', '$sce',
|
||||
function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngRoute.$route
|
||||
* @requires $location
|
||||
* @requires $routeParams
|
||||
*
|
||||
* @property {Object} current Reference to the current route definition.
|
||||
* The route definition contains:
|
||||
*
|
||||
* - `controller`: The controller constructor as define in route definition.
|
||||
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
|
||||
* controller instantiation. The `locals` contain
|
||||
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
|
||||
*
|
||||
* - `$scope` - The current route scope.
|
||||
* - `$template` - The current route template HTML.
|
||||
*
|
||||
* @property {Array.<Object>} routes Array of all configured routes.
|
||||
*
|
||||
* @description
|
||||
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
|
||||
* It watches `$location.url()` and tries to map the path to an existing route definition.
|
||||
*
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*
|
||||
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
|
||||
*
|
||||
* The `$route` service is typically used in conjunction with the {@link ngRoute.directive:ngView `ngView`}
|
||||
* directive and the {@link ngRoute.$routeParams `$routeParams`} service.
|
||||
*
|
||||
* @example
|
||||
This example shows how changing the URL hash causes the `$route` to match a route against the
|
||||
URL, and the `ngView` pulls in the partial.
|
||||
|
||||
Note that this example is using {@link ng.directive:script inlined templates}
|
||||
to get it working on jsfiddle as well.
|
||||
|
||||
<example module="ngView" deps="angular-route.js">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCntl">
|
||||
Choose:
|
||||
<a href="Book/Moby">Moby</a> |
|
||||
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
<a href="Book/Gatsby">Gatsby</a> |
|
||||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
||||
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
||||
|
||||
<div ng-view></div>
|
||||
<hr />
|
||||
|
||||
<pre>$location.path() = {{$location.path()}}</pre>
|
||||
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
|
||||
<pre>$route.current.params = {{$route.current.params}}</pre>
|
||||
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
||||
<pre>$routeParams = {{$routeParams}}</pre>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="book.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
</file>
|
||||
|
||||
<file name="chapter.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
Chapter Id: {{params.chapterId}}
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('ngView', ['ngRoute']).config(function($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/Book/:bookId', {
|
||||
templateUrl: 'book.html',
|
||||
controller: BookCntl,
|
||||
resolve: {
|
||||
// I will cause a 1 second delay
|
||||
delay: function($q, $timeout) {
|
||||
var delay = $q.defer();
|
||||
$timeout(delay.resolve, 1000);
|
||||
return delay.promise;
|
||||
}
|
||||
}
|
||||
});
|
||||
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
|
||||
templateUrl: 'chapter.html',
|
||||
controller: ChapterCntl
|
||||
});
|
||||
|
||||
// configure html5 to get links working on jsfiddle
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
function MainCntl($scope, $route, $routeParams, $location) {
|
||||
$scope.$route = $route;
|
||||
$scope.$location = $location;
|
||||
$scope.$routeParams = $routeParams;
|
||||
}
|
||||
|
||||
function BookCntl($scope, $routeParams) {
|
||||
$scope.name = "BookCntl";
|
||||
$scope.params = $routeParams;
|
||||
}
|
||||
|
||||
function ChapterCntl($scope, $routeParams) {
|
||||
$scope.name = "ChapterCntl";
|
||||
$scope.params = $routeParams;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="scenario.js">
|
||||
it('should load and compile correct template', function() {
|
||||
element('a:contains("Moby: Ch1")').click();
|
||||
var content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: ChapterCntl/);
|
||||
expect(content).toMatch(/Book Id\: Moby/);
|
||||
expect(content).toMatch(/Chapter Id\: 1/);
|
||||
|
||||
element('a:contains("Scarlet")').click();
|
||||
sleep(2); // promises are not part of scenario waiting
|
||||
content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: BookCntl/);
|
||||
expect(content).toMatch(/Book Id\: Scarlet/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeChangeStart
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted before a route change. At this point the route services starts
|
||||
* resolving all of the dependencies needed for the route change to occurs.
|
||||
* Typically this involves fetching the view template as well as any dependencies
|
||||
* defined in `resolve` route property. Once all of the dependencies are resolved
|
||||
* `$routeChangeSuccess` is fired.
|
||||
*
|
||||
* @param {Route} next Future route information.
|
||||
* @param {Route} current Current route information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeChangeSuccess
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted after a route dependencies are resolved.
|
||||
* {@link ngRoute.directive:ngView ngView} listens for the directive
|
||||
* to instantiate the controller and render the view.
|
||||
*
|
||||
* @param {Object} angularEvent Synthetic event object.
|
||||
* @param {Route} current Current route information.
|
||||
* @param {Route|Undefined} previous Previous route information, or undefined if current is first route entered.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeChangeError
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
* Broadcasted if any of the resolve promises are rejected.
|
||||
*
|
||||
* @param {Route} current Current route information.
|
||||
* @param {Route} previous Previous route information.
|
||||
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.$route#$routeUpdate
|
||||
* @eventOf ngRoute.$route
|
||||
* @eventType broadcast on root scope
|
||||
* @description
|
||||
*
|
||||
* The `reloadOnSearch` property has been set to false, and we are reusing the same
|
||||
* instance of the Controller.
|
||||
*/
|
||||
|
||||
var forceReload = false,
|
||||
$route = {
|
||||
routes: routes,
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngRoute.$route#reload
|
||||
* @methodOf ngRoute.$route
|
||||
*
|
||||
* @description
|
||||
* Causes `$route` service to reload the current route even if
|
||||
* {@link ng.$location $location} hasn't changed.
|
||||
*
|
||||
* As a result of that, {@link ngRoute.directive:ngView ngView}
|
||||
* creates new scope, reinstantiates the controller.
|
||||
*/
|
||||
reload: function() {
|
||||
forceReload = true;
|
||||
$rootScope.$evalAsync(updateRoute);
|
||||
}
|
||||
};
|
||||
|
||||
$rootScope.$on('$locationChangeSuccess', updateRoute);
|
||||
|
||||
return $route;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @param on {string} current url
|
||||
* @param route {Object} route regexp to match the url against
|
||||
* @return {?Object}
|
||||
*
|
||||
* @description
|
||||
* Check if the route matches the current url.
|
||||
*
|
||||
* Inspired by match in
|
||||
* visionmedia/express/lib/router/router.js.
|
||||
*/
|
||||
function switchRouteMatcher(on, route) {
|
||||
var keys = route.keys,
|
||||
params = {};
|
||||
|
||||
if (!route.regexp) return null;
|
||||
|
||||
var m = route.regexp.exec(on);
|
||||
if (!m) return null;
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
var key = keys[i - 1];
|
||||
|
||||
var val = 'string' == typeof m[i]
|
||||
? decodeURIComponent(m[i])
|
||||
: m[i];
|
||||
|
||||
if (key && val) {
|
||||
params[key.name] = val;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function updateRoute() {
|
||||
var next = parseRoute(),
|
||||
last = $route.current;
|
||||
|
||||
if (next && last && next.$$route === last.$$route
|
||||
&& equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) {
|
||||
last.params = next.params;
|
||||
copy(last.params, $routeParams);
|
||||
$rootScope.$broadcast('$routeUpdate', last);
|
||||
} else if (next || last) {
|
||||
forceReload = false;
|
||||
$rootScope.$broadcast('$routeChangeStart', next, last);
|
||||
$route.current = next;
|
||||
if (next) {
|
||||
if (next.redirectTo) {
|
||||
if (isString(next.redirectTo)) {
|
||||
$location.path(interpolate(next.redirectTo, next.params)).search(next.params)
|
||||
.replace();
|
||||
} else {
|
||||
$location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
|
||||
.replace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$q.when(next).
|
||||
then(function() {
|
||||
if (next) {
|
||||
var locals = extend({}, next.resolve),
|
||||
template, templateUrl;
|
||||
|
||||
forEach(locals, function(value, key) {
|
||||
locals[key] = isString(value) ? $injector.get(value) : $injector.invoke(value);
|
||||
});
|
||||
|
||||
if (isDefined(template = next.template)) {
|
||||
if (isFunction(template)) {
|
||||
template = template(next.params);
|
||||
}
|
||||
} else if (isDefined(templateUrl = next.templateUrl)) {
|
||||
if (isFunction(templateUrl)) {
|
||||
templateUrl = templateUrl(next.params);
|
||||
}
|
||||
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
|
||||
if (isDefined(templateUrl)) {
|
||||
next.loadedTemplateUrl = templateUrl;
|
||||
template = $http.get(templateUrl, {cache: $templateCache}).
|
||||
then(function(response) { return response.data; });
|
||||
}
|
||||
}
|
||||
if (isDefined(template)) {
|
||||
locals['$template'] = template;
|
||||
}
|
||||
return $q.all(locals);
|
||||
}
|
||||
}).
|
||||
// after route change
|
||||
then(function(locals) {
|
||||
if (next == $route.current) {
|
||||
if (next) {
|
||||
next.locals = locals;
|
||||
copy(next.params, $routeParams);
|
||||
}
|
||||
$rootScope.$broadcast('$routeChangeSuccess', next, last);
|
||||
}
|
||||
}, function(error) {
|
||||
if (next == $route.current) {
|
||||
$rootScope.$broadcast('$routeChangeError', next, last, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @returns the current active route, by matching it against the URL
|
||||
*/
|
||||
function parseRoute() {
|
||||
// Match a route
|
||||
var params, match;
|
||||
forEach(routes, function(route, path) {
|
||||
if (!match && (params = switchRouteMatcher($location.path(), route))) {
|
||||
match = inherit(route, {
|
||||
params: extend({}, $location.search(), params),
|
||||
pathParams: params});
|
||||
match.$$route = route;
|
||||
}
|
||||
});
|
||||
// No route matched; fallback to "otherwise" route
|
||||
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns interpolation of the redirect path with the parameters
|
||||
*/
|
||||
function interpolate(string, params) {
|
||||
var result = [];
|
||||
forEach((string||'').split(':'), function(segment, i) {
|
||||
if (i === 0) {
|
||||
result.push(segment);
|
||||
} else {
|
||||
var segmentMatch = segment.match(/(\w+)(.*)/);
|
||||
var key = segmentMatch[1];
|
||||
result.push(params[key]);
|
||||
result.push(segmentMatch[2] || '');
|
||||
delete params[key];
|
||||
}
|
||||
});
|
||||
return result.join('');
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ngRoute.$routeParams
|
||||
* @requires $route
|
||||
*
|
||||
* @description
|
||||
* The `$routeParams` service allows you to retrieve the current set of route parameters.
|
||||
*
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*
|
||||
* The route parameters are a combination of {@link ng.$location `$location`}'s
|
||||
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
|
||||
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
|
||||
*
|
||||
* In case of parameter name collision, `path` params take precedence over `search` params.
|
||||
*
|
||||
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
|
||||
* (but its properties will likely change) even when a route change occurs.
|
||||
*
|
||||
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
|
||||
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
|
||||
* Instead you can use `$route.current.params` to access the new route's parameters.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* // Given:
|
||||
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
|
||||
* // Route: /Chapter/:chapterId/Section/:sectionId
|
||||
* //
|
||||
* // Then
|
||||
* $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
|
||||
* </pre>
|
||||
*/
|
||||
function $RouteParamsProvider() {
|
||||
this.$get = function() { return {}; };
|
||||
}
|
||||
|
||||
ngRouteModule.directive('ngView', ngViewFactory);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngRoute.directive:ngView
|
||||
* @restrict ECA
|
||||
*
|
||||
* @description
|
||||
* # Overview
|
||||
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
|
||||
* including the rendered template of the current route into the main layout (`index.html`) file.
|
||||
* Every time the current route changes, the included view changes with it according to the
|
||||
* configuration of the `$route` service.
|
||||
*
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*
|
||||
* @animations
|
||||
* enter - animation is used to bring new content into the browser.
|
||||
* leave - animation is used to animate existing content away.
|
||||
*
|
||||
* The enter and leave animation occur concurrently.
|
||||
*
|
||||
* @scope
|
||||
* @example
|
||||
<example module="ngViewExample" deps="angular-route.js" animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCntl as main">
|
||||
Choose:
|
||||
<a href="Book/Moby">Moby</a> |
|
||||
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
<a href="Book/Gatsby">Gatsby</a> |
|
||||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
||||
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
||||
|
||||
<div class="example-animate-container">
|
||||
<div ng-view class="view-example"></div>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<pre>$location.path() = {{main.$location.path()}}</pre>
|
||||
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
|
||||
<pre>$route.current.params = {{main.$route.current.params}}</pre>
|
||||
<pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
|
||||
<pre>$routeParams = {{main.$routeParams}}</pre>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="book.html">
|
||||
<div>
|
||||
controller: {{book.name}}<br />
|
||||
Book Id: {{book.params.bookId}}<br />
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="chapter.html">
|
||||
<div>
|
||||
controller: {{chapter.name}}<br />
|
||||
Book Id: {{chapter.params.bookId}}<br />
|
||||
Chapter Id: {{chapter.params.chapterId}}
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="animations.css">
|
||||
.example-animate-container {
|
||||
position:relative;
|
||||
background:white;
|
||||
border:1px solid black;
|
||||
height:40px;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.example-animate-container > div {
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.view-example.ng-enter, .view-example.ng-leave {
|
||||
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
|
||||
|
||||
display:block;
|
||||
width:100%;
|
||||
border-left:1px solid black;
|
||||
|
||||
position:absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
right:0;
|
||||
bottom:0;
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.example-animate-container {
|
||||
position:relative;
|
||||
height:100px;
|
||||
}
|
||||
|
||||
.view-example.ng-enter {
|
||||
left:100%;
|
||||
}
|
||||
.view-example.ng-enter.ng-enter-active {
|
||||
left:0;
|
||||
}
|
||||
|
||||
.view-example.ng-leave { }
|
||||
.view-example.ng-leave.ng-leave-active {
|
||||
left:-100%;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'], function($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/Book/:bookId', {
|
||||
templateUrl: 'book.html',
|
||||
controller: BookCntl,
|
||||
controllerAs: 'book'
|
||||
});
|
||||
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
|
||||
templateUrl: 'chapter.html',
|
||||
controller: ChapterCntl,
|
||||
controllerAs: 'chapter'
|
||||
});
|
||||
|
||||
// configure html5 to get links working on jsfiddle
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
function MainCntl($route, $routeParams, $location) {
|
||||
this.$route = $route;
|
||||
this.$location = $location;
|
||||
this.$routeParams = $routeParams;
|
||||
}
|
||||
|
||||
function BookCntl($routeParams) {
|
||||
this.name = "BookCntl";
|
||||
this.params = $routeParams;
|
||||
}
|
||||
|
||||
function ChapterCntl($routeParams) {
|
||||
this.name = "ChapterCntl";
|
||||
this.params = $routeParams;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="scenario.js">
|
||||
it('should load and compile correct template', function() {
|
||||
element('a:contains("Moby: Ch1")').click();
|
||||
var content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: ChapterCntl/);
|
||||
expect(content).toMatch(/Book Id\: Moby/);
|
||||
expect(content).toMatch(/Chapter Id\: 1/);
|
||||
|
||||
element('a:contains("Scarlet")').click();
|
||||
content = element('.doc-example-live [ng-view]').text();
|
||||
expect(content).toMatch(/controller\: BookCntl/);
|
||||
expect(content).toMatch(/Book Id\: Scarlet/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngRoute.directive:ngView#$viewContentLoaded
|
||||
* @eventOf ngRoute.directive:ngView
|
||||
* @eventType emit on the current ngView scope
|
||||
* @description
|
||||
* Emitted every time the ngView content is reloaded.
|
||||
*/
|
||||
ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate'];
|
||||
function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
priority: 1000,
|
||||
transclude: 'element',
|
||||
compile: function(element, attr, linker) {
|
||||
return function(scope, $element, attr) {
|
||||
var currentScope,
|
||||
currentElement,
|
||||
onloadExp = attr.onload || '';
|
||||
|
||||
scope.$on('$routeChangeSuccess', update);
|
||||
update();
|
||||
|
||||
function cleanupLastView() {
|
||||
if (currentScope) {
|
||||
currentScope.$destroy();
|
||||
currentScope = null;
|
||||
}
|
||||
if(currentElement) {
|
||||
$animate.leave(currentElement);
|
||||
currentElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
function update() {
|
||||
var locals = $route.current && $route.current.locals,
|
||||
template = locals && locals.$template;
|
||||
|
||||
if (template) {
|
||||
var newScope = scope.$new();
|
||||
linker(newScope, function(clone) {
|
||||
cleanupLastView();
|
||||
|
||||
clone.html(template);
|
||||
$animate.enter(clone, null, $element);
|
||||
|
||||
var link = $compile(clone.contents()),
|
||||
current = $route.current;
|
||||
|
||||
currentScope = current.scope = newScope;
|
||||
currentElement = clone;
|
||||
|
||||
if (current.controller) {
|
||||
locals.$scope = currentScope;
|
||||
var controller = $controller(current.controller, locals);
|
||||
if (current.controllerAs) {
|
||||
currentScope[current.controllerAs] = controller;
|
||||
}
|
||||
clone.data('$ngControllerController', controller);
|
||||
clone.contents().data('$ngControllerController', controller);
|
||||
}
|
||||
|
||||
link(currentScope);
|
||||
currentScope.$emit('$viewContentLoaded');
|
||||
currentScope.$eval(onloadExp);
|
||||
|
||||
// $anchorScroll might listen on event...
|
||||
$anchorScroll();
|
||||
});
|
||||
} else {
|
||||
cleanupLastView();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
Reference in New Issue
Block a user