Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07ee29c563 | |||
| 9f5d0cf79f | |||
| 1413328e6a | |||
| 7d09bd30f9 | |||
| dde1b29497 | |||
| 4ae3184c59 | |||
| ed53100a0d | |||
| 6df598d9f5 | |||
| 4aa9df7a7a | |||
| 7d5d62dafe | |||
| 524650a40e | |||
| 02a45826f1 | |||
| e324c14907 | |||
| e1cfb1957f | |||
| 2a3586381f | |||
| 834d316829 | |||
| c61be8d0e6 | |||
| 465212835f | |||
| b3acddea37 | |||
| 308598795a | |||
| 2cd09c9f0e | |||
| 34fee06ca7 | |||
| c7a46d4b8a | |||
| de065f1961 | |||
| c3ab915d2e | |||
| f4a4f42abb | |||
| b2c84ccde3 | |||
| 2b344dbd20 | |||
| cde840fdf8 | |||
| f9656dab2d | |||
| a0d759c613 | |||
| 0d421f093d | |||
| 5f937e54df | |||
| 0f9a1c21e6 | |||
| ece7854972 | |||
| 8ace8073fd | |||
| 43a2f3d0bf | |||
| a9cccbe14f | |||
| 36c9e42de2 | |||
| 9f566db33c | |||
| c77b2bcca3 | |||
| 5a4145fe16 | |||
| 039b990d8d | |||
| 5a9cb8be3f | |||
| 63cd873fef | |||
| 69452fa94f | |||
| 2ed4ad5502 | |||
| 1d2a388830 | |||
| ac05276a51 | |||
| cb9c0f200a | |||
| b1d676b7f7 | |||
| 9ddef840b6 | |||
| 28fc80bba0 | |||
| b6c42d5e81 | |||
| 1c045f1b46 | |||
| 95e1b2d612 | |||
| 75345e3487 | |||
| f4fe28bd92 | |||
| ace13b94e6 | |||
| 5df7e73adf | |||
| e115342fce | |||
| e89150ca0f | |||
| 9693a426e3 | |||
| 162485d303 | |||
| affcbad501 | |||
| 7e916455b3 | |||
| cdc4d485a6 | |||
| c894470d41 | |||
| 1b0718bf89 | |||
| 53fd24ffcb | |||
| eb90672aae | |||
| 28cfd96fdc | |||
| 99d5defb1a | |||
| efbc242875 | |||
| d4d58f287f | |||
| dc89db33df | |||
| 5dc27959d5 | |||
| 4c21355940 | |||
| 6f6cb5c8d8 | |||
| 821ed310a7 | |||
| a7aa4cc0a9 | |||
| e0ce9ed36d | |||
| caeb740265 | |||
| 1bb33cccbe | |||
| d9ed9c5ac1 | |||
| 00cac6ed10 | |||
| 2e9d7cc6cb | |||
| 32cc6cbb6f | |||
| 3b1a4fe0c8 | |||
| 9569778f2f | |||
| 0f61316b24 | |||
| 86151b0cea | |||
| 1b7a6c66f8 | |||
| 0ef76dde41 |
@@ -1,3 +1,74 @@
|
||||
<a name="1.2.9"></a>
|
||||
# 1.2.9 enchanted-articulacy (2014-01-15)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure the final closing timeout respects staggering animations
|
||||
([ed53100a](https://github.com/angular/angular.js/commit/ed53100a0dbc9119d5dfc8b7248845d4f6989df2))
|
||||
- prevent race conditions for class-based animations when animating on the same CSS class
|
||||
([4aa9df7a](https://github.com/angular/angular.js/commit/4aa9df7a7ae533531dfae1e3eb9646245d6b5ff4),
|
||||
[#5588](https://github.com/angular/angular.js/issues/5588))
|
||||
- correctly detect and handle CSS transition changes during class addition and removal
|
||||
([7d5d62da](https://github.com/angular/angular.js/commit/7d5d62dafe11620082c79da35958f8014eeb008c))
|
||||
- avoid accidentally matching substrings when resolving the presence of className tokens
|
||||
([524650a4](https://github.com/angular/angular.js/commit/524650a40ed20f01571e5466475749874ee67288))
|
||||
- **$http:** ensure default headers PUT and POST are different objects
|
||||
([e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
|
||||
[#5742](https://github.com/angular/angular.js/issues/5742), [#5747](https://github.com/angular/angular.js/issues/5747), [#5764](https://github.com/angular/angular.js/issues/5764))
|
||||
- **$rootScope:** prevent infinite $digest by checking if asyncQueue is empty when decrementing ttl
|
||||
([2cd09c9f](https://github.com/angular/angular.js/commit/2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f),
|
||||
[#2622](https://github.com/angular/angular.js/issues/2622))
|
||||
- **$route:** update current route upon $route instantiation
|
||||
([2b344dbd](https://github.com/angular/angular.js/commit/2b344dbd20777fb1283b3a5bcf35a6ae8d09469d),
|
||||
[#4957](https://github.com/angular/angular.js/issues/4957))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$animate:**
|
||||
- provide support for DOM callbacks
|
||||
([dde1b294](https://github.com/angular/angular.js/commit/dde1b2949727c297e214c99960141bfad438d7a4))
|
||||
- use requestAnimationFrame instead of a timeout to issue a reflow
|
||||
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
|
||||
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
|
||||
|
||||
<a name="1.2.8"></a>
|
||||
# 1.2.8 interdimensional-cartography (2014-01-10)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$http:**
|
||||
- return responseText on IE8 for requests with responseType set
|
||||
([a9cccbe1](https://github.com/angular/angular.js/commit/a9cccbe14f1bd9048f5dab4443f58c804d4259a1),
|
||||
[#4464](https://github.com/angular/angular.js/issues/4464), [#4738](https://github.com/angular/angular.js/issues/4738), [#5636](https://github.com/angular/angular.js/issues/5636))
|
||||
- Allow status code 0 from any protocol
|
||||
([28fc80bb](https://github.com/angular/angular.js/commit/28fc80bba0107075ab371fd0a7634a38891626b2),
|
||||
[#1356](https://github.com/angular/angular.js/issues/1356), [#5547](https://github.com/angular/angular.js/issues/5547))
|
||||
- cancelled JSONP requests will not print error in the console
|
||||
([95e1b2d6](https://github.com/angular/angular.js/commit/95e1b2d6121b4e26cf87dcf6746a7b8cb4c25e7f),
|
||||
[#5615](https://github.com/angular/angular.js/issues/5615), [#5616](https://github.com/angular/angular.js/issues/5616))
|
||||
- **$location:** return '/' for root path in hashbang mode
|
||||
([63cd873f](https://github.com/angular/angular.js/commit/63cd873fef3207deef30c7a7ed66f4b8f647dc12),
|
||||
[#5650](https://github.com/angular/angular.js/issues/5650), [#5712](https://github.com/angular/angular.js/issues/5712))
|
||||
- **$parse:** fix CSP nested property evaluation, and issue that prevented its tests from failing
|
||||
([3b1a4fe0](https://github.com/angular/angular.js/commit/3b1a4fe0c83c7898ecd7261ab4213998ee7be0ec),
|
||||
[#5591](https://github.com/angular/angular.js/issues/5591), [#5592](https://github.com/angular/angular.js/issues/5592))
|
||||
- **closure:** add Closure externs for angular.$q.Promise.finally
|
||||
([caeb7402](https://github.com/angular/angular.js/commit/caeb7402651702cd13df2f1594e9827439a8b760),
|
||||
[#4757](https://github.com/angular/angular.js/issues/4757))
|
||||
- **ngMock window.inject:** Remove Error 'stack' property changes
|
||||
([7e916455](https://github.com/angular/angular.js/commit/7e916455b36dc9ca4d4afc1e44cade90006d00e3))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **select:** allow multiline ng-options
|
||||
([43a2f3d0](https://github.com/angular/angular.js/commit/43a2f3d0bf435e3626cd679caff4281cfb3415bd),
|
||||
[#5602](https://github.com/angular/angular.js/issues/5602))
|
||||
|
||||
<a name="1.2.7"></a>
|
||||
# 1.2.7 emoji-clairvoyance (2014-01-03)
|
||||
|
||||
|
||||
+1
-2
@@ -80,7 +80,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our Coding Rules
|
||||
* Follow our [Coding Rules](#coding-rules)
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
@@ -259,5 +259,4 @@ You can find out more detailed information about contributing in the
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+8
-1
@@ -101,6 +101,11 @@ module.exports = function(grunt) {
|
||||
},
|
||||
|
||||
|
||||
runprotractor: {
|
||||
normal: 'protractor-conf.js'
|
||||
},
|
||||
|
||||
|
||||
clean: {
|
||||
build: ['build'],
|
||||
tmp: ['tmp']
|
||||
@@ -283,13 +288,15 @@ module.exports = function(grunt) {
|
||||
|
||||
|
||||
//alias tasks
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:e2e']);
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:e2e', 'webdriver', 'runprotractor:normal']);
|
||||
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
|
||||
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
|
||||
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
|
||||
grunt.registerTask('test:e2e', 'Run the end to end tests with Karma and keep a test server running in the background', ['connect:testserver', 'tests:end2end']);
|
||||
// This should eventually replace test:e2e
|
||||
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'runprotractor:normal']);
|
||||
grunt.registerTask('test:docgen', ['jasmine_node']);
|
||||
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ This module is provided by default and contains the core components of AngularJS
|
||||
</td>
|
||||
<td>
|
||||
<p>
|
||||
The core filters available in the ng module are used to transform template data before it is renders within directives and expressions.
|
||||
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
|
||||
</p>
|
||||
<p>
|
||||
Some examples include:
|
||||
|
||||
@@ -184,7 +184,7 @@ The following graphic shows how everything works together after we introduced th
|
||||
# View independent business logic: Services
|
||||
|
||||
Right now, the `InvoiceController` contains all logic of our example. When the application grows it
|
||||
is a good practise to move view independent logic from the controller into a so called
|
||||
is a good practice to move view independent logic from the controller into a so called
|
||||
<a name="service">"{@link dev_guide.services service}"</a>, so it can be reused by other parts
|
||||
of the application as well. Later on, we could also change that service to load the exchange rates
|
||||
from the web, e.g. by calling the Yahoo Finance API, without changing the controller.
|
||||
|
||||
@@ -19,7 +19,7 @@ testable.
|
||||
To register a service, you must have a module that this service will be part of. Afterwards, you
|
||||
can register the service with the module either via the {@link api/angular.Module Module api} or
|
||||
by using the {@link api/AUTO.$provide $provide} service in the module configuration
|
||||
function.The following pseudo-code shows both approaches:
|
||||
function. The following pseudo-code shows both approaches:
|
||||
|
||||
Using the angular.Module api:
|
||||
<pre>
|
||||
|
||||
@@ -280,7 +280,7 @@ using `templateUrl` instead:
|
||||
</example>
|
||||
|
||||
Great! But what if we wanted to have our directive match the tag name `<my-customer>` instead?
|
||||
If we simply put a `<my-customer>` element into the HMTL, it doesn't work.
|
||||
If we simply put a `<my-customer>` element into the HTML, it doesn't work.
|
||||
|
||||
<div class="alert alert-waring">
|
||||
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
|
||||
|
||||
@@ -116,7 +116,7 @@ This ensures that the user is not distracted with an error until after interacti
|
||||
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
|
||||
The form instance can optionally be published into the scope using the `name` attribute.
|
||||
|
||||
Similarly, an input control that has the {@link api/ng.directive:ngModel} directive holds an
|
||||
Similarly, an input control that has the {@link api/ng.directive:ngModel ngModel} directive holds an
|
||||
instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
|
||||
Such a control instance can be published as a property of the form instance using the `name` attribute
|
||||
on the input control. The name attribute specifies the name of the property on the form instance.
|
||||
|
||||
@@ -97,9 +97,9 @@ locale, it is fine to rely on the default currency symbol. However, if you antic
|
||||
in other locales might use your app, you should provide your own currency symbol to make sure the
|
||||
actual value is understood.
|
||||
|
||||
For example, if you want to display account balance of 1000 dollars with the following binding
|
||||
For example, if you want to display an account balance of 1000 dollars with the following binding
|
||||
containing currency filter: `{{ 1000 | currency }}`, and your app is currently in en-US locale.
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, her
|
||||
'$1000.00' will be shown. However, if someone in a different local (say, Japan) views your app, their
|
||||
browser will specify the locale as ja, and the balance of '¥1000.00' will be shown instead. This
|
||||
will really upset your client.
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ It is very unlikely that issues specific to IE7 or earlier will be given any tim
|
||||
|
||||
To make your Angular application work on IE please make sure that:
|
||||
|
||||
1. You polyfill JSON.stringify if necessary (IE7 will need this). You can use
|
||||
1. You polyfill JSON.stringify for IE7 and below. You can use
|
||||
[JSON2](https://github.com/douglascrockford/JSON-js) or
|
||||
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
|
||||
<pre>
|
||||
@@ -51,7 +51,7 @@ To make your Angular application work on IE please make sure that:
|
||||
3. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
|
||||
`<div ng-view>` instead), or
|
||||
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE happy:
|
||||
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
|
||||
<pre>
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
|
||||
|
||||
@@ -73,7 +73,6 @@ directory.</p></li>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
|
||||
don't have it already:</p>
|
||||
<pre>
|
||||
npm install -g karma
|
||||
npm install
|
||||
</pre></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
|
||||
@@ -191,8 +191,8 @@ You can do this by issuing `npm install` into your terminal.
|
||||
To run the test, do the following:
|
||||
|
||||
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
|
||||
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
|
||||
is located at `./config/karma.conf.js`).
|
||||
`./scripts/test.sh` (if you are on Windows, run scripts\test.bat) to start the Karma server (the
|
||||
config file necessary to start the server is located at `./config/karma.conf.js`).
|
||||
|
||||
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
|
||||
the background. Karma will use this browser for test execution.
|
||||
@@ -206,7 +206,7 @@ is located at `./config/karma.conf.js`).
|
||||
|
||||
Yay! The test passed! Or not...
|
||||
|
||||
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
|
||||
4. To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
# Experiments
|
||||
@@ -217,9 +217,13 @@ is located at `./config/karma.conf.js`).
|
||||
|
||||
* Create a new model property in the controller and bind to it from the template. For example:
|
||||
|
||||
$scope.hello = "Hello, World!"
|
||||
$scope.name = "World"
|
||||
|
||||
Refresh your browser to make sure it says, "Hello, World!"
|
||||
Then add a new binding to `index.html`:
|
||||
|
||||
<p>Hello, {{name}}!</p>
|
||||
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
|
||||
* Create a repeater that constructs a simple table:
|
||||
|
||||
|
||||
@@ -88,8 +88,8 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
|
||||
record. This property is used to order phones by age.
|
||||
|
||||
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
|
||||
not set the default value here, the model would stay uninitialized until our user picks an
|
||||
option from the drop down menu.
|
||||
not set a default value here, the `orderBy` filter would remain uninitialized until our
|
||||
user picked an option from the drop down menu.
|
||||
|
||||
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
|
||||
browser, "Newest" is selected in the drop down menu. This is because we set `orderProp` to `'age'`
|
||||
|
||||
@@ -170,6 +170,9 @@ describe('PhoneCat controllers', function() {
|
||||
describe('PhoneListCtrl', function(){
|
||||
var scope, ctrl, $httpBackend;
|
||||
|
||||
// Load our app module definition before each test.
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
|
||||
// This allows us to inject a service but then attach it to a variable
|
||||
// with the same name as the service.
|
||||
|
||||
@@ -121,7 +121,7 @@ view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` co
|
||||
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
|
||||
empty `PhoneDetailCtrl` controller to the `app/js/controllers.js` file for the phone details view.
|
||||
|
||||
`$route.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser
|
||||
`$routeProvider.otherwise({redirectTo: '/phones'})` triggers a redirection to `/phones` when the browser
|
||||
address doesn't match either of our routes.
|
||||
|
||||
Note the use of the `:phoneId` parameter in the second route declaration. The `$route` service uses
|
||||
|
||||
@@ -293,8 +293,7 @@ Let's add another animation to our application. Switching to our `phone-detail.h
|
||||
we see that we have a nice thumbnail swapper. By clicking on the thumbnails listed on the page,
|
||||
the profile phone image changes. But how can we change this around to add animations?
|
||||
|
||||
Lets think about it first,
|
||||
basically when you click on a thumbnail image, you're changing the state of the profile image to reflect the newly
|
||||
Let's think about it first. Basically, when you click on a thumbnail image, you're changing the state of the profile image to reflect the newly
|
||||
selected thumbnail image.
|
||||
The best way to specify state changes within HTML is to use classes.
|
||||
Much like before, how we used a CSS class to specify
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
var ngdoc = require('../src/ngdoc.js');
|
||||
var DOM = require('../src/dom.js').DOM;
|
||||
var gruntUtil = require('../../lib/grunt/utils.js');
|
||||
|
||||
|
||||
describe('ngdoc', function() {
|
||||
var Doc = ngdoc.Doc;
|
||||
@@ -287,6 +289,33 @@ describe('ngdoc', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('api section', function() {
|
||||
|
||||
it('should render a "view source" button with link to the source in master', function() {
|
||||
var doc = new Doc({
|
||||
id: 'ng.abc',
|
||||
name: 'ng.abc',
|
||||
section: 'api',
|
||||
ngdoc: 'service',
|
||||
file: 'fooService.js',
|
||||
line: '333'
|
||||
});
|
||||
|
||||
if (gruntUtil.getVersion().full.indexOf('-') === -1) {
|
||||
expect(doc.html().match(/^(<a .*?<\/a>)/)[1]).toMatch(
|
||||
/<a href="http:\/\/github\.com\/angular\/angular\.js\/tree\/v\d+\.\d+\.\d+\/fooService\.js#L333" class="view-source/
|
||||
);
|
||||
} else {
|
||||
expect(doc.html().match(/^(<a .*?<\/a>)/)[1]).toMatch(
|
||||
/<a href="http:\/\/github\.com\/angular\/angular\.js\/tree\/[a-z0-9]{7}\/fooService\.js#L333" class="view-source/
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
describe('TAG', function() {
|
||||
|
||||
+16
-3
@@ -6,7 +6,8 @@ var makeUnique = {
|
||||
'script.js': true,
|
||||
'unit.js': true,
|
||||
'spec.js': true,
|
||||
'scenario.js': true
|
||||
'scenario.js': true,
|
||||
'protractorTest.js': true
|
||||
}
|
||||
|
||||
function ids(list) {
|
||||
@@ -14,7 +15,7 @@ function ids(list) {
|
||||
};
|
||||
|
||||
|
||||
exports.Example = function(scenarios) {
|
||||
exports.Example = function(scenarios, protractorTests) {
|
||||
this.module = '';
|
||||
this.deps = ['angular.js'];
|
||||
this.html = [];
|
||||
@@ -24,6 +25,8 @@ exports.Example = function(scenarios) {
|
||||
this.unit = [];
|
||||
this.scenario = [];
|
||||
this.scenarios = scenarios;
|
||||
this.protractorTest = [];
|
||||
this.protractorTests = protractorTests;
|
||||
}
|
||||
|
||||
exports.Example.prototype.setModule = function(module) {
|
||||
@@ -44,6 +47,10 @@ exports.Example.prototype.addSource = function(name, content) {
|
||||
var ext = name == 'scenario.js' ? 'scenario' : name.split('.')[1],
|
||||
id = name;
|
||||
|
||||
if (name == 'protractorTest.js') {
|
||||
ext = 'protractorTest';
|
||||
}
|
||||
|
||||
if (makeUnique[name] && usedIds[id]) {
|
||||
id = name + '-' + (seqCount++);
|
||||
}
|
||||
@@ -56,6 +63,9 @@ exports.Example.prototype.addSource = function(name, content) {
|
||||
if (ext == 'scenario') {
|
||||
this.scenarios.push(content);
|
||||
}
|
||||
if (ext == 'protractorTest') {
|
||||
this.protractorTests.push(content);
|
||||
}
|
||||
};
|
||||
|
||||
exports.Example.prototype.enableAnimations = function() {
|
||||
@@ -92,6 +102,7 @@ exports.Example.prototype.toHtmlEdit = function() {
|
||||
out.push(' source-edit-json="' + ids(this.json) + '"');
|
||||
out.push(' source-edit-unit="' + ids(this.unit) + '"');
|
||||
out.push(' source-edit-scenario="' + ids(this.scenario) + '"');
|
||||
out.push(' source-edit-protractor="' + ids(this.protractorTest) + '"');
|
||||
out.push('></div>\n');
|
||||
return out.join('');
|
||||
};
|
||||
@@ -107,6 +118,7 @@ exports.Example.prototype.toHtmlTabs = function() {
|
||||
htmlTabs(this.json);
|
||||
htmlTabs(this.unit);
|
||||
htmlTabs(this.scenario);
|
||||
htmlTabs(this.protractorTest);
|
||||
out.push('</div>');
|
||||
return out.join('');
|
||||
|
||||
@@ -119,7 +131,8 @@ exports.Example.prototype.toHtmlTabs = function() {
|
||||
if (name === 'index.html') {
|
||||
wrap = ' ng-html-wrap="' + self.module + ' ' + self.deps.join(' ') + '"';
|
||||
}
|
||||
if (name == 'scenario.js') name = 'End to end test';
|
||||
if (name == 'scenario.js') name = 'ngScenario e2e test';
|
||||
if (name == 'protractorTest.js') name = 'Protractor e2e test';
|
||||
|
||||
out.push(
|
||||
'<div class="tab-pane" title="' + name + '">\n' +
|
||||
|
||||
@@ -17,6 +17,8 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||
return writer.makeDir('build/docs/components/bootstrap');
|
||||
}).then(function() {
|
||||
return writer.makeDir('build/docs/components/font-awesome');
|
||||
}).then(function() {
|
||||
return writer.makeDir('build/docs/e2etests');
|
||||
}).then(function() {
|
||||
console.log('Generating AngularJS Reference Documentation...');
|
||||
return reader.collect();
|
||||
@@ -53,6 +55,10 @@ writer.makeDir('build/docs/', true).then(function() {
|
||||
var id = doc.id.replace('angular.Module', 'angular.IModule');
|
||||
|
||||
fileFutures.push(writer.output('partials/' + doc.section + '/' + id + '.html', doc.html()));
|
||||
// If it has a sample Protractor test, output that as well.
|
||||
if (doc.protractorTests.length) {
|
||||
fileFutures.push(writer.output('ptore2e/' + doc.section + '/' + id + '_test.js', ngdoc.writeProtractorTest(doc)));
|
||||
}
|
||||
});
|
||||
|
||||
ngdoc.checkBrokenLinks(docs);
|
||||
|
||||
+23
-3
@@ -35,6 +35,7 @@ var lookupMinerrMsg = function (doc) {
|
||||
exports.trim = trim;
|
||||
exports.metadata = metadata;
|
||||
exports.scenarios = scenarios;
|
||||
exports.writeProtractorTest = writeProtractorTest;
|
||||
exports.merge = merge;
|
||||
exports.checkBrokenLinks = checkBrokenLinks;
|
||||
exports.Doc = Doc;
|
||||
@@ -155,6 +156,7 @@ function Doc(text, file, line) {
|
||||
this.line = line;
|
||||
}
|
||||
this.scenarios = this.scenarios || [];
|
||||
this.protractorTests = this.protractorTests || [];
|
||||
this.requires = this.requires || [];
|
||||
this.param = this.param || [];
|
||||
this.properties = this.properties || [];
|
||||
@@ -292,7 +294,7 @@ Doc.prototype = {
|
||||
replace(/<example(?:\s+module="([^"]*)")?(?:\s+deps="([^"]*)")?(\s+animations="true")?>([\s\S]*?)<\/example>/gmi,
|
||||
function(_, module, deps, animations, content) {
|
||||
|
||||
var example = new Example(self.scenarios);
|
||||
var example = new Example(self.scenarios, self.protractorTests);
|
||||
if(animations) {
|
||||
example.enableAnimations();
|
||||
example.addDeps('angular-animate.js');
|
||||
@@ -329,7 +331,7 @@ Doc.prototype = {
|
||||
}).
|
||||
replace(/^<doc:example(\s+[^>]*)?>([\s\S]*)<\/doc:example>/mi, function(_, attrs, content) {
|
||||
var html, script, scenario,
|
||||
example = new Example(self.scenarios);
|
||||
example = new Example(self.scenarios, self.protractorTests);
|
||||
|
||||
example.setModule((attrs||'module=""').match(/^\s*module=["'](.*)["']\s*$/)[1]);
|
||||
content.
|
||||
@@ -347,6 +349,8 @@ Doc.prototype = {
|
||||
}).
|
||||
replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi, function(_, before, content){
|
||||
example.addSource('scenario.js', content);
|
||||
}).replace(/(<doc:protractor>)([\s\S]*)(<\/doc:protractor>)/mi, function(_, before, content){
|
||||
example.addSource('protractorTest.js', content);
|
||||
});
|
||||
|
||||
return placeholder(example.toHtml());
|
||||
@@ -548,7 +552,7 @@ Doc.prototype = {
|
||||
minerrMsg;
|
||||
|
||||
var gitTagFromFullVersion = function(version) {
|
||||
var match = version.match(/-(\w{7})/);
|
||||
var match = version.match(/sha\.(\w{7})/);
|
||||
|
||||
if (match) {
|
||||
// git sha
|
||||
@@ -1106,6 +1110,22 @@ function scenarios(docs){
|
||||
}
|
||||
}
|
||||
|
||||
function writeProtractorTest(doc){
|
||||
var lines = [];
|
||||
lines.push('describe("' + doc.section + '/' + doc.id + '", function() {');
|
||||
lines.push(' beforeEach(function() {');
|
||||
lines.push(' browser.get("index-nocache.html#!/' + doc.section + '/' + doc.id + '");');
|
||||
lines.push(' });');
|
||||
lines.push('');
|
||||
doc.protractorTests.forEach(function(test){
|
||||
lines.push(indentCode(trim(test), 2));
|
||||
lines.push('');
|
||||
});
|
||||
lines.push('});');
|
||||
lines.push('');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function metadata(docs){
|
||||
|
||||
@@ -167,6 +167,11 @@
|
||||
margin-top: .5em;
|
||||
}
|
||||
|
||||
.content h6 {
|
||||
text-transform:none;
|
||||
color:black;
|
||||
}
|
||||
|
||||
ul.parameters > li > p,
|
||||
.returns > p {
|
||||
display: inline;
|
||||
|
||||
@@ -5,11 +5,19 @@ var docsApp = {
|
||||
filter: {}
|
||||
};
|
||||
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
docsApp.controller.DocsVersionsCtrl = ['$scope', '$rootScope', '$window', 'NG_VERSIONS', 'NG_VERSION', function($scope, $rootScope, $window, NG_VERSIONS, NG_VERSION) {
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.docs_version = NG_VERSIONS[0];
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
$window.location = version.url;
|
||||
var currentPagePath = '';
|
||||
|
||||
// preserve URL path when switching between doc versions
|
||||
if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
|
||||
currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
|
||||
}
|
||||
|
||||
$window.location = version.url + currentPagePath;
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -645,7 +653,7 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
|
||||
}];
|
||||
|
||||
|
||||
docsApp.controller.DocsController = function($scope, $location, $window, $cookies, sections) {
|
||||
docsApp.controller.DocsController = function($scope, $rootScope, $location, $window, $cookies, sections) {
|
||||
$scope.fold = function(url) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
@@ -736,9 +744,9 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
sectionName = SECTION_NAME[sectionId] || sectionId,
|
||||
page = sections.getPage(sectionId, partialId);
|
||||
|
||||
$scope.currentPage = sections.getPage(sectionId, partialId);
|
||||
$rootScope.currentPage = sections.getPage(sectionId, partialId);
|
||||
|
||||
if (!$scope.currentPage) {
|
||||
if (!$rootScope.currentPage) {
|
||||
$scope.partialTitle = 'Error: Page Not Found!';
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-col
|
||||
|
||||
# END TO END TESTS #
|
||||
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
|
||||
grunt test:protractor
|
||||
|
||||
# Promises/A+ TESTS #
|
||||
grunt test:promises-aplus --no-color
|
||||
|
||||
+1
-1
@@ -10,12 +10,12 @@ module.exports = function(config) {
|
||||
|
||||
'build/angular.js',
|
||||
'build/angular-cookies.js',
|
||||
'build/angular-mocks.js',
|
||||
'build/angular-resource.js',
|
||||
'build/angular-touch.js',
|
||||
'build/angular-sanitize.js',
|
||||
'build/angular-route.js',
|
||||
'build/angular-animate.js',
|
||||
'build/angular-mocks.js',
|
||||
|
||||
'build/docs/components/lunr.js',
|
||||
'build/docs/components/google-code-prettify.js',
|
||||
|
||||
@@ -36,13 +36,14 @@ module.exports = function(config, specificOptions) {
|
||||
},
|
||||
'SL_Firefox': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox'
|
||||
browserName: 'firefox',
|
||||
version: '26'
|
||||
},
|
||||
'SL_Safari': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'Mac 10.8',
|
||||
version: '6'
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
},
|
||||
'SL_IE_8': {
|
||||
base: 'SauceLabs',
|
||||
|
||||
@@ -61,6 +61,14 @@ module.exports = function(grunt) {
|
||||
util.startKarma.call(util, this.data, false, this.async());
|
||||
});
|
||||
|
||||
grunt.registerTask('webdriver', 'Update webdriver', function() {
|
||||
util.updateWebdriver.call(util, this.async());
|
||||
});
|
||||
|
||||
grunt.registerMultiTask('runprotractor', 'Run Protractor integration tests', function() {
|
||||
util.startProtractor.call(util, this.data, this.async());
|
||||
});
|
||||
|
||||
grunt.registerTask('collect-errors', 'Combine stripped error files', function () {
|
||||
util.collectErrors();
|
||||
});
|
||||
|
||||
@@ -84,6 +84,38 @@ module.exports = {
|
||||
},
|
||||
|
||||
|
||||
updateWebdriver: function(done){
|
||||
var p = spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update']);
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
p.on('exit', function(code){
|
||||
if(code !== 0) grunt.fail.warn('Webdriver failed to update');
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
startProtractor: function(config, done){
|
||||
var sauceUser = grunt.option('sauceUser');
|
||||
var sauceKey = grunt.option('sauceKey');
|
||||
var tunnelIdentifier = grunt.option('capabilities.tunnel-identifier');
|
||||
var sauceBuild = grunt.option('capabilities.build');
|
||||
var args = ['node_modules/protractor/bin/protractor', config];
|
||||
if (sauceUser) args.push('--sauceUser=' + sauceUser);
|
||||
if (sauceKey) args.push('--sauceKey=' + sauceKey);
|
||||
if (tunnelIdentifier) args.push('--capabilities.tunnel-identifier=' + tunnelIdentifier);
|
||||
if (sauceBuild) args.push('--capabilities.build=' + sauceBuild);
|
||||
|
||||
|
||||
var p = spawn('node', args);
|
||||
p.stdout.pipe(process.stdout);
|
||||
p.stderr.pipe(process.stderr);
|
||||
p.on('exit', function(code){
|
||||
if(code !== 0) grunt.fail.warn('Protractor test(s) failed. Exit code: ' + code);
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
wrap: function(src, name){
|
||||
src.unshift('src/' + name + '.prefix');
|
||||
src.push('src/' + name + '.suffix');
|
||||
|
||||
+13
-12
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.7",
|
||||
"cdnVersion": "1.2.6",
|
||||
"codename": "emoji-clairvoyance",
|
||||
"version": "1.2.9",
|
||||
"cdnVersion": "1.2.8",
|
||||
"codename": "enchanted-articulacy",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -20,14 +20,16 @@
|
||||
"q-io": "~1.10.6",
|
||||
"qq": "~0.3.5",
|
||||
"shelljs": "~0.2.6",
|
||||
"karma": "0.11.11",
|
||||
"karma-jasmine": "~0.1.0",
|
||||
"karma-chrome-launcher": "~0.1.0",
|
||||
"karma-firefox-launcher": "~0.1.0",
|
||||
"karma-ng-scenario": "~0.1.0",
|
||||
"karma-junit-reporter": "~0.2.1",
|
||||
"karma-sauce-launcher": "~0.2.0",
|
||||
"karma-script-launcher": "~0.1.0",
|
||||
"karma": "0.11.12",
|
||||
"karma-jasmine": "0.1.5",
|
||||
"karma-chrome-launcher": "0.1.2",
|
||||
"karma-firefox-launcher": "0.1.3",
|
||||
"karma-ng-scenario": "0.1.0",
|
||||
"karma-junit-reporter": "0.2.1",
|
||||
"karma-sauce-launcher": "0.2.0",
|
||||
"karma-script-launcher": "0.1.0",
|
||||
"karma-browserstack-launcher": "0.0.7",
|
||||
"protractor": "~0.16.1",
|
||||
"yaml-js": "~0.0.8",
|
||||
"marked": "0.2.9",
|
||||
"rewire": "1.1.3",
|
||||
@@ -39,7 +41,6 @@
|
||||
"grunt-shell": "~0.4.0",
|
||||
"semver": "~2.1.0",
|
||||
"lodash": "~2.1.0",
|
||||
"karma-browserstack-launcher": "git://github.com/karma-runner/karma-browserstack-launcher.git#master",
|
||||
"browserstacktunnel-wrapper": "~1.1.1"
|
||||
},
|
||||
"licenses": [
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
|
||||
specs: [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
|
||||
baseUrl: 'http://localhost:8000/build/docs/',
|
||||
|
||||
framework: 'jasmine',
|
||||
|
||||
jasmineNodeOpts: {
|
||||
defaultTimeoutInterval: 30000
|
||||
}
|
||||
};
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating angular-phonecat repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update angular-phonecat ###"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
REPO_DIR=$TMP_DIR/angular-phonecat
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
}
|
||||
|
||||
function prepare {
|
||||
echo "-- Cloning angular-phonecat"
|
||||
git clone git@github.com:angular/angular-phonecat.git $REPO_DIR
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
echo "-- Updating angular-phonecat"
|
||||
cd $REPO_DIR
|
||||
./scripts/private/update-angular.sh $BUILD_DIR
|
||||
|
||||
# Test
|
||||
if [[ $NO_TEST != "true" ]]; then
|
||||
./scripts/private/test-all.sh
|
||||
fi
|
||||
|
||||
# Generate demo
|
||||
./scripts/private/snapshot-web.sh
|
||||
git checkout gh-pages
|
||||
git pull
|
||||
rm -r step*
|
||||
mv angular-phonecat-snapshots-web/step* .
|
||||
git add step*
|
||||
git commit -am "Angular $NEW_VERSION release"
|
||||
}
|
||||
|
||||
function publish {
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing angular-phonecat"
|
||||
git push origin master -f --tags
|
||||
git push origin gh-pages -f
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating angular-seed repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update angular-seed ###"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
REPO_DIR=$TMP_DIR/angular-seed
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
}
|
||||
|
||||
function prepare {
|
||||
echo "-- Cloning angular-seed"
|
||||
git clone git@github.com:angular/angular-seed.git $REPO_DIR
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
echo "-- Updating angular-seed"
|
||||
cd $REPO_DIR
|
||||
./scripts/update-angular.sh $BUILD_DIR
|
||||
|
||||
# Test
|
||||
if [[ $NO_TEST != "true" ]]; then
|
||||
./scripts/test-all.sh
|
||||
fi
|
||||
}
|
||||
|
||||
function publish {
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing angular-seed"
|
||||
git push origin master
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Remove "-snapshot" from version ########"
|
||||
echo "############################################"
|
||||
|
||||
ARG_DEFS=()
|
||||
|
||||
function run {
|
||||
cd ../..
|
||||
|
||||
replaceJsonProp "package.json" "version" "(.*)-snapshot" "\2"
|
||||
VERSION=$(readJsonProp "package.json" "version")
|
||||
|
||||
git add package.json
|
||||
git commit -m "chore(release): cut v$VERSION release"
|
||||
git tag -m "v$VERSION" v$VERSION
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Increment version, add "-snapshot" and set version name ##"
|
||||
echo "############################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--next-version-type=(patch|minor|major)"
|
||||
"--next-version-name=(.+)"
|
||||
)
|
||||
|
||||
function run {
|
||||
cd ../..
|
||||
|
||||
grunt bump:$NEXT_VERSION_TYPE
|
||||
NEXT_VERSION=$(readJsonProp "package.json" "version")
|
||||
replaceJsonProp "package.json" "version" "(.*)" "\2-snapshot"
|
||||
replaceJsonProp "package.json" "codename" ".*" "$NEXT_VERSION_NAME"
|
||||
|
||||
git add package.json
|
||||
git commit -m "chore(release): start v$NEXT_VERSION ($NEXT_VERSION)"
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Script for updating cdnVersion of angular.js
|
||||
|
||||
echo "###################################"
|
||||
echo "## Update angular.js cdnVersion ###"
|
||||
echo "###################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--cdn-version=(.*)"
|
||||
"--action=(prepare|publish)"
|
||||
)
|
||||
|
||||
function init {
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function prepare {
|
||||
replaceJsonProp "package.json" "cdnVersion" "(.*)" "$CDN_VERSION"
|
||||
git add package.json
|
||||
git commit -m "chore(release): update cdn version"
|
||||
}
|
||||
|
||||
function publish {
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
# push the commits to github
|
||||
git push origin $BRANCH
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating angular.js repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update angular.js ###"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"--next-version-type=(patch|minor|major)"
|
||||
"--next-version-name=(.+)"
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
function init {
|
||||
cd ../..
|
||||
}
|
||||
|
||||
function prepare() {
|
||||
./scripts/angular.js/finalize-version.sh
|
||||
|
||||
# Build
|
||||
if [[ $NO_TEST == "true" ]]; then
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
else
|
||||
./jenkins_build.sh
|
||||
fi
|
||||
|
||||
./scripts/angular.js/initialize-new-version.sh --next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
|
||||
}
|
||||
|
||||
function publish() {
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
# push the commits to github
|
||||
git push origin $BRANCH
|
||||
# push the release tag
|
||||
git push origin v`cat build/version.txt`
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/bin/sh
|
||||
# Script for updating angularjs.org repo
|
||||
|
||||
echo "#################################"
|
||||
echo "##### Update angularjs.org ######"
|
||||
echo "#################################"
|
||||
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
"--cdn-version=(.*)"
|
||||
)
|
||||
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
REPO_DIR=$TMP_DIR/angularjs.org
|
||||
}
|
||||
|
||||
function prepare {
|
||||
echo "-- Cloning angularjs.org"
|
||||
git clone git@github.com:angular/angularjs.org.git $REPO_DIR
|
||||
|
||||
#
|
||||
# update files
|
||||
#
|
||||
echo "-- Updating angularjs.org"
|
||||
cd $REPO_DIR
|
||||
VERSION_REGEX="[a-z0-9\-\.\+]+"
|
||||
|
||||
replaceInFile "index.html" "(ajax\/libs\/angularjs\/)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
replaceInFile "index.html" "(<span class=\"version\">[^<]*<span>)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
replaceInFile "index.html" "(code.angularjs.org\/)$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
|
||||
replaceInFile "js/homepage.js" "($scope.CURRENT_STABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
replaceInFile "js/homepage.js" "($scope.CURRENT_UNSTABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
|
||||
|
||||
git add index.html
|
||||
git add js/homepage.js
|
||||
git commit -m "update(version): update angular version to $CDN_VERSION"
|
||||
}
|
||||
|
||||
function publish {
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing angularjs.org"
|
||||
git push origin master
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
@@ -1,15 +0,0 @@
|
||||
# Angular Bower Script
|
||||
|
||||
Script for updating the Angular bower repos from current local build.
|
||||
|
||||
## Instructions
|
||||
|
||||
`grunt package`: Build angular locally
|
||||
|
||||
```shell
|
||||
./publish.sh
|
||||
```
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
+87
-74
@@ -1,88 +1,101 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating the Angular bower repos from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update bower ###############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`
|
||||
|
||||
SCRIPT_DIR=`pwd`
|
||||
TMP_DIR=../../tmp
|
||||
BUILD_DIR=../../build
|
||||
NEW_VERSION=`cat $BUILD_DIR/version.txt`
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
)
|
||||
|
||||
#
|
||||
# clone repos
|
||||
#
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Cloning bower-$repo"
|
||||
git clone git@github.com:angular/bower-$repo.git $TMP_DIR/bower-$repo
|
||||
done
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
REPOS=(
|
||||
angular
|
||||
angular-animate
|
||||
angular-cookies
|
||||
angular-i18n
|
||||
angular-loader
|
||||
angular-mocks
|
||||
angular-route
|
||||
angular-resource
|
||||
angular-sanitize
|
||||
angular-scenario
|
||||
angular-touch
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# move the files from the build
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
if [ -f $BUILD_DIR/$repo.js ] # ignore i18l
|
||||
then
|
||||
echo "-- Updating files in bower-$repo"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp $BUILD_DIR/$repo.* $TMP_DIR/bower-$repo/
|
||||
fi
|
||||
done
|
||||
|
||||
# move i18n files
|
||||
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
cp $BUILD_DIR/angular-csp.css $TMP_DIR/bower-angular
|
||||
function prepare {
|
||||
#
|
||||
# clone repos
|
||||
#
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Cloning bower-$repo"
|
||||
git clone git@github.com:angular/bower-$repo.git $TMP_DIR/bower-$repo
|
||||
done
|
||||
|
||||
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
#
|
||||
#
|
||||
# move the files from the build
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Updating version in bower-$repo to $NEW_VERSION"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
sed -i .tmp -E 's/"(version)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
|
||||
sed -i .tmp -E 's/"(angular.*)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
|
||||
# delete tmp files
|
||||
rm *.tmp
|
||||
git add -A
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
if [ -f $BUILD_DIR/$repo.js ] # ignore i18l
|
||||
then
|
||||
echo "-- Updating files in bower-$repo"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp $BUILD_DIR/$repo.* $TMP_DIR/bower-$repo/
|
||||
fi
|
||||
done
|
||||
|
||||
echo "-- Committing, tagging and pushing bower-$repo"
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git tag v$NEW_VERSION
|
||||
git push origin master
|
||||
git push origin v$NEW_VERSION
|
||||
cd $SCRIPT_DIR
|
||||
done
|
||||
# move i18n files
|
||||
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
cp $BUILD_DIR/angular-csp.css $TMP_DIR/bower-angular
|
||||
|
||||
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
#
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Updating version in bower-$repo to $NEW_VERSION"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
replaceJsonProp "bower.json" "version" ".*" "$NEW_VERSION"
|
||||
replaceJsonProp "bower.json" "angular.*" ".*" "$NEW_VERSION"
|
||||
|
||||
git add -A
|
||||
|
||||
echo "-- Committing and tagging bower-$repo"
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git tag v$NEW_VERSION
|
||||
cd $SCRIPT_DIR
|
||||
done
|
||||
}
|
||||
|
||||
function publish {
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
echo "-- Pushing bower-$repo"
|
||||
cd $TMP_DIR/bower-$repo
|
||||
git push origin master
|
||||
git push origin v$NEW_VERSION
|
||||
cd $SCRIPT_DIR
|
||||
done
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# code.angular.js.org Script
|
||||
|
||||
Script for updating code.angularjs.org repo from current local build.
|
||||
|
||||
Note: For a snapshot build, this will fetch the data from the ci server
|
||||
and NOT take the local build!
|
||||
|
||||
## Instructions
|
||||
|
||||
`grunt package`: Build angular locally
|
||||
|
||||
```shell
|
||||
./publish.sh
|
||||
```
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
@@ -1,62 +1,73 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script for updating code.angularjs.org repo from current local build.
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update code.angular.js.org ###"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`
|
||||
ARG_DEFS=(
|
||||
"--action=(prepare|publish)"
|
||||
)
|
||||
|
||||
TMP_DIR=../../tmp
|
||||
REPO_DIR=$TMP_DIR/code.angularjs.org
|
||||
BUILD_DIR=../../build
|
||||
SCRIPT_DIR=`pwd`
|
||||
NEW_VERSION=`cat $BUILD_DIR/version.txt`
|
||||
function init {
|
||||
TMP_DIR=$(resolveDir ../../tmp)
|
||||
BUILD_DIR=$(resolveDir ../../build)
|
||||
REPO_DIR=$TMP_DIR/code.angularjs.org
|
||||
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
|
||||
if [[ "$NEW_VERSION" =~ sha ]]; then
|
||||
IS_SNAPSHOT_BUILD=true
|
||||
else
|
||||
IS_SNAPSHOT_BUILD=
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Snapshot builds are kept in a temp directory in code.angularjs.org
|
||||
# that is filled by calling a php script there.
|
||||
#
|
||||
if [[ "$NEW_VERSION" =~ sha ]] ;then
|
||||
echo "-- updating snapshot version"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||
exit 0;
|
||||
fi
|
||||
function prepare {
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
# nothing to prepare for snapshot builds as
|
||||
# code.angularjs.org will fetch the current snapshot from
|
||||
# the build server during publish
|
||||
exit 0
|
||||
fi
|
||||
|
||||
#
|
||||
# clone
|
||||
#
|
||||
echo "-- Cloning code.angularjs.org"
|
||||
git clone git@github.com:angular/code.angularjs.org.git $REPO_DIR
|
||||
|
||||
echo "-- Cloning code.angularjs.org"
|
||||
git clone git@github.com:angular/code.angularjs.org.git $REPO_DIR
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
echo "-- Updating code.angularjs.org"
|
||||
mkdir $REPO_DIR/$NEW_VERSION
|
||||
cd $REPO_DIR
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp -r $BUILD_DIR/* $REPO_DIR/$NEW_VERSION/
|
||||
|
||||
#
|
||||
# copy the files from the build
|
||||
#
|
||||
#
|
||||
# commit
|
||||
#
|
||||
echo "-- Committing code.angularjs.org"
|
||||
cd $REPO_DIR
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
}
|
||||
|
||||
echo "-- Updating code.angularjs.org"
|
||||
mkdir $REPO_DIR/$NEW_VERSION
|
||||
cd $REPO_DIR
|
||||
git reset --hard HEAD
|
||||
git checkout master
|
||||
git fetch --all
|
||||
git reset --hard origin/master
|
||||
cd $SCRIPT_DIR
|
||||
cp -r $BUILD_DIR/* $REPO_DIR/$NEW_VERSION/
|
||||
function publish {
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
echo "-- Updating snapshot version"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
#
|
||||
# commit and push
|
||||
#
|
||||
echo "-- Committing and pushing code.angularjs.org"
|
||||
cd $REPO_DIR
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git push origin master
|
||||
cd $SCRIPT_DIR
|
||||
cd $REPO_DIR
|
||||
echo "-- Pushing code.angularjs.org"
|
||||
git push origin master
|
||||
|
||||
#
|
||||
# refresh code.angularjs.org from github
|
||||
#
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
echo "-- Refreshing code.angularjs.org"
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Increment version and add "-snapshot" ##"
|
||||
echo "############################################"
|
||||
|
||||
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
|
||||
echo "Please specify the next version type: patch|minor|major"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_TYPE=$1
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
echo "-- increment version "
|
||||
grunt bump:$BUMP_TYPE
|
||||
NEXT_VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
|
||||
sed -i .tmp -E 's/"version": "(.*)"/"version": "\1-snapshot"/' package.json
|
||||
echo "-- new version: `grep '"version"' package.json`"
|
||||
echo "-- commit"
|
||||
git add package.json
|
||||
git commit -m "chore(release): start v$NEXT_VERSION"
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "############################################"
|
||||
echo "## Remove "-snapshot" from version ########"
|
||||
echo "############################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
echo "-- old version: `grep '"version"' package.json`"
|
||||
sed -i .tmp -E 's/"version": "(.*)-snapshot"/"version": "\1"/' package.json
|
||||
VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
|
||||
echo "-- local version: $VERSION"
|
||||
|
||||
echo "-- commit and tag with v$VERSION"
|
||||
git add package.json
|
||||
git commit -m "chore(release): cut v$VERSION release"
|
||||
git tag -m "v$VERSION" v$VERSION
|
||||
+37
-15
@@ -4,22 +4,44 @@ echo "#################################"
|
||||
echo "#### Update master ##############"
|
||||
echo "#################################"
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
ARG_DEFS=(
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
cd `dirname $0`/../..
|
||||
function init {
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
}
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Jenkins Build ############"
|
||||
echo "#################################"
|
||||
./jenkins_build.sh
|
||||
function build {
|
||||
cd ../..
|
||||
|
||||
echo "#################################"
|
||||
echo "## Update code.angular.js.org ###"
|
||||
echo "#################################"
|
||||
./scripts/code.angularjs.org/publish.sh
|
||||
if [[ $NO_TEST == "true" ]]; then
|
||||
npm install --color false
|
||||
grunt ci-checks package --no-color
|
||||
else
|
||||
./jenkins_build.sh
|
||||
fi
|
||||
|
||||
echo "#################################"
|
||||
echo "#### Update bower ###############"
|
||||
echo "#################################"
|
||||
./scripts/bower/publish.sh
|
||||
cd $SCRIPT_DIR
|
||||
}
|
||||
|
||||
function phase {
|
||||
ACTION_ARG="--action=$1"
|
||||
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
}
|
||||
|
||||
function run {
|
||||
build
|
||||
|
||||
# First prepare all scripts (build, test, commit, tag, ...),
|
||||
# so we are sure everything is all right
|
||||
phase prepare
|
||||
# only then publish to github
|
||||
phase publish
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
|
||||
ARG_DEFS=(
|
||||
# require the git dryrun flag so the script can't be run without
|
||||
# thinking about this!
|
||||
"--git-push-dryrun=(true|false)"
|
||||
"--cdn-version=(.*)"
|
||||
)
|
||||
|
||||
function init {
|
||||
NG_ARGS=("$@")
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
}
|
||||
|
||||
function phase {
|
||||
ACTION_ARG="--action=$1"
|
||||
CDN_VERSION_ARG="--cdn-version=$CDN_VERSION"
|
||||
./scripts/angular.js/publish-cdn-version.sh $ACTION_ARG $CDN_VERSION_ARG $VERBOSE_ARG
|
||||
./scripts/angularjs.org/publish.sh $ACTION_ARG $CDN_VERSION_ARG $VERBOSE_ARG
|
||||
}
|
||||
|
||||
function checkCdn {
|
||||
STATUS=$(curl http://ajax.googleapis.com/ajax/libs/angularjs/$CDN_VERSION/angular.min.js --write-out '%{http_code}' -o /dev/null -silent)
|
||||
if [[ $STATUS != 200 ]]; then
|
||||
echo "Could not find release $CDN_VERSION on CDN"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function run {
|
||||
cd ../..
|
||||
checkCdn
|
||||
|
||||
phase prepare
|
||||
phase publish
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
+36
-34
@@ -4,41 +4,43 @@ echo "#################################"
|
||||
echo "#### Cut release ################"
|
||||
echo "#################################"
|
||||
|
||||
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
|
||||
echo "Please specify the next version type: patch|minor|major"
|
||||
exit 1
|
||||
fi
|
||||
BUMP_TYPE=$1
|
||||
ARG_DEFS=(
|
||||
"--next-version-type=(patch|minor|major)"
|
||||
"--next-version-name=(.+)"
|
||||
# require the git dryrun flag so the script can't be run without
|
||||
# thinking about this!
|
||||
"--git-push-dryrun=(true|false)"
|
||||
"[--no-test=(true|false)]"
|
||||
)
|
||||
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
function init {
|
||||
NG_ARGS=("$@")
|
||||
if [[ ! $VERBOSE ]]; then
|
||||
VERBOSE=false
|
||||
fi
|
||||
if [[ ! $NO_TEST ]]; then
|
||||
NO_TEST=false
|
||||
fi
|
||||
VERBOSE_ARG="--verbose=$VERBOSE"
|
||||
NO_TEST_ARG="--no_test=$NO_TEST"
|
||||
}
|
||||
|
||||
# Jump onto the master branch and make sure we are using the latest
|
||||
git checkout -f master
|
||||
git merge --ff-only origin/master
|
||||
function phase {
|
||||
ACTION_ARG="--action=$1"
|
||||
../angular.js/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG \
|
||||
--next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
|
||||
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
|
||||
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
|
||||
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
|
||||
}
|
||||
|
||||
function run {
|
||||
# First prepare all scripts (build, test, commit, tag, ...),
|
||||
# so we are sure everything is all right
|
||||
phase prepare
|
||||
# only then publish to github
|
||||
phase publish
|
||||
}
|
||||
|
||||
# Normalize working dir to script dir
|
||||
cd `dirname $0`/../..
|
||||
|
||||
|
||||
# Bump versions: remove "-snapshot" suffix
|
||||
./scripts/jenkins/bump-remove-snapshot.sh
|
||||
|
||||
# Build
|
||||
./jenkins_build.sh
|
||||
|
||||
# Bump versions: Increment version and add "-snapshot"
|
||||
./scripts/jenkins/bump-increment.sh $BUMP_TYPE
|
||||
|
||||
echo "-- push to Github"
|
||||
# push the commits to github
|
||||
git push origin master
|
||||
# push the release tag
|
||||
git push origin v`cat build/version.txt`
|
||||
|
||||
# Update code.angularjs.org
|
||||
./scripts/code.angularjs.org/publish.sh
|
||||
|
||||
# Update bower
|
||||
./scripts/bower/publish.sh
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
||||
@@ -11,6 +11,10 @@ if [ $JOB = "unit" ]; then
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
elif [ $JOB = "e2e" ]; then
|
||||
grunt test:e2e --browsers SL_Chrome --reporters dots
|
||||
grunt test:protractor --sauceUser $SAUCE_USERNAME \
|
||||
--sauceKey $SAUCE_ACCESS_KEY \
|
||||
--capabilities.tunnel-identifier=$TRAVIS_JOB_NUMBER \
|
||||
--capabilities.build=$TRAVIS_BUILD_NUMBER
|
||||
else
|
||||
echo "Unknown job type. Please set JOB=unit or JOB=e2e."
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
# This file provides:
|
||||
# - a default control flow
|
||||
# * initializes the environment
|
||||
# * able to mock "git push" in your script and in all sub scripts
|
||||
# * call a function in your script based on the arguments
|
||||
# - named argument parsing and automatic generation of the "usage" for your script
|
||||
# - intercepting "git push" in your script and all sub scripts
|
||||
# - utility functions
|
||||
#
|
||||
# Usage:
|
||||
# - define the variable ARGS_DEF (see below) with the arguments for your script
|
||||
# - include this file using `source utils.inc` at the end of your script.
|
||||
#
|
||||
# Default control flow:
|
||||
# 0. Set the current directory to the directory of the script. By this
|
||||
# the script can be called from anywhere.
|
||||
# 1. Parse the named arguments
|
||||
# 2. If the parameter "git_push_dryrun" is set, all calls the `git push` in this script
|
||||
# or in child scripts will be intercepted so that the `--dry-run` and `--porcelain` is added
|
||||
# to show what the push would do but not actually do it.
|
||||
# 3. If the parameter "verbose" is set, the `-x` flag will be set in bash.
|
||||
# 4. The function "init" will be called if it exists
|
||||
# 5. If the parameter "action" is set, it will call the function with the name of that parameter.
|
||||
# Otherwise the function "run" will be called.
|
||||
#
|
||||
# Named Argument Parsing:
|
||||
# - The variable ARGS_DEF defines the valid command arguments
|
||||
# * Required args syntax: --paramName=paramRegex
|
||||
# * Optional args syntax: [--paramName=paramRegex]
|
||||
# * e.g. ARG_DEFS=("--required_param=(.+)" "[--optional_param=(.+)]")
|
||||
# - Checks that:
|
||||
# * all arguments match to an entry in ARGS_DEF
|
||||
# * all required arguments are present
|
||||
# * all arguments match their regex
|
||||
# - Afterwards, every paramter value will be stored in a variable
|
||||
# with the name of the parameter in upper case (with dash converted to underscore).
|
||||
#
|
||||
# Special arguments that are always available:
|
||||
# - "--action=.*": This parameter will be used to dispatch to a function with that name when the
|
||||
# script is started
|
||||
# - "--git_push_dryrun=true": This will intercept all calls to `git push` in this script
|
||||
# or in child scripts so that the `--dry-run` and `--porcelain` is added
|
||||
# to show what the push would do but not actually do it.
|
||||
# - "--verbose=true": This will set the `-x` flag in bash so that all calls will be logged
|
||||
#
|
||||
# Utility functions:
|
||||
# - readJsonProp
|
||||
# - replaceJsonProp
|
||||
# - resolveDir
|
||||
# - getVar
|
||||
# - serVar
|
||||
# - isFunction
|
||||
|
||||
# always stop on errors
|
||||
set -e
|
||||
|
||||
function usage {
|
||||
echo "Usage: ${0} ${ARG_DEFS[@]}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
function parseArgs {
|
||||
local REQUIRED_ARG_NAMES=()
|
||||
|
||||
# -- helper functions
|
||||
function varName {
|
||||
# everything to upper case and dash to underscore
|
||||
echo ${1//-/_} | tr '[:lower:]' '[:upper:]'
|
||||
}
|
||||
|
||||
function readArgDefs {
|
||||
local ARG_DEF
|
||||
local AD_OPTIONAL
|
||||
local AD_NAME
|
||||
local AD_RE
|
||||
|
||||
# -- helper functions
|
||||
function parseArgDef {
|
||||
local ARG_DEF_REGEX="(\[?)--([^=]+)=(.*)"
|
||||
if [[ ! $1 =~ $ARG_DEF_REGEX ]]; then
|
||||
echo "Internal error: arg def has wrong format: $ARG_DEF"
|
||||
exit 1
|
||||
fi
|
||||
AD_OPTIONAL="${BASH_REMATCH[1]}"
|
||||
AD_NAME="${BASH_REMATCH[2]}"
|
||||
AD_RE="${BASH_REMATCH[3]}"
|
||||
if [[ $AD_OPTIONAL ]]; then
|
||||
# Remove last bracket for optional args.
|
||||
# Can't put this into the ARG_DEF_REGEX somehow...
|
||||
AD_RE=${AD_RE%?}
|
||||
fi
|
||||
}
|
||||
|
||||
# -- run
|
||||
for ARG_DEF in "${ARG_DEFS[@]}"
|
||||
do
|
||||
parseArgDef $ARG_DEF
|
||||
|
||||
local AD_NAME_UPPER=$(varName $AD_NAME)
|
||||
setVar "${AD_NAME_UPPER}_OPTIONAL" "$AD_OPTIONAL"
|
||||
setVar "${AD_NAME_UPPER}_RE" "$AD_RE"
|
||||
if [[ ! $AD_OPTIONAL ]]; then
|
||||
REQUIRED_ARG_NAMES+=($AD_NAME)
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
function readAndValidateArgs {
|
||||
local ARG_NAME
|
||||
local ARG_VALUE
|
||||
local ARG_NAME_UPPER
|
||||
|
||||
# -- helper functions
|
||||
function parseArg {
|
||||
local ARG_REGEX="--([^=]+)=?(.*)"
|
||||
|
||||
if [[ ! $1 =~ $ARG_REGEX ]]; then
|
||||
echo "Can't parse argument $i"
|
||||
usage
|
||||
fi
|
||||
|
||||
ARG_NAME="${BASH_REMATCH[1]}"
|
||||
ARG_VALUE="${BASH_REMATCH[2]}"
|
||||
ARG_NAME_UPPER=$(varName $ARG_NAME)
|
||||
}
|
||||
|
||||
function validateArg {
|
||||
local AD_RE=$(getVar ${ARG_NAME_UPPER}_RE)
|
||||
|
||||
if [[ ! $AD_RE ]]; then
|
||||
echo "Unknown option: $ARG_NAME"
|
||||
usage
|
||||
fi
|
||||
|
||||
if [[ ! $ARG_VALUE =~ ^${AD_RE}$ ]]; then
|
||||
echo "Wrong format: $ARG_NAME"
|
||||
usage;
|
||||
fi
|
||||
|
||||
# validate that the "action" option points to a valid function
|
||||
if [[ $ARG_NAME == "action" ]] && ! isFunction $ARG_VALUE; then
|
||||
echo "No action $ARG_VALUE defined in this script"
|
||||
usage;
|
||||
fi
|
||||
}
|
||||
|
||||
# -- run
|
||||
for i in "$@"
|
||||
do
|
||||
parseArg $i
|
||||
validateArg
|
||||
setVar "${ARG_NAME_UPPER}" "$ARG_VALUE"
|
||||
done
|
||||
}
|
||||
|
||||
function checkMissingArgs {
|
||||
local ARG_NAME
|
||||
for ARG_NAME in "${REQUIRED_ARG_NAMES[@]}"
|
||||
do
|
||||
ARG_VALUE=$(getVar $(varName $ARG_NAME))
|
||||
|
||||
if [[ ! $ARG_VALUE ]]; then
|
||||
echo "Missing: $ARG_NAME"
|
||||
usage;
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# -- run
|
||||
readArgDefs
|
||||
readAndValidateArgs "$@"
|
||||
checkMissingArgs
|
||||
|
||||
}
|
||||
|
||||
# getVar(varName)
|
||||
function getVar {
|
||||
echo ${!1}
|
||||
}
|
||||
|
||||
# setVar(varName, varValue)
|
||||
function setVar {
|
||||
eval "$1=\"$2\""
|
||||
}
|
||||
|
||||
# isFunction(name)
|
||||
# - to be used in an if, so return 0 if successful and 1 if not!
|
||||
function isFunction {
|
||||
if [[ $(type -t $1) == "function" ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# readJsonProp(jsonFile, property)
|
||||
# - restriction: property needs to be on an own line!
|
||||
function readJsonProp {
|
||||
echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1)
|
||||
}
|
||||
|
||||
# replaceJsonProp(jsonFile, propertyRegex, valueRegex, replacePattern)
|
||||
# - note: propertyRegex will be automatically placed into a
|
||||
# capturing group! -> all other groups start at index 2!
|
||||
function replaceJsonProp {
|
||||
replaceInFile $1 '"('$2')"[ ]*:[ ]*"'$3'"' '"\1": "'$4'"'
|
||||
}
|
||||
|
||||
# replaceInFile(file, findPattern, replacePattern)
|
||||
function replaceInFile {
|
||||
sed -i .tmp -E "s/$2/$3/" $1
|
||||
rm $1.tmp
|
||||
}
|
||||
|
||||
# resolveDir(relativeDir)
|
||||
# - resolves a directory relative to the current script
|
||||
function resolveDir {
|
||||
echo $(cd $SCRIPT_DIR; cd $1; pwd)
|
||||
}
|
||||
|
||||
function git_push_dryrun_proxy {
|
||||
echo "## git push dryrun proxy enabled!"
|
||||
export ORIGIN_GIT=$(which git)
|
||||
|
||||
function git {
|
||||
local ARGS=("$@")
|
||||
local RC
|
||||
if [[ $1 == "push" ]]; then
|
||||
ARGS+=("--dry-run" "--porcelain")
|
||||
echo "####### START GIT PUSH DRYRUN #######"
|
||||
echo "${ARGS[@]}"
|
||||
fi
|
||||
if [[ $1 == "commit" ]]; then
|
||||
echo "${ARGS[@]}"
|
||||
fi
|
||||
$ORIGIN_GIT "${ARGS[@]}"
|
||||
RC=$?
|
||||
if [[ $1 == "push" ]]; then
|
||||
echo "####### END GIT PUSH DRYRUN #######"
|
||||
fi
|
||||
return $RC
|
||||
}
|
||||
|
||||
export -f git
|
||||
}
|
||||
|
||||
function main {
|
||||
# normalize the working dir to the directory of the script
|
||||
cd $(dirname $0);SCRIPT_DIR=$(pwd)
|
||||
|
||||
ARG_DEFS+=("[--git-push-dryrun=(true|false)]" "[--verbose=(true|false)]")
|
||||
parseArgs "$@"
|
||||
|
||||
# --git_push_dryrun argument
|
||||
if [[ $GIT_PUSH_DRYRUN == "true" ]]; then
|
||||
git_push_dryrun_proxy
|
||||
fi
|
||||
|
||||
# --verbose argument
|
||||
if [[ $VERBOSE == "true" ]]; then
|
||||
set -x
|
||||
fi
|
||||
|
||||
if isFunction init; then
|
||||
init "$@"
|
||||
fi
|
||||
|
||||
# jump to the function denoted by the --action argument,
|
||||
# otherwise call the "run" function
|
||||
if [[ $ACTION ]]; then
|
||||
$ACTION "$@"
|
||||
else
|
||||
run "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
main "$@"
|
||||
+2
-1
@@ -192,7 +192,8 @@ function isArrayLike(obj) {
|
||||
* is the value of an object property or an array element and `key` is the object property key or
|
||||
* array element index. Specifying a `context` for the function is optional.
|
||||
*
|
||||
* Note: this function was previously known as `angular.foreach`.
|
||||
* It is worth nothing that `.forEach` does not iterate over inherited properties because it filters
|
||||
* using the `hasOwnProperty` method.
|
||||
*
|
||||
<pre>
|
||||
var values = {name: 'misko', gender: 'male'};
|
||||
|
||||
+16
-11
@@ -474,7 +474,7 @@ function annotate(fn) {
|
||||
* constructor function that will be used to instantiate the service instance.
|
||||
*
|
||||
* You should use {@link AUTO.$provide#methods_service $provide.service(class)} if you define your service
|
||||
* as a type/class. This is common when using {@link http://coffeescript.org CoffeeScript}.
|
||||
* as a type/class.
|
||||
*
|
||||
* @param {string} name The name of the instance.
|
||||
* @param {Function} constructor A class (constructor function) that will be instantiated.
|
||||
@@ -482,20 +482,25 @@ function annotate(fn) {
|
||||
*
|
||||
* @example
|
||||
* Here is an example of registering a service using
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
|
||||
* {@link AUTO.$provide#methods_service $provide.service(class)}.
|
||||
* <pre>
|
||||
* class Ping
|
||||
* constructor: (@$http) ->
|
||||
* send: () =>
|
||||
* @$http.get('/ping')
|
||||
*
|
||||
* $provide.service('ping', ['$http', Ping])
|
||||
* $provide.service('ping', ['$http', function($http) {
|
||||
* var Ping = function() {
|
||||
* this.$http = $http;
|
||||
* };
|
||||
*
|
||||
* Ping.prototype.send = function() {
|
||||
* return this.$http.get('/ping');
|
||||
* };
|
||||
*
|
||||
* return Ping;
|
||||
* }]);
|
||||
* </pre>
|
||||
* You would then inject and use this service like this:
|
||||
* <pre>
|
||||
* someModule.controller 'Ctrl', ['ping', (ping) ->
|
||||
* ping.send()
|
||||
* ]
|
||||
* someModule.controller('Ctrl', ['ping', function(ping) {
|
||||
* ping.send();
|
||||
* }]);
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
|
||||
+1
-1
@@ -1957,7 +1957,7 @@ function directiveNormalize(name) {
|
||||
*
|
||||
*
|
||||
* @param {string} name Normalized element attribute name of the property to modify. The name is
|
||||
* revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
|
||||
* reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
|
||||
* property to the original name.
|
||||
* @param {string} value Value to set the attribute to. The value can be an interpolated string.
|
||||
*/
|
||||
|
||||
@@ -336,12 +336,10 @@ forEach(BOOLEAN_ATTR, function(propName, attrName) {
|
||||
ngAttributeAliasDirectives[normalized] = function() {
|
||||
return {
|
||||
priority: 100,
|
||||
compile: function() {
|
||||
return function(scope, element, attr) {
|
||||
scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
|
||||
attr.$set(attrName, !!value);
|
||||
});
|
||||
};
|
||||
link: function(scope, element, attr) {
|
||||
scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
|
||||
attr.$set(attrName, !!value);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -249,10 +249,10 @@ function FormController(element, attrs) {
|
||||
*
|
||||
*
|
||||
* # CSS classes
|
||||
* - `ng-valid` Is set if the form is valid.
|
||||
* - `ng-invalid` Is set if the form is invalid.
|
||||
* - `ng-pristine` Is set if the form is pristine.
|
||||
* - `ng-dirty` Is set if the form is dirty.
|
||||
* - `ng-valid` is set if the form is valid.
|
||||
* - `ng-invalid` is set if the form is invalid.
|
||||
* - `ng-pristine` is set if the form is pristine.
|
||||
* - `ng-dirty` is set if the form is dirty.
|
||||
*
|
||||
*
|
||||
* # Submitting a form and preventing the default action
|
||||
|
||||
+18
-62
@@ -390,6 +390,12 @@ var inputType = {
|
||||
'reset': noop
|
||||
};
|
||||
|
||||
// A helper function to call $setValidity and return the value / undefined,
|
||||
// a pattern that is repeated a lot in the input validation logic.
|
||||
function validate(ctrl, validatorName, validity, value){
|
||||
ctrl.$setValidity(validatorName, validity);
|
||||
return validity ? value : undefined;
|
||||
}
|
||||
|
||||
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// In composition mode, users are still inputing intermediate text buffer,
|
||||
@@ -474,22 +480,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
patternValidator,
|
||||
match;
|
||||
|
||||
var validate = function(regexp, value) {
|
||||
if (ctrl.$isEmpty(value) || regexp.test(value)) {
|
||||
ctrl.$setValidity('pattern', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('pattern', false);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
if (pattern) {
|
||||
var validateRegex = function(regexp, value) {
|
||||
return validate(ctrl, 'pattern', ctrl.$isEmpty(value) || regexp.test(value), value);
|
||||
};
|
||||
match = pattern.match(/^\/(.*)\/([gim]*)$/);
|
||||
if (match) {
|
||||
pattern = new RegExp(match[1], match[2]);
|
||||
patternValidator = function(value) {
|
||||
return validate(pattern, value);
|
||||
return validateRegex(pattern, value);
|
||||
};
|
||||
} else {
|
||||
patternValidator = function(value) {
|
||||
@@ -500,7 +499,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
'Expected {0} to be a RegExp but was {1}. Element: {2}', pattern,
|
||||
patternObj, startingTag(element));
|
||||
}
|
||||
return validate(patternObj, value);
|
||||
return validateRegex(patternObj, value);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -512,13 +511,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
if (attr.ngMinlength) {
|
||||
var minlength = int(attr.ngMinlength);
|
||||
var minLengthValidator = function(value) {
|
||||
if (!ctrl.$isEmpty(value) && value.length < minlength) {
|
||||
ctrl.$setValidity('minlength', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('minlength', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'minlength', ctrl.$isEmpty(value) || value.length >= minlength, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minLengthValidator);
|
||||
@@ -529,13 +522,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
if (attr.ngMaxlength) {
|
||||
var maxlength = int(attr.ngMaxlength);
|
||||
var maxLengthValidator = function(value) {
|
||||
if (!ctrl.$isEmpty(value) && value.length > maxlength) {
|
||||
ctrl.$setValidity('maxlength', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('maxlength', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxLengthValidator);
|
||||
@@ -564,13 +551,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
if (attr.min) {
|
||||
var minValidator = function(value) {
|
||||
var min = parseFloat(attr.min);
|
||||
if (!ctrl.$isEmpty(value) && value < min) {
|
||||
ctrl.$setValidity('min', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('min', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minValidator);
|
||||
@@ -580,13 +561,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
if (attr.max) {
|
||||
var maxValidator = function(value) {
|
||||
var max = parseFloat(attr.max);
|
||||
if (!ctrl.$isEmpty(value) && value > max) {
|
||||
ctrl.$setValidity('max', false);
|
||||
return undefined;
|
||||
} else {
|
||||
ctrl.$setValidity('max', true);
|
||||
return value;
|
||||
}
|
||||
return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value);
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxValidator);
|
||||
@@ -594,14 +569,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
}
|
||||
|
||||
ctrl.$formatters.push(function(value) {
|
||||
|
||||
if (ctrl.$isEmpty(value) || isNumber(value)) {
|
||||
ctrl.$setValidity('number', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('number', false);
|
||||
return undefined;
|
||||
}
|
||||
return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -609,13 +577,7 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var urlValidator = function(value) {
|
||||
if (ctrl.$isEmpty(value) || URL_REGEXP.test(value)) {
|
||||
ctrl.$setValidity('url', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('url', false);
|
||||
return undefined;
|
||||
}
|
||||
return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value);
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(urlValidator);
|
||||
@@ -626,13 +588,7 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
var emailValidator = function(value) {
|
||||
if (ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value)) {
|
||||
ctrl.$setValidity('email', true);
|
||||
return value;
|
||||
} else {
|
||||
ctrl.$setValidity('email', false);
|
||||
return undefined;
|
||||
}
|
||||
return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value);
|
||||
};
|
||||
|
||||
ctrl.$formatters.push(emailValidator);
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
</button>
|
||||
count: {{count}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
<doc:protractor>
|
||||
it('should check ng-click', function() {
|
||||
expect(binding('count')).toBe('0');
|
||||
element('.doc-example-live :button').click();
|
||||
expect(binding('count')).toBe('1');
|
||||
expect(element(by.binding('count')).getText()).toMatch('0');
|
||||
element(by.css('.doc-example-live button')).click();
|
||||
expect(element(by.binding('count')).getText()).toMatch('1');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:protractor>
|
||||
</doc:example>
|
||||
*/
|
||||
/*
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
|
||||
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
|
||||
*
|
||||
* Creating aliases for these properties is possible with {@link api/ng.directive:ngInit `ngInit`}.
|
||||
* This may be useful when, for instance, nesting ngRepeats.
|
||||
*
|
||||
* # Special repeat start and end points
|
||||
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
|
||||
|
||||
@@ -168,11 +168,9 @@ var ngSwitchWhenDirective = ngDirective({
|
||||
transclude: 'element',
|
||||
priority: 800,
|
||||
require: '^ngSwitch',
|
||||
compile: function(element, attrs) {
|
||||
return function(scope, element, attr, ctrl, $transclude) {
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
|
||||
};
|
||||
link: function(scope, element, attrs, ctrl, $transclude) {
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
* @restrict E
|
||||
*
|
||||
* @description
|
||||
* Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the
|
||||
* template can be used by `ngInclude`, `ngView` or directive templates.
|
||||
* Load the content of a `<script>` element into {@link api/ng.$templateCache `$templateCache`}, so that the
|
||||
* template can be used by {@link api/ng.directive:ngInclude `ngInclude`},
|
||||
* {@link api/ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
|
||||
* `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
|
||||
* assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
|
||||
*
|
||||
* @param {'text/ng-template'} type must be set to `'text/ng-template'`
|
||||
* @param {'text/ng-template'} type Must be set to `'text/ng-template'`.
|
||||
* @param {string} id Cache name of the template.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
|
||||
@@ -128,8 +128,8 @@ var ngOptionsMinErr = minErr('ngOptions');
|
||||
var ngOptionsDirective = valueFn({ terminal: true });
|
||||
// jshint maxlen: false
|
||||
var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
//0000111110000000000022220000000000000000000000333300000000000000444444444444444000000000555555555555555000000066666666666666600000000000000007777000000000000000000088888
|
||||
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/,
|
||||
//000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
|
||||
var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
|
||||
nullModelCtrl = {$setViewValue: noop};
|
||||
// jshint maxlen: 100
|
||||
|
||||
|
||||
+1
-2
@@ -6,8 +6,7 @@
|
||||
* @requires $window
|
||||
*
|
||||
* @description
|
||||
* A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document`
|
||||
* element.
|
||||
* A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
|
||||
*/
|
||||
function $DocumentProvider(){
|
||||
this.$get = ['$window', function(window){
|
||||
|
||||
+8
-19
@@ -14,8 +14,8 @@
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
* - `string`: Predicate that results in a substring match using the value of `expression`
|
||||
* string. All strings or objects with string properties in `array` that contain this string
|
||||
* - `string`: The string is evaluated as an expression and the resulting value is used for substring match against
|
||||
* the contents of the `array`. All strings or objects with string properties in `array` that contain this string
|
||||
* will be returned. The predicate can be negated by prefixing the string with `!`.
|
||||
*
|
||||
* - `Object`: A pattern object can be used to filter specific properties on objects contained
|
||||
@@ -173,23 +173,12 @@ function filterFilter() {
|
||||
case "object":
|
||||
// jshint +W086
|
||||
for (var key in expression) {
|
||||
if (key == '$') {
|
||||
(function() {
|
||||
if (!expression[key]) return;
|
||||
var path = key;
|
||||
predicates.push(function(value) {
|
||||
return search(value, expression[path]);
|
||||
});
|
||||
})();
|
||||
} else {
|
||||
(function() {
|
||||
if (typeof(expression[key]) == 'undefined') { return; }
|
||||
var path = key;
|
||||
predicates.push(function(value) {
|
||||
return search(getter(value,path), expression[path]);
|
||||
});
|
||||
})();
|
||||
}
|
||||
(function(path) {
|
||||
if (typeof expression[path] == 'undefined') return;
|
||||
predicates.push(function(value) {
|
||||
return search(path == '$' ? value : getter(value, path), expression[path]);
|
||||
});
|
||||
})(key);
|
||||
}
|
||||
break;
|
||||
case 'function':
|
||||
|
||||
+17
-6
@@ -111,9 +111,9 @@ function $HttpProvider() {
|
||||
common: {
|
||||
'Accept': 'application/json, text/plain, */*'
|
||||
},
|
||||
post: CONTENT_TYPE_APPLICATION_JSON,
|
||||
put: CONTENT_TYPE_APPLICATION_JSON,
|
||||
patch: CONTENT_TYPE_APPLICATION_JSON
|
||||
post: copy(CONTENT_TYPE_APPLICATION_JSON),
|
||||
put: copy(CONTENT_TYPE_APPLICATION_JSON),
|
||||
patch: copy(CONTENT_TYPE_APPLICATION_JSON)
|
||||
},
|
||||
|
||||
xsrfCookieName: 'XSRF-TOKEN',
|
||||
@@ -291,7 +291,15 @@ function $HttpProvider() {
|
||||
* `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
|
||||
*
|
||||
* The defaults can also be set at runtime via the `$http.defaults` object in the same
|
||||
* fashion. In addition, you can supply a `headers` property in the config object passed when
|
||||
* fashion. For example:
|
||||
*
|
||||
* ```
|
||||
* module.run(function($http) {
|
||||
* $http.defaults.headers.common.Authentication = 'Basic YmVlcDpib29w'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* In addition, you can supply a `headers` property in the config object passed when
|
||||
* calling `$http(config)`, which overrides the defaults without changing them globally.
|
||||
*
|
||||
*
|
||||
@@ -315,7 +323,9 @@ function $HttpProvider() {
|
||||
* properties. These properties are by default an array of transform functions, which allows you
|
||||
* to `push` or `unshift` a new transformation function into the transformation chain. You can
|
||||
* also decide to completely override any default transformations by assigning your
|
||||
* transformation functions to these properties directly without the array wrapper.
|
||||
* transformation functions to these properties directly without the array wrapper. These defaults
|
||||
* are again available on the $http factory at run-time, which may be useful if you have run-time
|
||||
* services you wish to be involved in your transformations.
|
||||
*
|
||||
* Similarly, to locally override the request/response transforms, augment the
|
||||
* `transformRequest` and/or `transformResponse` properties of the configuration object passed
|
||||
@@ -529,7 +539,8 @@ function $HttpProvider() {
|
||||
* for added security.
|
||||
*
|
||||
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
|
||||
* properties of either $httpProvider.defaults, or the per-request config object.
|
||||
* properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
|
||||
* or the per-request config object.
|
||||
*
|
||||
*
|
||||
* @param {object} config Object describing the request to be made and how it should be
|
||||
|
||||
+10
-9
@@ -54,7 +54,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
} else {
|
||||
completeRequest(callback, status || -2);
|
||||
}
|
||||
delete callbacks[callbackId];
|
||||
callbacks[callbackId] = angular.noop;
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -71,7 +71,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
// response is in the cache. the promise api will ensure that to the app code the api is
|
||||
// always async
|
||||
xhr.onreadystatechange = function() {
|
||||
// onreadystatechange might by called multiple times with readyState === 4 on mobile webkit caused by
|
||||
// onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
|
||||
// xhrs that are resolved while the app is in the background (see #5426).
|
||||
// since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
|
||||
// continuing
|
||||
@@ -84,11 +84,12 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
|
||||
if(status !== ABORTED) {
|
||||
responseHeaders = xhr.getAllResponseHeaders();
|
||||
response = xhr.responseType ? xhr.response : xhr.responseText;
|
||||
|
||||
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
||||
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
||||
response = ('response' in xhr) ? xhr.response : xhr.responseText;
|
||||
}
|
||||
|
||||
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
||||
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
||||
completeRequest(callback,
|
||||
status || xhr.status,
|
||||
response,
|
||||
@@ -121,14 +122,14 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
}
|
||||
|
||||
function completeRequest(callback, status, response, headersString) {
|
||||
var protocol = urlResolve(url).protocol;
|
||||
|
||||
// cancel timeout and subsequent timeout promise resolution
|
||||
timeoutId && $browserDefer.cancel(timeoutId);
|
||||
jsonpDone = xhr = null;
|
||||
|
||||
// fix status code for file protocol (it's always 0)
|
||||
status = (protocol == 'file' && status === 0) ? (response ? 200 : 404) : status;
|
||||
// fix status code when it is 0 (0 status is undocumented).
|
||||
// Occurs when accessing file resources.
|
||||
// On Android 4.1 stock browser it occurs while retrieving files from application cache.
|
||||
status = (status === 0) ? (response ? 200 : 404) : status;
|
||||
|
||||
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
||||
status = status == 1223 ? 204 : status;
|
||||
|
||||
+1
-1
@@ -576,7 +576,7 @@ function $LocationProvider(){
|
||||
* Broadcasted before a URL will change. This change can be prevented by calling
|
||||
* `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
|
||||
* details about event object. Upon successful change
|
||||
* {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
|
||||
* {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired.
|
||||
*
|
||||
* @param {Object} angularEvent Synthetic event object.
|
||||
* @param {string} newUrl New URL
|
||||
|
||||
+16
-8
@@ -894,16 +894,20 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
if (pathVal == null) return pathVal;
|
||||
pathVal = pathVal[key0];
|
||||
|
||||
if (pathVal == null) return key1 ? undefined : pathVal;
|
||||
if (!key1) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key1];
|
||||
|
||||
if (pathVal == null) return key2 ? undefined : pathVal;
|
||||
if (!key2) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key2];
|
||||
|
||||
if (pathVal == null) return key3 ? undefined : pathVal;
|
||||
if (!key3) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key3];
|
||||
|
||||
if (pathVal == null) return key4 ? undefined : pathVal;
|
||||
if (!key4) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key4];
|
||||
|
||||
return pathVal;
|
||||
@@ -924,8 +928,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (pathVal == null) return key1 ? undefined : pathVal;
|
||||
|
||||
if (!key1) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key1];
|
||||
if (pathVal && pathVal.then) {
|
||||
promiseWarning(fullExp);
|
||||
@@ -936,8 +941,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (pathVal == null) return key2 ? undefined : pathVal;
|
||||
|
||||
if (!key2) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key2];
|
||||
if (pathVal && pathVal.then) {
|
||||
promiseWarning(fullExp);
|
||||
@@ -948,8 +954,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (pathVal == null) return key3 ? undefined : pathVal;
|
||||
|
||||
if (!key3) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key3];
|
||||
if (pathVal && pathVal.then) {
|
||||
promiseWarning(fullExp);
|
||||
@@ -960,8 +967,9 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
|
||||
}
|
||||
pathVal = pathVal.$$v;
|
||||
}
|
||||
if (pathVal == null) return key4 ? undefined : pathVal;
|
||||
|
||||
if (!key4) return pathVal;
|
||||
if (pathVal == null) return undefined;
|
||||
pathVal = pathVal[key4];
|
||||
if (pathVal && pathVal.then) {
|
||||
promiseWarning(fullExp);
|
||||
|
||||
+3
-3
@@ -186,7 +186,7 @@ function $RootScopeProvider(){
|
||||
} else {
|
||||
ChildScope = function() {}; // should be anonymous; This is so that when the minifier munges
|
||||
// the name it does not become random set of chars. This will then show up as class
|
||||
// name in the debugger.
|
||||
// name in the web inspector.
|
||||
ChildScope.prototype = this;
|
||||
child = new ChildScope();
|
||||
child.$id = nextUid();
|
||||
@@ -287,7 +287,7 @@ function $RootScopeProvider(){
|
||||
// No digest has been run so the counter will be zero
|
||||
expect(scope.foodCounter).toEqual(0);
|
||||
|
||||
// Run the digest but since food has not changed cout will still be zero
|
||||
// Run the digest but since food has not changed count will still be zero
|
||||
scope.$digest();
|
||||
expect(scope.foodCounter).toEqual(0);
|
||||
|
||||
@@ -632,7 +632,7 @@ function $RootScopeProvider(){
|
||||
|
||||
// `break traverseScopesLoop;` takes us to here
|
||||
|
||||
if(dirty && !(ttl--)) {
|
||||
if((dirty || asyncQueue.length) && !(ttl--)) {
|
||||
clearPhase();
|
||||
throw $rootScopeMinErr('infdig',
|
||||
'{0} $digest() iterations reached. Aborting!\n' +
|
||||
|
||||
+1
-1
@@ -321,7 +321,7 @@ function $SceDelegateProvider() {
|
||||
*
|
||||
* @param {*} value The result of a prior {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}
|
||||
* call or anything else.
|
||||
* @returns {*} The value the was originally provided to {@link ng.$sceDelegate#methods_trustAs
|
||||
* @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#methods_trustAs
|
||||
* `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
|
||||
* `value` unchanged.
|
||||
*/
|
||||
|
||||
+135
-32
@@ -248,6 +248,26 @@ angular.module('ngAnimate', ['ng'])
|
||||
* Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application.
|
||||
*
|
||||
*/
|
||||
.factory('$$animateReflow', ['$window', '$timeout', function($window, $timeout) {
|
||||
var requestAnimationFrame = $window.requestAnimationFrame ||
|
||||
$window.webkitRequestAnimationFrame ||
|
||||
function(fn) {
|
||||
return $timeout(fn, 10, false);
|
||||
};
|
||||
|
||||
var cancelAnimationFrame = $window.cancelAnimationFrame ||
|
||||
$window.webkitCancelAnimationFrame ||
|
||||
function(timer) {
|
||||
return $timeout.cancel(timer);
|
||||
};
|
||||
return function(fn) {
|
||||
var id = requestAnimationFrame(fn);
|
||||
return function() {
|
||||
cancelAnimationFrame(id);
|
||||
};
|
||||
};
|
||||
}])
|
||||
|
||||
.config(['$provide', '$animateProvider', function($provide, $animateProvider) {
|
||||
var noop = angular.noop;
|
||||
var forEach = angular.forEach;
|
||||
@@ -295,6 +315,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
return classNameFilter.test(className);
|
||||
};
|
||||
|
||||
function async(fn) {
|
||||
return $timeout(fn, 0, false);
|
||||
}
|
||||
|
||||
function lookup(name) {
|
||||
if (name) {
|
||||
var matches = [],
|
||||
@@ -586,6 +610,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//best to catch this early on to prevent any animation operations from occurring
|
||||
if(!node || !isAnimatableClassName(classes)) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -605,6 +631,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case a NO animation is not found.
|
||||
if (animationsDisabled(element, parentElement) || matches.length === 0) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
@@ -643,14 +671,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
//animation do it's thing and close this one early
|
||||
if(animations.length === 0) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var ONE_SPACE = ' ';
|
||||
//this value will be searched for class-based CSS className lookup. Therefore,
|
||||
//we prefix and suffix the current className value with spaces to avoid substring
|
||||
//lookups of className tokens
|
||||
var futureClassName = ' ' + currentClassName + ' ';
|
||||
var futureClassName = ONE_SPACE + currentClassName + ONE_SPACE;
|
||||
if(ngAnimateState.running) {
|
||||
//if an animation is currently running on the element then lets take the steps
|
||||
//to cancel that animation and fire any required callbacks
|
||||
@@ -658,12 +689,23 @@ angular.module('ngAnimate', ['ng'])
|
||||
cleanup(element);
|
||||
cancelAnimations(ngAnimateState.animations);
|
||||
|
||||
//in the event that the CSS is class is quickly added and removed back
|
||||
//then we don't want to wait until after the reflow to add/remove the CSS
|
||||
//class since both class animations may run into a race condition.
|
||||
//The code below will check to see if that is occurring and will
|
||||
//immediately remove the former class before the reflow so that the
|
||||
//animation can snap back to the original animation smoothly
|
||||
var isFullyClassBasedAnimation = isClassBased && !ngAnimateState.structural;
|
||||
var isRevertingClassAnimation = isFullyClassBasedAnimation &&
|
||||
ngAnimateState.className == className &&
|
||||
animationEvent != ngAnimateState.event;
|
||||
|
||||
//if the class is removed during the reflow then it will revert the styles temporarily
|
||||
//back to the base class CSS styling causing a jump-like effect to occur. This check
|
||||
//here ensures that the domOperation is only performed after the reflow has commenced
|
||||
if(ngAnimateState.beforeComplete) {
|
||||
if(ngAnimateState.beforeComplete || isRevertingClassAnimation) {
|
||||
(ngAnimateState.done || noop)(true);
|
||||
} else if(isClassBased && !ngAnimateState.structural) {
|
||||
} else if(isFullyClassBasedAnimation) {
|
||||
//class-based animations will compare element className values after cancelling the
|
||||
//previous animation to see if the element properties already contain the final CSS
|
||||
//class and if so then the animation will be skipped. Since the domOperation will
|
||||
@@ -671,8 +713,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
//will be invalid. Therefore the same string manipulation that would occur within the
|
||||
//DOM operation will be performed below so that the class comparison is valid...
|
||||
futureClassName = ngAnimateState.event == 'removeClass' ?
|
||||
futureClassName.replace(ngAnimateState.className, '') :
|
||||
futureClassName + ngAnimateState.className + ' ';
|
||||
futureClassName.replace(ONE_SPACE + ngAnimateState.className + ONE_SPACE, ONE_SPACE) :
|
||||
futureClassName + ngAnimateState.className + ONE_SPACE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,10 +722,12 @@ angular.module('ngAnimate', ['ng'])
|
||||
//(on addClass) or doesn't contain (on removeClass) the className being animated.
|
||||
//The reason why this is being called after the previous animations are cancelled
|
||||
//is so that the CSS classes present on the element can be properly examined.
|
||||
var classNameToken = ' ' + className + ' ';
|
||||
var classNameToken = ONE_SPACE + className + ONE_SPACE;
|
||||
if((animationEvent == 'addClass' && futureClassName.indexOf(classNameToken) >= 0) ||
|
||||
(animationEvent == 'removeClass' && futureClassName.indexOf(classNameToken) == -1)) {
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
return;
|
||||
}
|
||||
@@ -724,6 +768,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
function invokeRegisteredAnimationFns(animations, phase, allAnimationFnsComplete) {
|
||||
phase == 'after' ?
|
||||
fireAfterCallbackAsync() :
|
||||
fireBeforeCallbackAsync();
|
||||
|
||||
var endFnName = phase + 'End';
|
||||
forEach(animations, function(animation, index) {
|
||||
var animationPhaseCompleted = function() {
|
||||
@@ -760,8 +808,27 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
function fireDOMCallback(animationPhase) {
|
||||
element.triggerHandler('$animate:' + animationPhase, {
|
||||
event : animationEvent,
|
||||
className : className
|
||||
});
|
||||
}
|
||||
|
||||
function fireBeforeCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('before');
|
||||
});
|
||||
}
|
||||
|
||||
function fireAfterCallbackAsync() {
|
||||
async(function() {
|
||||
fireDOMCallback('after');
|
||||
});
|
||||
}
|
||||
|
||||
function fireDoneCallbackAsync() {
|
||||
doneCallback && $timeout(doneCallback, 0, false);
|
||||
doneCallback && async(doneCallback);
|
||||
}
|
||||
|
||||
//it is less complicated to use a flag than managing and cancelling
|
||||
@@ -785,9 +852,9 @@ angular.module('ngAnimate', ['ng'])
|
||||
if(isClassBased) {
|
||||
cleanup(element);
|
||||
} else {
|
||||
data.closeAnimationTimeout = $timeout(function() {
|
||||
data.closeAnimationTimeout = async(function() {
|
||||
cleanup(element);
|
||||
}, 0, false);
|
||||
});
|
||||
element.data(NG_ANIMATE_STATE, data);
|
||||
}
|
||||
}
|
||||
@@ -811,10 +878,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
function cancelAnimations(animations) {
|
||||
var isCancelledFlag = true;
|
||||
forEach(animations, function(animation) {
|
||||
if(!animations.beforeComplete) {
|
||||
if(!animation.beforeComplete) {
|
||||
(animation.beforeEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
if(!animations.afterComplete) {
|
||||
if(!animation.afterComplete) {
|
||||
(animation.afterEnd || noop)(isCancelledFlag);
|
||||
}
|
||||
});
|
||||
@@ -860,7 +927,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}]);
|
||||
|
||||
$animateProvider.register('', ['$window', '$sniffer', '$timeout', function($window, $sniffer, $timeout) {
|
||||
$animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow',
|
||||
function($window, $sniffer, $timeout, $$animateReflow) {
|
||||
// Detect proper transitionend/animationend event names.
|
||||
var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT;
|
||||
|
||||
@@ -905,11 +973,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
var parentCounter = 0;
|
||||
var animationReflowQueue = [];
|
||||
var animationElementQueue = [];
|
||||
var animationTimer;
|
||||
var cancelAnimationReflow;
|
||||
var closingAnimationTime = 0;
|
||||
var timeOut = false;
|
||||
function afterReflow(element, callback) {
|
||||
$timeout.cancel(animationTimer);
|
||||
if(cancelAnimationReflow) {
|
||||
cancelAnimationReflow();
|
||||
}
|
||||
|
||||
animationReflowQueue.push(callback);
|
||||
|
||||
@@ -918,15 +988,19 @@ angular.module('ngAnimate', ['ng'])
|
||||
animationElementQueue.push(element);
|
||||
|
||||
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
|
||||
closingAnimationTime = Math.max(closingAnimationTime,
|
||||
(elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER * ONE_SECOND);
|
||||
|
||||
var stagger = elementData.stagger;
|
||||
var staggerTime = elementData.itemIndex * (Math.max(stagger.animationDelay, stagger.transitionDelay) || 0);
|
||||
|
||||
var animationTime = (elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER;
|
||||
closingAnimationTime = Math.max(closingAnimationTime, (staggerTime + animationTime) * ONE_SECOND);
|
||||
|
||||
//by placing a counter we can avoid an accidental
|
||||
//race condition which may close an animation when
|
||||
//a follow-up animation is midway in its animation
|
||||
elementData.animationCount = animationCounter;
|
||||
|
||||
animationTimer = $timeout(function() {
|
||||
cancelAnimationReflow = $$animateReflow(function() {
|
||||
forEach(animationReflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
@@ -947,11 +1021,11 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
animationReflowQueue = [];
|
||||
animationElementQueue = [];
|
||||
animationTimer = null;
|
||||
cancelAnimationReflow = null;
|
||||
lookupCache = {};
|
||||
closingAnimationTime = 0;
|
||||
animationCounter++;
|
||||
}, 10, false);
|
||||
});
|
||||
}
|
||||
|
||||
function closeAllAnimations(elements, count) {
|
||||
@@ -1042,13 +1116,13 @@ angular.module('ngAnimate', ['ng'])
|
||||
return parentID + '-' + extractElementNode(element).className;
|
||||
}
|
||||
|
||||
function animateSetup(element, className) {
|
||||
function animateSetup(element, className, calculationDecorator) {
|
||||
var cacheKey = getCacheKey(element);
|
||||
var eventCacheKey = cacheKey + ' ' + className;
|
||||
var stagger = {};
|
||||
var ii = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
||||
var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0;
|
||||
|
||||
if(ii > 0) {
|
||||
if(itemIndex > 0) {
|
||||
var staggerClassName = className + '-stagger';
|
||||
var staggerCacheKey = cacheKey + ' ' + staggerClassName;
|
||||
var applyClasses = !lookupCache[staggerCacheKey];
|
||||
@@ -1060,9 +1134,16 @@ angular.module('ngAnimate', ['ng'])
|
||||
applyClasses && element.removeClass(staggerClassName);
|
||||
}
|
||||
|
||||
/* the animation itself may need to add/remove special CSS classes
|
||||
* before calculating the anmation styles */
|
||||
calculationDecorator = calculationDecorator ||
|
||||
function(fn) { return fn(); };
|
||||
|
||||
element.addClass(className);
|
||||
|
||||
var timings = getElementAnimationDetails(element, eventCacheKey);
|
||||
var timings = calculationDecorator(function() {
|
||||
return getElementAnimationDetails(element, eventCacheKey);
|
||||
});
|
||||
|
||||
/* there is no point in performing a reflow if the animation
|
||||
timeout is empty (this would cause a flicker bug normally
|
||||
@@ -1094,7 +1175,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
classes : className + ' ' + activeClassName,
|
||||
timings : timings,
|
||||
stagger : stagger,
|
||||
ii : ii
|
||||
itemIndex : itemIndex
|
||||
});
|
||||
|
||||
return true;
|
||||
@@ -1139,7 +1220,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
|
||||
var startTime = Date.now();
|
||||
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
|
||||
var ii = elementData.ii;
|
||||
var itemIndex = elementData.itemIndex;
|
||||
|
||||
var style = '', appliedStyles = [];
|
||||
if(timings.transitionDuration > 0) {
|
||||
@@ -1152,17 +1233,17 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
}
|
||||
|
||||
if(ii > 0) {
|
||||
if(itemIndex > 0) {
|
||||
if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
|
||||
var delayStyle = timings.transitionDelayStyle;
|
||||
style += CSS_PREFIX + 'transition-delay: ' +
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
|
||||
prepareStaggerDelay(delayStyle, stagger.transitionDelay, itemIndex) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-delay');
|
||||
}
|
||||
|
||||
if(stagger.animationDelay > 0 && stagger.animationDuration === 0) {
|
||||
style += CSS_PREFIX + 'animation-delay: ' +
|
||||
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, ii) + '; ';
|
||||
prepareStaggerDelay(timings.animationDelayStyle, stagger.animationDelay, itemIndex) + '; ';
|
||||
appliedStyles.push(CSS_PREFIX + 'animation-delay');
|
||||
}
|
||||
}
|
||||
@@ -1227,8 +1308,8 @@ angular.module('ngAnimate', ['ng'])
|
||||
return style;
|
||||
}
|
||||
|
||||
function animateBefore(element, className) {
|
||||
if(animateSetup(element, className)) {
|
||||
function animateBefore(element, className, calculationDecorator) {
|
||||
if(animateSetup(element, className, calculationDecorator)) {
|
||||
return function(cancelled) {
|
||||
cancelled && animateClose(element, className);
|
||||
};
|
||||
@@ -1323,7 +1404,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
},
|
||||
|
||||
beforeAddClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'), function(fn) {
|
||||
|
||||
/* when a CSS class is added to an element then the transition style that
|
||||
* is applied is the transition defined on the element when the CSS class
|
||||
* is added at the time of the animation. This is how CSS3 functions
|
||||
* outside of ngAnimate. */
|
||||
element.addClass(className);
|
||||
var timings = fn();
|
||||
element.removeClass(className);
|
||||
return timings;
|
||||
});
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
@@ -1340,7 +1432,18 @@ angular.module('ngAnimate', ['ng'])
|
||||
},
|
||||
|
||||
beforeRemoveClass : function(element, className, animationCompleted) {
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
|
||||
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'), function(fn) {
|
||||
/* when classes are removed from an element then the transition style
|
||||
* that is applied is the transition defined on the element without the
|
||||
* CSS class being there. This is how CSS3 functions outside of ngAnimate.
|
||||
* http://plnkr.co/edit/j8OzgTNxHTb4n3zLyjGW?p=preview */
|
||||
var klass = element.attr('class');
|
||||
element.removeClass(className);
|
||||
var timings = fn();
|
||||
element.attr('class', klass);
|
||||
return timings;
|
||||
});
|
||||
|
||||
if(cancellationMethod) {
|
||||
afterReflow(element, function() {
|
||||
unblockTransitions(element);
|
||||
|
||||
Vendored
+47
-2
@@ -756,6 +756,36 @@ angular.mock.TzDate = function (offset, timestamp) {
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
/* jshint +W101 */
|
||||
|
||||
// TODO(matias): remove this IMMEDIATELY once we can properly detect the
|
||||
// presence of a registered module
|
||||
var animateLoaded;
|
||||
try {
|
||||
angular.module('ngAnimate');
|
||||
animateLoaded = true;
|
||||
} catch(e) {}
|
||||
|
||||
if(animateLoaded) {
|
||||
angular.module('ngAnimate').config(['$provide', function($provide) {
|
||||
var reflowQueue = [];
|
||||
$provide.value('$$animateReflow', function(fn) {
|
||||
reflowQueue.push(fn);
|
||||
return angular.noop;
|
||||
});
|
||||
$provide.decorator('$animate', function($delegate) {
|
||||
$delegate.triggerReflow = function() {
|
||||
if(reflowQueue.length === 0) {
|
||||
throw new Error('No animation reflows present');
|
||||
}
|
||||
angular.forEach(reflowQueue, function(fn) {
|
||||
fn();
|
||||
});
|
||||
reflowQueue = [];
|
||||
};
|
||||
return $delegate;
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
||||
angular.mock.animate = angular.module('mock.animate', ['ng'])
|
||||
|
||||
.config(['$provide', function($provide) {
|
||||
@@ -1913,7 +1943,6 @@ angular.mock.clearDataCache = function() {
|
||||
};
|
||||
|
||||
|
||||
|
||||
if(window.jasmine || window.mocha) {
|
||||
|
||||
var currentSpec = null,
|
||||
@@ -2079,6 +2108,20 @@ if(window.jasmine || window.mocha) {
|
||||
*
|
||||
* @param {...Function} fns any number of functions which will be injected using the injector.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
var ErrorAddingDeclarationLocationStack = function(e, errorForStack) {
|
||||
this.message = e.message;
|
||||
this.name = e.name;
|
||||
if (e.line) this.line = e.line;
|
||||
if (e.sourceId) this.sourceId = e.sourceId;
|
||||
if (e.stack && errorForStack)
|
||||
this.stack = e.stack + '\n' + errorForStack.stack;
|
||||
if (e.stackArray) this.stackArray = e.stackArray;
|
||||
};
|
||||
ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
|
||||
|
||||
window.inject = angular.mock.inject = function() {
|
||||
var blockFns = Array.prototype.slice.call(arguments, 0);
|
||||
var errorForStack = new Error('Declaration Location');
|
||||
@@ -2099,7 +2142,9 @@ if(window.jasmine || window.mocha) {
|
||||
injector.invoke(blockFns[i] || angular.noop, this);
|
||||
/* jshint +W040 */
|
||||
} catch (e) {
|
||||
if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack;
|
||||
if (e.stack && errorForStack) {
|
||||
throw new ErrorAddingDeclarationLocationStack(e, errorForStack);
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
errorForStack = null;
|
||||
|
||||
@@ -90,7 +90,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
* when a param value needs to be obtained for a request (unless the param was overridden).
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url seapph query after the `?`.
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
*
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
|
||||
@@ -26,6 +26,15 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
|
||||
*
|
||||
* @scope
|
||||
* @priority 400
|
||||
* @param {string=} onload Expression to evaluate whenever the view updates.
|
||||
*
|
||||
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
|
||||
* $anchorScroll} to scroll the viewport after the view is updated.
|
||||
*
|
||||
* - If the attribute is not set, disable scrolling.
|
||||
* - If the attribute is set without value, enable scrolling.
|
||||
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
|
||||
* as an expression yields a truthy value.
|
||||
* @example
|
||||
<example module="ngViewExample" deps="angular-route.js" animations="true">
|
||||
<file name="index.html">
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
describe('docs.angularjs.org', function () {
|
||||
describe('App', function () {
|
||||
// it('should filter the module list when searching', function () {
|
||||
// browser.get();
|
||||
// browser.waitForAngular();
|
||||
|
||||
// var search = element(by.input('q'));
|
||||
// search.clear();
|
||||
// search.sendKeys('ngBind');
|
||||
|
||||
// var firstModule = element(by.css('.search-results a'));
|
||||
// expect(firstModule.getText()).toEqual('ngBind');
|
||||
// });
|
||||
|
||||
|
||||
it('should change the page content when clicking a link to a service', function () {
|
||||
browser.get('');
|
||||
|
||||
var ngBindLink = element(by.css('.definition-table td a[href="api/ng.directive:ngClick"]'));
|
||||
ngBindLink.click();
|
||||
|
||||
var pageBody = element(by.css('.content h1 code'));
|
||||
expect(pageBody.getText()).toEqual('ngClick');
|
||||
});
|
||||
|
||||
|
||||
it('should show the functioning input directive example', function () {
|
||||
browser.get('index-nocache.html#!/api/ng.directive:input');
|
||||
//Wait for animation
|
||||
browser.sleep(500);
|
||||
|
||||
var nameInput = element(by.input('user.name'));
|
||||
nameInput.click();
|
||||
nameInput.sendKeys('!!!');
|
||||
|
||||
var code = element(by.css('.doc-example-live tt'));
|
||||
expect(code.getText()).toContain('guest!!!');
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -724,7 +724,7 @@ describe('input', function() {
|
||||
|
||||
describe('minlength', function() {
|
||||
|
||||
it('should invalid shorter than given minlenght', function() {
|
||||
it('should invalid shorter than given minlength', function() {
|
||||
compileInput('<input type="text" ng-model="value" ng-minlength="3" />');
|
||||
|
||||
changeInputValueTo('aa');
|
||||
@@ -738,7 +738,7 @@ describe('input', function() {
|
||||
|
||||
describe('maxlength', function() {
|
||||
|
||||
it('should invalid shorter than given maxlenght', function() {
|
||||
it('should invalid shorter than given maxlength', function() {
|
||||
compileInput('<input type="text" ng-model="value" ng-maxlength="5" />');
|
||||
|
||||
changeInputValueTo('aaaaaaaa');
|
||||
|
||||
@@ -708,6 +708,31 @@ describe('select', function() {
|
||||
expect(sortedHtml(options[0])).toEqual('<option value="regularProperty">visible</option>');
|
||||
});
|
||||
|
||||
it('should allow expressions over multiple lines', function() {
|
||||
scope.isNotFoo = function(item) {
|
||||
return item.name !== 'Foo';
|
||||
};
|
||||
|
||||
createSelect({
|
||||
'ng-options': 'key.id\n' +
|
||||
'for key in object\n' +
|
||||
'| filter:isNotFoo',
|
||||
'ng-model': 'selected'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.object = [{'id': 1, 'name': 'Foo'},
|
||||
{'id': 2, 'name': 'Bar'},
|
||||
{'id': 3, 'name': 'Baz'}];
|
||||
scope.selected = scope.object[0];
|
||||
});
|
||||
|
||||
var options = element.find('option');
|
||||
expect(options.length).toEqual(3);
|
||||
expect(sortedHtml(options[1])).toEqual('<option value="0">2</option>');
|
||||
expect(sortedHtml(options[2])).toEqual('<option value="1">3</option>');
|
||||
});
|
||||
|
||||
describe('binding', function() {
|
||||
|
||||
it('should bind to scope value', function() {
|
||||
|
||||
@@ -60,6 +60,17 @@ describe('Filter: filter', function() {
|
||||
expect(filter(items, {first:'misko', last:'hevery'})[0]).toEqual(items[0]);
|
||||
});
|
||||
|
||||
it('should match any properties for given "$" property', function() {
|
||||
var items = [{first: 'tom', last: 'hevery'},
|
||||
{first: 'adam', last: 'hevery', alias: 'tom', done: false},
|
||||
{first: 'john', last: 'clark', middle: 'tommy'}];
|
||||
expect(filter(items, {$: 'tom'}).length).toBe(3);
|
||||
expect(filter(items, {$: 'a'}).length).toBe(2);
|
||||
expect(filter(items, {$: false}).length).toBe(1);
|
||||
expect(filter(items, {$: 10}).length).toBe(0);
|
||||
expect(filter(items, {$: 'hevery'})[0]).toEqual(items[0]);
|
||||
});
|
||||
|
||||
it('should support boolean properties', function() {
|
||||
var items = [{name: 'tom', current: true},
|
||||
{name: 'demi', current: false},
|
||||
|
||||
+46
-17
@@ -264,21 +264,45 @@ describe('$httpBackend', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should set responseType and return xhr.response', function() {
|
||||
$backend('GET', '/whatever', null, callback, {}, null, null, 'blob');
|
||||
describe('responseType', function() {
|
||||
|
||||
var xhrInstance = MockXhr.$$lastInstance;
|
||||
expect(xhrInstance.responseType).toBe('blob');
|
||||
it('should set responseType and return xhr.response', function() {
|
||||
$backend('GET', '/whatever', null, callback, {}, null, null, 'blob');
|
||||
|
||||
callback.andCallFake(function(status, response) {
|
||||
expect(response).toBe(xhrInstance.response);
|
||||
var xhrInstance = MockXhr.$$lastInstance;
|
||||
expect(xhrInstance.responseType).toBe('blob');
|
||||
|
||||
callback.andCallFake(function(status, response) {
|
||||
expect(response).toBe(xhrInstance.response);
|
||||
});
|
||||
|
||||
xhrInstance.response = {some: 'object'};
|
||||
xhrInstance.readyState = 4;
|
||||
xhrInstance.onreadystatechange();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
xhrInstance.response = {some: 'object'};
|
||||
xhrInstance.readyState = 4;
|
||||
xhrInstance.onreadystatechange();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
it('should read responseText if response was not defined', function() {
|
||||
// old browsers like IE8, don't support responseType, so they always respond with responseText
|
||||
|
||||
$backend('GET', '/whatever', null, callback, {}, null, null, 'blob');
|
||||
|
||||
var xhrInstance = MockXhr.$$lastInstance;
|
||||
var responseText = '{"some": "object"}';
|
||||
expect(xhrInstance.responseType).toBe('blob');
|
||||
|
||||
callback.andCallFake(function(status, response) {
|
||||
expect(response).toBe(responseText);
|
||||
});
|
||||
|
||||
xhrInstance.responseText = responseText;
|
||||
xhrInstance.readyState = 4;
|
||||
xhrInstance.onreadystatechange();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -330,7 +354,7 @@ describe('$httpBackend', function() {
|
||||
script.onload();
|
||||
}
|
||||
|
||||
expect(callbacks[callbackId]).toBeUndefined();
|
||||
expect(callbacks[callbackId]).toBe(angular.noop);
|
||||
expect(fakeDocument.body.removeChild).toHaveBeenCalledOnceWith(script);
|
||||
});
|
||||
|
||||
@@ -397,7 +421,7 @@ describe('$httpBackend', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should abort request on timeout', function() {
|
||||
it('should abort request on timeout and replace callback with noop', function() {
|
||||
callback.andCallFake(function(status, response) {
|
||||
expect(status).toBe(-1);
|
||||
});
|
||||
@@ -406,9 +430,14 @@ describe('$httpBackend', function() {
|
||||
expect(fakeDocument.$$scripts.length).toBe(1);
|
||||
expect(fakeTimeout.delays[0]).toBe(2000);
|
||||
|
||||
var script = fakeDocument.$$scripts.shift(),
|
||||
callbackId = script.src.match(SCRIPT_URL)[2];
|
||||
|
||||
fakeTimeout.flush();
|
||||
expect(fakeDocument.$$scripts.length).toBe(0);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
||||
expect(callbacks[callbackId]).toBe(angular.noop);
|
||||
});
|
||||
|
||||
|
||||
@@ -416,7 +445,7 @@ describe('$httpBackend', function() {
|
||||
// TODO(vojta): test whether it fires "async-end" on both success and error
|
||||
});
|
||||
|
||||
describe('file protocol', function() {
|
||||
describe('protocols that return 0 status code', function() {
|
||||
|
||||
function respond(status, content) {
|
||||
xhr = MockXhr.$$lastInstance;
|
||||
@@ -430,7 +459,7 @@ describe('$httpBackend', function() {
|
||||
it('should convert 0 to 200 if content', function() {
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'file:///whatever/index.html', null, callback);
|
||||
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
|
||||
respond(0, 'SOME CONTENT');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
@@ -441,7 +470,7 @@ describe('$httpBackend', function() {
|
||||
it('should convert 0 to 404 if no content', function() {
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'file:///whatever/index.html', null, callback);
|
||||
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
|
||||
respond(0, '');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
@@ -457,10 +486,10 @@ describe('$httpBackend', function() {
|
||||
hash : "#/C:/",
|
||||
host : "",
|
||||
hostname : "",
|
||||
href : "file:///C:/base#!/C:/foo",
|
||||
href : "someProtocol:///C:/base#!/C:/foo",
|
||||
pathname : "/C:/foo",
|
||||
port : "",
|
||||
protocol : "file:",
|
||||
protocol : "someProtocol:",
|
||||
search : "",
|
||||
setAttribute: angular.noop
|
||||
};
|
||||
|
||||
@@ -1439,6 +1439,12 @@ describe('$http', function() {
|
||||
$http.get('/url');
|
||||
$httpBackend.flush();
|
||||
});
|
||||
|
||||
it('should have seperate opbjects for defaults PUT and POST', function() {
|
||||
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.put);
|
||||
expect($http.defaults.headers.post).not.toBe($http.defaults.headers.patch);
|
||||
expect($http.defaults.headers.put).not.toBe($http.defaults.headers.patch);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+58
-5
@@ -204,13 +204,24 @@ describe('parser', function() {
|
||||
|
||||
describe('csp: ' + cspEnabled + ", unwrapPromises: " + unwrapPromisesEnabled, function() {
|
||||
|
||||
beforeEach(module(function ($parseProvider) {
|
||||
var originalSecurityPolicy;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
originalSecurityPolicy = window.document.securityPolicy;
|
||||
window.document.securityPolicy = {isActive : cspEnabled};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
window.document.securityPolicy = originalSecurityPolicy;
|
||||
});
|
||||
|
||||
beforeEach(module(function ($parseProvider, $provide) {
|
||||
$parseProvider.unwrapPromises(unwrapPromisesEnabled);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($rootScope, $sniffer) {
|
||||
beforeEach(inject(function ($rootScope) {
|
||||
scope = $rootScope;
|
||||
$sniffer.csp = cspEnabled;
|
||||
}));
|
||||
|
||||
it('should parse expressions', function() {
|
||||
@@ -344,6 +355,25 @@ describe('parser', function() {
|
||||
expect(scope.$eval("a.b.c.d.e.f.g.h.i.j.k.l.m.n", scope)).toBe('nooo!');
|
||||
});
|
||||
|
||||
forEach([2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 42, 99], function(pathLength) {
|
||||
it('should resolve nested paths of length ' + pathLength, function() {
|
||||
// Create a nested object {x2: {x3: {x4: ... {x[n]: 42} ... }}}.
|
||||
var obj = 42;
|
||||
for (var i = pathLength; i >= 2; i--) {
|
||||
var newObj = {};
|
||||
newObj['x' + i] = obj;
|
||||
obj = newObj;
|
||||
}
|
||||
// Assign to x1 and build path 'x1.x2.x3. ... .x[n]' to access the final value.
|
||||
scope.x1 = obj;
|
||||
var path = 'x1';
|
||||
for (var i = 2; i <= pathLength; i++) {
|
||||
path += '.x' + i;
|
||||
}
|
||||
expect(scope.$eval(path)).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be forgiving', function() {
|
||||
scope.a = {b: 23};
|
||||
expect(scope.$eval('b')).toBeUndefined();
|
||||
@@ -1069,6 +1099,17 @@ describe('parser', function() {
|
||||
|
||||
var $log;
|
||||
var PROMISE_WARNING_REGEXP = /\[\$parse\] Promise found in the expression `[^`]+`. Automatic unwrapping of promises in Angular expressions is deprecated\./;
|
||||
var originalSecurityPolicy;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
originalSecurityPolicy = window.document.securityPolicy;
|
||||
window.document.securityPolicy = {isActive : cspEnabled};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
window.document.securityPolicy = originalSecurityPolicy;
|
||||
});
|
||||
|
||||
beforeEach(module(function($parseProvider) {
|
||||
$parseProvider.unwrapPromises(true);
|
||||
@@ -1142,15 +1183,27 @@ describe('parser', function() {
|
||||
|
||||
describe('csp ' + cspEnabled, function() {
|
||||
|
||||
var originalSecurityPolicy;
|
||||
|
||||
|
||||
beforeEach(function() {
|
||||
originalSecurityPolicy = window.document.securityPolicy;
|
||||
window.document.securityPolicy = {isActive : cspEnabled};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
window.document.securityPolicy = originalSecurityPolicy;
|
||||
});
|
||||
|
||||
|
||||
beforeEach(module(function($parseProvider) {
|
||||
$parseProvider.unwrapPromises(true);
|
||||
$parseProvider.logPromiseWarnings(false);
|
||||
}));
|
||||
|
||||
|
||||
beforeEach(inject(function($rootScope, $sniffer, $q) {
|
||||
beforeEach(inject(function($rootScope, $q) {
|
||||
scope = $rootScope;
|
||||
$sniffer.csp = cspEnabled;
|
||||
|
||||
q = $q;
|
||||
deferred = q.defer();
|
||||
|
||||
@@ -258,6 +258,31 @@ describe('Scope', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should prevent infinite loop when creating and resolving a promise in a watched expression', function() {
|
||||
module(function($rootScopeProvider) {
|
||||
$rootScopeProvider.digestTtl(10);
|
||||
});
|
||||
inject(function($rootScope, $q) {
|
||||
var d = $q.defer();
|
||||
|
||||
d.resolve('Hello, world.');
|
||||
$rootScope.$watch(function () {
|
||||
var $d2 = $q.defer();
|
||||
$d2.resolve('Goodbye.');
|
||||
$d2.promise.then(function () { });
|
||||
return d.promise;
|
||||
}, function () { return 0; });
|
||||
|
||||
expect(function() {
|
||||
$rootScope.$digest();
|
||||
}).toThrowMinErr('$rootScope', 'infdig', '10 $digest() iterations reached. Aborting!\n'+
|
||||
'Watchers fired in the last 5 iterations: []');
|
||||
|
||||
expect($rootScope.$$phase).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not fire upon $watch registration on initial $digest', inject(function($rootScope) {
|
||||
var log = '';
|
||||
$rootScope.a = 1;
|
||||
|
||||
+343
-110
@@ -280,7 +280,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -298,7 +298,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-leave')).toBe(true);
|
||||
expect(child.hasClass('ng-leave-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -322,7 +322,7 @@ describe("ngAnimate", function() {
|
||||
$animate.move(child1, element, child2);
|
||||
$rootScope.$digest();
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element.text()).toBe('21');
|
||||
}));
|
||||
@@ -336,7 +336,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeHidden();
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(child.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -354,7 +354,7 @@ describe("ngAnimate", function() {
|
||||
expect(child).toBeShown();
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(child.hasClass('ng-hide-add-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -374,7 +374,7 @@ describe("ngAnimate", function() {
|
||||
//enter
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.attr('class')).toContain('ng-enter');
|
||||
expect(child.attr('class')).toContain('ng-enter-active');
|
||||
@@ -385,7 +385,7 @@ describe("ngAnimate", function() {
|
||||
element.append(after);
|
||||
$animate.move(child, element, after);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.attr('class')).toContain('ng-move');
|
||||
expect(child.attr('class')).toContain('ng-move-active');
|
||||
@@ -394,14 +394,14 @@ describe("ngAnimate", function() {
|
||||
|
||||
//hide
|
||||
$animate.addClass(child, 'ng-hide');
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-hide-add');
|
||||
expect(child.attr('class')).toContain('ng-hide-add-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
|
||||
//show
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-hide-remove');
|
||||
expect(child.attr('class')).toContain('ng-hide-remove-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -409,7 +409,7 @@ describe("ngAnimate", function() {
|
||||
//leave
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.attr('class')).toContain('ng-leave');
|
||||
expect(child.attr('class')).toContain('ng-leave-active');
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -435,7 +435,7 @@ describe("ngAnimate", function() {
|
||||
element.addClass('ng-hide');
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element.text()).toBe('memento');
|
||||
}));
|
||||
@@ -455,7 +455,9 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(child).toBeHidden(); //hides instantly
|
||||
|
||||
//lets change this to prove that done doesn't fire anymore for the previous hide() operation
|
||||
@@ -485,7 +487,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
//this is to verify that the existing style is appended with a semicolon automatically
|
||||
expect(child.attr('style')).toMatch(/width: 20px;.+?/i);
|
||||
@@ -504,6 +506,7 @@ describe("ngAnimate", function() {
|
||||
child.addClass('custom-delay ng-hide');
|
||||
$animate.removeClass(child, 'ng-hide');
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
$timeout.flush(2000);
|
||||
@@ -530,7 +533,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(completed).toBe(false);
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
$timeout.flush();
|
||||
@@ -661,7 +664,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 4000, elapsedTime: 4 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -686,7 +689,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 6000, elapsedTime: 6 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -713,7 +716,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'animationend', { timeStamp : Date.now() + 20000, elapsedTime: 10 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -751,7 +754,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.animations) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
}
|
||||
@@ -762,7 +765,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
if($sniffer.animations) { //cleanup some pending animations
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-add-active')).toBe(true);
|
||||
browserTrigger(element,'animationend', { timeStamp: Date.now() + 2000, elapsedTime: 2 });
|
||||
@@ -806,7 +809,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/animation-delay: 0\.1\d*s/);
|
||||
@@ -823,7 +826,13 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
var expectFailure = true;
|
||||
try {
|
||||
$animate.triggerReflow();
|
||||
expectFailure = false;
|
||||
} catch(e) {}
|
||||
|
||||
expect(expectFailure).toBe(true);
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).not.toMatch(/animation-delay: 0\.1\d*s/);
|
||||
@@ -859,7 +868,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/animation-delay: 1\.1\d*s,\s*2\.1\d*s/);
|
||||
@@ -896,7 +905,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
@@ -920,7 +929,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
@@ -948,7 +957,6 @@ describe("ngAnimate", function() {
|
||||
|
||||
element.addClass('ng-hide');
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
$timeout.flush(0);
|
||||
expect(element).toBeShown();
|
||||
$animate.enabled(true);
|
||||
|
||||
@@ -957,7 +965,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 3000, elapsedTime: 3 });
|
||||
@@ -985,7 +993,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
var now = Date.now();
|
||||
browserTrigger(element,'transitionend', { timeStamp: now + 1000, elapsedTime: 1 });
|
||||
@@ -1014,7 +1022,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(element).toBeShown();
|
||||
if ($sniffer.transitions) {
|
||||
@@ -1039,7 +1047,7 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-remove')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-remove-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -1051,7 +1059,7 @@ describe("ngAnimate", function() {
|
||||
$animate.addClass(element, 'ng-hide');
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('ng-hide-add')).toBe(true);
|
||||
expect(element.hasClass('ng-hide-add-active')).toBe(true);
|
||||
}
|
||||
@@ -1092,7 +1100,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).toMatch(/transition-delay: 0\.1\d*s/);
|
||||
@@ -1109,7 +1117,14 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
var expectFailure = true;
|
||||
try {
|
||||
$animate.triggerReflow();
|
||||
expectFailure = false;
|
||||
} catch(e) {}
|
||||
|
||||
expect(expectFailure).toBe(true);
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
expect(elements[1].attr('style')).not.toMatch(/transition-delay: 0\.1\d*s/);
|
||||
@@ -1145,7 +1160,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toMatch(/transition-duration: 1\d*s,\s*3\d*s;/);
|
||||
expect(elements[0].attr('style')).not.toContain('transition-delay');
|
||||
@@ -1155,7 +1170,7 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
|
||||
|
||||
it("apply a closing timeout to close all pending transitions",
|
||||
it("should apply a closing timeout to close all pending transitions",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
@@ -1167,13 +1182,55 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$timeout.flush(7500); //closing timeout
|
||||
expect(element.hasClass('some-class-add-active')).toBe(false);
|
||||
}));
|
||||
|
||||
it("apply a closing timeout with respect to a staggering animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.entering-element.ng-enter',
|
||||
'-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
ss.addRule('.entering-element.ng-enter-stagger',
|
||||
'-webkit-transition-delay:0.5s;' +
|
||||
'transition-delay:0.5s;');
|
||||
|
||||
element = $compile(html('<div></div>'))($rootScope);
|
||||
var kids = [];
|
||||
for(var i = 0; i < 5; i++) {
|
||||
kids.push(angular.element('<div class="entering-element"></div>'));
|
||||
$animate.enter(kids[i], element);
|
||||
}
|
||||
$rootScope.$digest();
|
||||
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.children().length).toBe(5);
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
$timeout.flush(7500);
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
//(stagger * index) + (duration + delay) * 150%
|
||||
$timeout.flush(9500); //0.5 * 4 + 5 * 1.5 = 9500;
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
expect(kids[i].hasClass('ng-enter-active')).toBe(false);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
it("should not allow the closing animation to close off a successive animation midway",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $timeout) {
|
||||
@@ -1189,12 +1246,12 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //reflow
|
||||
$animate.triggerReflow(); //reflow
|
||||
expect(element.hasClass('some-class-add-active')).toBe(true);
|
||||
|
||||
$animate.removeClass(element, 'some-class');
|
||||
|
||||
$timeout.flush(10); //second reflow
|
||||
$animate.triggerReflow(); //second reflow
|
||||
|
||||
$timeout.flush(7500); //closing timeout for the first animation
|
||||
expect(element.hasClass('some-class-remove-active')).toBe(true);
|
||||
@@ -1237,7 +1294,7 @@ describe("ngAnimate", function() {
|
||||
};
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(elements[0].attr('style')).toBeFalsy();
|
||||
|
||||
@@ -1275,7 +1332,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('abc')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
expect(element.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -1289,7 +1346,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if ($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('xyz')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
expect(element.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -1303,8 +1360,6 @@ describe("ngAnimate", function() {
|
||||
it('should only append active to the newly append CSS className values',
|
||||
inject(function($animate, $rootScope, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
ss.addRule('.ng-enter', '-webkit-transition:9s linear all;' +
|
||||
'transition:9s linear all;');
|
||||
ss.addRule('.ng-enter', '-webkit-transition:9s linear all;' +
|
||||
'transition:9s linear all;');
|
||||
|
||||
@@ -1319,7 +1374,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one')).toBe(true);
|
||||
expect(element.hasClass('two')).toBe(true);
|
||||
expect(element.hasClass('ng-enter')).toBe(true);
|
||||
@@ -1441,6 +1496,68 @@ describe("ngAnimate", function() {
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
|
||||
it('should fire DOM callbacks on the element being animated',
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
ss.addRule('.klass-add', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(element);
|
||||
body.append($rootElement);
|
||||
|
||||
var steps = [];
|
||||
element.on('$animate:before', function(e, data) {
|
||||
steps.push(['before', data.className, data.event]);
|
||||
});
|
||||
|
||||
element.on('$animate:after', function(e, data) {
|
||||
steps.push(['after', data.className, data.event]);
|
||||
});
|
||||
|
||||
$animate.addClass(element, 'klass');
|
||||
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.pop()).toEqual(['before', 'klass', 'addClass']);
|
||||
|
||||
$animate.triggerReflow();
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.pop()).toEqual(['after', 'klass', 'addClass']);
|
||||
}));
|
||||
|
||||
it('should fire the DOM callbacks even if no animation is rendered',
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var parent = jqLite('<div></div>');
|
||||
var element = jqLite('<div></div>');
|
||||
$rootElement.append(parent);
|
||||
body.append($rootElement);
|
||||
|
||||
var steps = [];
|
||||
element.on('$animate:before', function(e, data) {
|
||||
steps.push(['before', data.className, data.event]);
|
||||
});
|
||||
|
||||
element.on('$animate:after', function(e, data) {
|
||||
steps.push(['after', data.className, data.event]);
|
||||
});
|
||||
|
||||
$animate.enter(element, parent);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(steps.shift()).toEqual(['before', 'ng-enter', 'enter']);
|
||||
expect(steps.shift()).toEqual(['after', 'ng-enter', 'enter']);
|
||||
}));
|
||||
|
||||
it("should fire a done callback when provided with no animation",
|
||||
inject(function($animate, $rootScope, $compile, $sniffer, $rootElement, $timeout) {
|
||||
@@ -1527,6 +1644,9 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
$animate.addClass(element, 'ng-hide'); //earlier animation cancelled
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
$timeout.flush();
|
||||
expect(signature).toBe('AB');
|
||||
}));
|
||||
@@ -1657,7 +1777,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-add')).toBe(true);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass')).toBe(true);
|
||||
expect(element.hasClass('klass-add-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 3000, elapsedTime: 3 });
|
||||
@@ -1672,7 +1792,7 @@ describe("ngAnimate", function() {
|
||||
if($sniffer.transitions) {
|
||||
expect(element.hasClass('klass-remove')).toBe(true);
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass')).toBe(false);
|
||||
expect(element.hasClass('klass-add')).toBe(false);
|
||||
expect(element.hasClass('klass-add-active')).toBe(false);
|
||||
@@ -1736,7 +1856,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass-add')).toBe(true);
|
||||
expect(element.hasClass('klass-add-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 11000, elapsedTime: 11 });
|
||||
@@ -1752,7 +1872,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('klass-remove')).toBe(true);
|
||||
expect(element.hasClass('klass-remove-active')).toBe(true);
|
||||
browserTrigger(element,'transitionend', { timeStamp: Date.now() + 11000, elapsedTime: 11 });
|
||||
@@ -1786,7 +1906,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one-add')).toBe(true);
|
||||
expect(element.hasClass('two-add')).toBe(true);
|
||||
|
||||
@@ -1832,7 +1952,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element.hasClass('one-remove')).toBe(true);
|
||||
expect(element.hasClass('two-remove')).toBe(true);
|
||||
|
||||
@@ -1886,7 +2006,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
@@ -1910,7 +2030,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 9000, elapsedTime: 9 });
|
||||
@@ -1920,41 +2040,6 @@ describe("ngAnimate", function() {
|
||||
}));
|
||||
|
||||
|
||||
it("should not set the transition property flag if only CSS animations are used",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
|
||||
if (!$sniffer.animations) return;
|
||||
|
||||
ss.addRule('.sleek-animation.ng-enter', '-webkit-animation: my_animation 2s linear;' +
|
||||
'animation: my_animation 2s linear');
|
||||
|
||||
ss.addRule('.trans.ng-enter', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all');
|
||||
|
||||
var propertyKey = ($sniffer.vendorPrefix == 'Webkit' ? '-webkit-' : '') + 'transition-property';
|
||||
|
||||
var element = html($compile('<div>...</div>')($rootScope));
|
||||
var child = $compile('<div class="skeep-animation">...</div>')($rootScope);
|
||||
child.css(propertyKey,'background-color');
|
||||
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 2000, elapsedTime: 2 });
|
||||
|
||||
expect(child.css(propertyKey)).toBe('background-color');
|
||||
child.remove();
|
||||
|
||||
child = $compile('<div class="sleek-animation">...</div>')($rootScope);
|
||||
child.attr('class','trans');
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(child.css(propertyKey)).not.toBe('background-color');
|
||||
}));
|
||||
|
||||
|
||||
it("should skip animations if the browser does not support CSS3 transitions and CSS3 animations",
|
||||
inject(function($compile, $rootScope, $animate, $sniffer) {
|
||||
|
||||
@@ -1999,9 +2084,8 @@ describe("ngAnimate", function() {
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush(10);
|
||||
|
||||
if($sniffer.transitions) {
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(child,'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 1 });
|
||||
}
|
||||
|
||||
@@ -2035,7 +2119,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
//this is added/removed right away otherwise
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
@@ -2075,7 +2159,7 @@ describe("ngAnimate", function() {
|
||||
$animate.leave(child);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter-active')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -2270,7 +2354,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
var empty = true;
|
||||
try {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
empty = false;
|
||||
}
|
||||
catch(e) {}
|
||||
@@ -2296,7 +2380,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.enter(child, element);
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
@@ -2356,7 +2440,7 @@ describe("ngAnimate", function() {
|
||||
expect(animationState).toBe('enter');
|
||||
if($sniffer.transitions) {
|
||||
expect(child.hasClass('ng-enter')).toBe(true);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(child.hasClass('ng-enter-active')).toBe(true);
|
||||
}
|
||||
|
||||
@@ -2373,7 +2457,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$animate.addClass(child, 'something');
|
||||
if($sniffer.transitions) {
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
}
|
||||
expect(animationState).toBe('addClass');
|
||||
if($sniffer.transitions) {
|
||||
@@ -2392,7 +2476,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
|
||||
it("should wait until a queue of animations are complete before performing a reflow",
|
||||
inject(function($rootScope, $compile, $timeout,$sniffer) {
|
||||
inject(function($rootScope, $compile, $timeout, $sniffer, $animate) {
|
||||
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
@@ -2404,7 +2488,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(element[0].querySelectorAll('.ng-enter-active').length).toBe(0);
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
expect(element[0].querySelectorAll('.ng-enter-active').length).toBe(5);
|
||||
|
||||
forEach(element.children(), function(kid) {
|
||||
@@ -2465,7 +2549,7 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
|
||||
|
||||
it("should disable all child animations on structural animations until the first reflow has passed", function() {
|
||||
it("should disable all child animations on structural animations until the post animation timeout has passed", function() {
|
||||
var intercepted;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.animated', function() {
|
||||
@@ -2579,7 +2663,6 @@ describe("ngAnimate", function() {
|
||||
$animate.enter(kid, element);
|
||||
}
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
//called three times since the classname is the same
|
||||
expect(count).toBe(2);
|
||||
@@ -2593,7 +2676,6 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(count).toBe(20);
|
||||
});
|
||||
@@ -2661,12 +2743,51 @@ describe("ngAnimate", function() {
|
||||
expect(element.hasClass('green')).toBe(false);
|
||||
expect(element.hasClass('red')).toBe(false);
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(element.hasClass('green')).toBe(true);
|
||||
expect(element.hasClass('red')).toBe(true);
|
||||
}));
|
||||
|
||||
it("should avoid mixing up substring classes during add and remove operations", function() {
|
||||
var currentAnimation, currentFn;
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.register('.on', function() {
|
||||
return {
|
||||
beforeAddClass : function(element, className, done) {
|
||||
currentAnimation = 'addClass';
|
||||
currentFn = done;
|
||||
return function(cancelled) {
|
||||
currentAnimation = cancelled ? null : currentAnimation;
|
||||
}
|
||||
},
|
||||
beforeRemoveClass : function(element, className, done) {
|
||||
currentAnimation = 'removeClass';
|
||||
currentFn = done;
|
||||
return function(cancelled) {
|
||||
currentAnimation = cancelled ? null : currentAnimation;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($compile, $rootScope, $animate, $sniffer, $timeout) {
|
||||
var element = $compile('<div class="animation-enabled only"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$animate.addClass(element, 'on');
|
||||
expect(currentAnimation).toBe('addClass');
|
||||
currentFn();
|
||||
|
||||
currentAnimation = null;
|
||||
|
||||
$animate.removeClass(element, 'on');
|
||||
$animate.addClass(element, 'on');
|
||||
|
||||
expect(currentAnimation).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
it('should enable and disable animations properly on the root element', function() {
|
||||
var count = 0;
|
||||
@@ -2772,17 +2893,17 @@ describe("ngAnimate", function() {
|
||||
$animate.removeClass(element, 'base-class one two');
|
||||
|
||||
//still true since we're before the reflow
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
expect(element.hasClass('base-class')).toBe(false);
|
||||
|
||||
//this will cancel the remove animation
|
||||
$animate.addClass(element, 'base-class one two');
|
||||
|
||||
//the cancellation was a success and the class was added right away
|
||||
//since there was no successive animation for the after animation
|
||||
expect(element.hasClass('base-class')).toBe(true);
|
||||
expect(element.hasClass('base-class')).toBe(false);
|
||||
|
||||
//the reflow...
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
//the reflow DOM operation was commenced but it ran before so it
|
||||
//shouldn't run agaun
|
||||
@@ -2816,9 +2937,10 @@ describe("ngAnimate", function() {
|
||||
node._setAttribute(prop, val);
|
||||
};
|
||||
|
||||
expect(capturedProperty).toBe('none');
|
||||
$animate.addClass(element, 'trigger-class');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedProperty).not.toBe('none');
|
||||
}));
|
||||
@@ -2845,7 +2967,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
expect(node.style[animationKey]).toContain('none');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(node.style[animationKey]).not.toContain('none');
|
||||
}));
|
||||
@@ -2890,7 +3012,7 @@ describe("ngAnimate", function() {
|
||||
expect(element[0].style[prop]).toContain('none');
|
||||
expect($window.getComputedStyle(element[0])[prop + 'Duration']).toBe('0s');
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2909,7 +3031,7 @@ describe("ngAnimate", function() {
|
||||
$animate.leave(element);
|
||||
$rootScope.$digest();
|
||||
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 0.50999999991 });
|
||||
|
||||
@@ -2933,7 +3055,7 @@ describe("ngAnimate", function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer) {
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $timeout, $templateCache, $sniffer, $animate) {
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
$templateCache.put('item-template', 'item: #{{ item }} ');
|
||||
@@ -2952,7 +3074,7 @@ describe("ngAnimate", function() {
|
||||
$rootScope.tpl = 'item-template';
|
||||
$rootScope.items = [1,2,3];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedAnimation).toBe('enter');
|
||||
expect(element.text()).toContain('item: #1');
|
||||
@@ -2964,7 +3086,7 @@ describe("ngAnimate", function() {
|
||||
|
||||
$rootScope.items = [];
|
||||
$rootScope.$digest();
|
||||
$timeout.flush();
|
||||
$animate.triggerReflow();
|
||||
|
||||
expect(capturedAnimation).toBe('leave');
|
||||
});
|
||||
@@ -3019,5 +3141,116 @@ describe("ngAnimate", function() {
|
||||
expect(leaveDone).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should respect the most relevant CSS transition property if defined in multiple classes',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.base-class', '-webkit-transition:1s linear all;' +
|
||||
'transition:1s linear all;');
|
||||
|
||||
ss.addRule('.base-class.on', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="base-class"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var ready = false;
|
||||
$animate.addClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(false);
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 5 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
|
||||
ready = false;
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 1 });
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not apply a transition upon removal of a class that has a transition',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.base-class.on', '-webkit-transition:5s linear all;' +
|
||||
'transition:5s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="base-class on"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var ready = false;
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
ready = true;
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(ready).toBe(true);
|
||||
}));
|
||||
|
||||
it('should avoid skip animations if the same CSS class is added / removed synchronously before the reflow kicks in',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
if (!$sniffer.transitions) return;
|
||||
|
||||
ss.addRule('.water-class', '-webkit-transition:2s linear all;' +
|
||||
'transition:2s linear all;');
|
||||
|
||||
$animate.enabled(true);
|
||||
|
||||
var element = $compile('<div class="water-class on"></div>')($rootScope);
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
var signature = '';
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(signature).toBe('AB');
|
||||
|
||||
signature = '';
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'A';
|
||||
});
|
||||
$animate.addClass(element, 'on', function() {
|
||||
signature += 'B';
|
||||
});
|
||||
$animate.removeClass(element, 'on', function() {
|
||||
signature += 'C';
|
||||
});
|
||||
|
||||
$timeout.flush(1);
|
||||
expect(signature).toBe('AB');
|
||||
|
||||
$animate.triggerReflow();
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now(), elapsedTime: 2000 });
|
||||
$timeout.flush(1);
|
||||
|
||||
expect(signature).toBe('ABC');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+25
@@ -862,6 +862,31 @@ describe('ngMock', function() {
|
||||
expect(log).toEqual('module;inject;')
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// We don't run the following tests on IE8.
|
||||
// IE8 throws "Object does not support this property or method." error,
|
||||
// when thrown from a function defined on window (which `inject` is).
|
||||
|
||||
it('should not change thrown Errors', inject(function($sniffer) {
|
||||
if ($sniffer.msie <= 8) return;
|
||||
|
||||
expect(function() {
|
||||
inject(function() {
|
||||
throw new Error('test message');
|
||||
});
|
||||
}).toThrow('test message');
|
||||
}));
|
||||
|
||||
it('should not change thrown strings', inject(function($sniffer) {
|
||||
if ($sniffer.msie <= 8) return;
|
||||
|
||||
expect(function() {
|
||||
inject(function() {
|
||||
throw 'test message';
|
||||
});
|
||||
}).toThrow('test message');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user