Toderp working with login and signup!

This commit is contained in:
Max Lynch
2013-09-10 20:37:41 -05:00
parent fc1a8270c0
commit 48d4c32173
11 changed files with 1804 additions and 133 deletions

View File

@ -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;
}

View File

@ -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>

View File

@ -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');
}
}
});
*/

View File

View File

@ -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);
}

View File

@ -1,4 +1,4 @@
angular.module('toderp', [])
angular.module('toderp', ['firebase'])
.factory('TaskStorageService', function() {
return {

View File

@ -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>

View 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);
};
});

View File

@ -133,6 +133,7 @@
line-height: $bar-button-line-height;
font-size: 12px;
padding: 4px 12px;
z-index: 1;
}
.button-bar {

View 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 { } /&#42; starting animations for enter &#42;/
* .slide.ng-enter-active { } /&#42; terminal animations for enter &#42;/
* .slide.ng-leave { } /&#42; starting animations for leave &#42;/
* .slide.ng-leave-active { } /&#42; terminal animations for leave &#42;/
* </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">
* /&#42;
* 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
* &#42;/
* .reveal-animation.ng-enter {
* -webkit-transition: 1s linear all; /&#42; Safari/Chrome &#42;/
* -moz-transition: 1s linear all; /&#42; Firefox &#42;/
* -o-transition: 1s linear all; /&#42; Opera &#42;/
* transition: 1s linear all; /&#42; IE10+ and Future Browsers &#42;/
*
* /&#42; The animation preparation code &#42;/
* opacity: 0;
* }
*
* /&#42;
* Keep in mind that you want to combine both CSS
* classes together to avoid any CSS-specificity
* conflicts
* &#42;/
* .reveal-animation.ng-enter.ng-enter-active {
* /&#42; The animation code itself &#42;/
* 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; /&#42; Safari/Chrome &#42;/
* -moz-animation: enter_sequence 1s linear; /&#42; Firefox &#42;/
* -o-animation: enter_sequence 1s linear; /&#42; Opera &#42;/
* animation: enter_sequence 1s linear; /&#42; IE10+ and Future Browsers &#42;/
* }
* &#64-webkit-keyframes enter_sequence {
* from { opacity:0; }
* to { opacity:1; }
* }
* &#64-moz-keyframes enter_sequence {
* from { opacity:0; }
* to { opacity:1; }
* }
* &#64-o-keyframes enter_sequence {
* from { opacity:0; }
* to { opacity:1; }
* }
* &#64keyframes 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
View 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);