Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cfbe1a701a | |||
| aa38ded59d | |||
| b03957f004 | |||
| 56f33af890 | |||
| 41bea59eb7 | |||
| 6d0dcca6f1 | |||
| f423dc6b51 | |||
| 9fca87ca5b | |||
| ebbaa4ac5e | |||
| 617137231e | |||
| 71b966ace7 | |||
| 488d5b4288 | |||
| 01b8772cca | |||
| 05ac0ba926 | |||
| 399e872228 | |||
| 3115408be7 | |||
| aea39e9fdc | |||
| 51f6752cec | |||
| db58801a55 | |||
| 2e33d170be | |||
| cc6dcb4bc2 | |||
| 294d6793fd | |||
| cefcc46e1b | |||
| c10dba144e | |||
| f05ffd9bf1 | |||
| e208d81293 | |||
| b51fca1a2b | |||
| 6685043ad4 | |||
| f176a2dbb7 | |||
| 3a366bd42b | |||
| 1c5db02638 | |||
| ffd5308d86 | |||
| aed9de9d37 | |||
| aab6825e85 | |||
| c4fad6ad61 | |||
| 2a7c37cf01 | |||
| 194c3adfed | |||
| 371957d9d5 | |||
| 1e5d36d4b0 | |||
| 3749c85829 | |||
| 886b59d317 | |||
| 2048b6df24 | |||
| 5ec0a50d39 |
+118
@@ -1,3 +1,83 @@
|
||||
<a name="1.5.6"></a>
|
||||
# 1.5.6 arrow-stringification (2016-05-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$browser:** set the url even if the browser transforms it
|
||||
([743b33ab](https://github.com/angular/angular.js/commit/743b33ab9d8cdb8aa6ff28028a54ac0023f4dc15),
|
||||
[#14427](https://github.com/angular/angular.js/issues/14427), [#14499](https://github.com/angular/angular.js/issues/14499))
|
||||
- **$compile:**
|
||||
- properly bind context to linking functions for directives with `templateUrl`
|
||||
([5ae72dc7](https://github.com/angular/angular.js/commit/5ae72dc7affb252774b7c4cbf278269c5e178f38))
|
||||
- always use the DDO as `this` in pre-/post-linking functions
|
||||
([47bc98a1](https://github.com/angular/angular.js/commit/47bc98a1eafd7853babc1a7dc54f13363a3f4a43),
|
||||
[#9306](https://github.com/angular/angular.js/issues/9306))
|
||||
- don't run unnecessary update to one-way bindings
|
||||
([30479647](https://github.com/angular/angular.js/commit/304796471292f9805b9cf77e51aacc9cfbb09921),
|
||||
[#14546](https://github.com/angular/angular.js/issues/14546), [#14580](https://github.com/angular/angular.js/issues/14580))
|
||||
- removing unnecessary white space in element-transclusion comments
|
||||
([1116a96d](https://github.com/angular/angular.js/commit/1116a96df662c95aa83ab527c479b644a14532b6),
|
||||
[#14549](https://github.com/angular/angular.js/issues/14549), [#14550](https://github.com/angular/angular.js/issues/14550))
|
||||
- properly handle setting `srcset` to undefined
|
||||
([308f22ba](https://github.com/angular/angular.js/commit/308f22ba9a803967ce84c995d810990c80478f6f),
|
||||
[#14470](https://github.com/angular/angular.js/issues/14470), [#14493](https://github.com/angular/angular.js/issues/14493))
|
||||
- **$injector:**
|
||||
- add workaround for class stringification in Chrome v50/51
|
||||
([7550b7bd](https://github.com/angular/angular.js/commit/7550b7bda8d992cc6ed3c65b1f9f7e323a09cae4),
|
||||
[#14531](https://github.com/angular/angular.js/issues/14531))
|
||||
- add workaround for fat-arrow stringification in Chrome v50/51
|
||||
([bb730704](https://github.com/angular/angular.js/commit/bb730704f2f3dea4620e7b40083dcd65f208e2b2),
|
||||
[#14487](https://github.com/angular/angular.js/issues/14487), [#14495](https://github.com/angular/angular.js/issues/14495))
|
||||
- **$templateRequest:** trust empty templates in `$templateCache` as well
|
||||
([10955969](https://github.com/angular/angular.js/commit/10955969620248bd31a0bd37bc9fd7cc3b1f5e88),
|
||||
[#14479](https://github.com/angular/angular.js/issues/14479), [#14496](https://github.com/angular/angular.js/issues/14496))
|
||||
- **filters:** always call `splice()` with 2 arguments or more
|
||||
([6f1fae8a](https://github.com/angular/angular.js/commit/6f1fae8a916cc6f26725f64869f86fcd4991e819),
|
||||
[#14467](https://github.com/angular/angular.js/issues/14467), [#14489](https://github.com/angular/angular.js/issues/14489))
|
||||
- **ng-bind-html:** watch the unwrapped value using `$sce.valueOf()` (instead of `toString()`)
|
||||
([1c1c9b27](https://github.com/angular/angular.js/commit/1c1c9b27cbb57b3219d4c9765eeea8a11553d297),
|
||||
[#14526](https://github.com/angular/angular.js/issues/14526), [#14527](https://github.com/angular/angular.js/issues/14527))
|
||||
- **ngAnimate:**
|
||||
- don't break on anchored animations without duration
|
||||
([21b76aed](https://github.com/angular/angular.js/commit/21b76aed06d9cc04bd25a99c23ba852af782b357),
|
||||
[#14641](https://github.com/angular/angular.js/issues/14641), [#14645](https://github.com/angular/angular.js/issues/14645))
|
||||
- properly handle empty jqLite collections
|
||||
([9f480257](https://github.com/angular/angular.js/commit/9f4802573348401ee24090f815f3138fa17c161d),
|
||||
[#14558](https://github.com/angular/angular.js/issues/14558), [#14559](https://github.com/angular/angular.js/issues/14559))
|
||||
- **ngMessages:** do not compile ngMessagesInclude template if scope is destroyed
|
||||
([69d9072d](https://github.com/angular/angular.js/commit/69d9072d5b5aba988ac8a9717c92ce1fac465cbe),
|
||||
[#12695](https://github.com/angular/angular.js/issues/12695), [#14640](https://github.com/angular/angular.js/issues/14640))
|
||||
- **ngMock:**
|
||||
- match HTTP request regardless of the order of query params
|
||||
([363fb16c](https://github.com/angular/angular.js/commit/363fb16c1076a806bb31006daaf583ab5c82e467),
|
||||
[#12762](https://github.com/angular/angular.js/issues/12762))
|
||||
- pass eventHandlers to $httpBackend if passThrough is active
|
||||
([147650b3](https://github.com/angular/angular.js/commit/147650b3beae7ad8b556a930129887d187a0f5d3),
|
||||
[#14471](https://github.com/angular/angular.js/issues/14471))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** support omitting required controller name if same as the local name
|
||||
([8965d57a](https://github.com/angular/angular.js/commit/8965d57aa0788c96859cf3cfa0f78b46d2cebedb),
|
||||
[#14513](https://github.com/angular/angular.js/issues/14513))
|
||||
- **$parse:** Add support for ES6 object initializers
|
||||
([1c9b9e24](https://github.com/angular/angular.js/commit/1c9b9e24ede7efc10ce4d53c6ab5528cc77e79d7))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$rootScope:** make queues more efficient
|
||||
([51b78f61](https://github.com/angular/angular.js/commit/51b78f61f954d94dc937f57190c9f881df9ab3ad),
|
||||
[#14545](https://github.com/angular/angular.js/issues/14545))
|
||||
- **ngAnimate:** listen for document visibility changes
|
||||
([b377d6b0](https://github.com/angular/angular.js/commit/b377d6b043db9c8da4e68493892f75a0f9759464),
|
||||
[#14568](https://github.com/angular/angular.js/issues/14568))
|
||||
- **ngClass:** improve even-odd checking
|
||||
([cf1b1755](https://github.com/angular/angular.js/commit/cf1b175508a72d4b7e3d64b8506903096c6ec25f))
|
||||
|
||||
|
||||
<a name="1.5.5"></a>
|
||||
# 1.5.5 material-conspiration (2016-04-18)
|
||||
|
||||
@@ -1913,6 +1993,44 @@ describe('$q.when', function() {
|
||||
[#11580](https://github.com/angular/angular.js/issues/11580), [#12234](https://github.com/angular/angular.js/issues/12234))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **ngInclude:** due to [3c6e8ce044446735eb2e70d0061db8c6db050289](https://github.com/angular/angular.js/commit/3c6e8ce044446735eb2e70d0061db8c6db050289), the `src` attribute of ngInclude no longer accepts an
|
||||
expression that returns the result of `$sce.trustAsResourceUrl`. This will now cause an infinite digest:
|
||||
|
||||
Before:
|
||||
```html
|
||||
<div ng-include="findTemplate('https://example.com/myTemplate.html')"></div>
|
||||
```
|
||||
|
||||
```js
|
||||
$scope.findTemplate = function(templateName) {
|
||||
return $sce.trustAsResourceUrl(templateName);
|
||||
};
|
||||
```
|
||||
|
||||
To migrate, either cache the result of `trustAsResourceUrl()`, or put the template url in the resource
|
||||
whitelist in the `config()` function:
|
||||
|
||||
After:
|
||||
|
||||
```js
|
||||
var templateCache = {};
|
||||
$scope.findTemplate = function(templateName) {
|
||||
if (!templateCache[templateName]) {
|
||||
templateCache[templateName] = $sce.trustAsResourceUrl(templateName);
|
||||
}
|
||||
|
||||
return templateCache[templateName];
|
||||
};
|
||||
|
||||
// Alternatively, use `$sceDelegateProvider.resourceUrlWhitelist()`:
|
||||
|
||||
angular.module('myApp', []).config(function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhitelist(['self', 'https://example.com/**'])
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
<a name="1.3.17"></a>
|
||||
# 1.3.17 tsktskskly-euouae (2015-07-06)
|
||||
|
||||
Vendored
+5
@@ -5,6 +5,7 @@ var angularFiles = {
|
||||
'src/minErr.js',
|
||||
'src/Angular.js',
|
||||
'src/loader.js',
|
||||
'src/shallowCopy.js',
|
||||
'src/stringify.js',
|
||||
'src/AngularPublic.js',
|
||||
'src/jqLite.js',
|
||||
@@ -128,6 +129,7 @@ var angularFiles = {
|
||||
'src/ngResource/resource.js'
|
||||
],
|
||||
'ngRoute': [
|
||||
'src/shallowCopy.js',
|
||||
'src/ngRoute/route.js',
|
||||
'src/ngRoute/routeParams.js',
|
||||
'src/ngRoute/directive/ngView.js'
|
||||
@@ -175,6 +177,7 @@ var angularFiles = {
|
||||
'test/auto/*.js',
|
||||
'test/ng/**/*.js',
|
||||
'test/ngAnimate/*.js',
|
||||
'test/ngMessageFormat/*.js',
|
||||
'test/ngMessages/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
@@ -212,7 +215,9 @@ var angularFiles = {
|
||||
'test/modules/no_bootstrap.js',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'test/helpers/*.js',
|
||||
'test/ngAnimate/*.js',
|
||||
'test/ngMessageFormat/*.js',
|
||||
'test/ngMessages/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngRoute/**/*.js',
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
|
||||
# Component Router
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Deprecation Notice:** In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
</div>
|
||||
|
||||
This guide describes the new Component Router for AngularJS 1.5.
|
||||
|
||||
<div class="alert alert-info">
|
||||
@@ -482,6 +487,14 @@ Just like any Angular application, we load the JavaScript files into our `index.
|
||||
<script src="/app/app.js"></script>
|
||||
```
|
||||
|
||||
You also need to include ES6 shims for browsers that do not support ES6 code (Internet Explorer,
|
||||
iOs < 8, Android < 5.0, Windows Mobile < 10):
|
||||
```html
|
||||
<!-- IE required polyfills, in this exact order -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
|
||||
<script src="https://npmcdn.com/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
|
||||
```
|
||||
|
||||
## Create the `app` module
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ angular.module('myApp').directive('form', function() {
|
||||
});
|
||||
```
|
||||
|
||||
### Templating (`ngRepeat`, `$compile`)
|
||||
### Templating (`ngRepeat`, `$compile`, `ngInclude`)
|
||||
|
||||
#### ngRepeat
|
||||
|
||||
@@ -545,6 +545,44 @@ Due to [62d514b](https://github.com/angular/angular.js/commit/62d514b06937cc7dd8
|
||||
returning an object from a controller constructor function will now override the scope. Views that use the
|
||||
controllerAs method will no longer get the this reference, but the returned object.
|
||||
|
||||
#### ngInclude
|
||||
Due to [3c6e8ce044446735eb2e70d0061db8c6db050289](https://github.com/angular/angular.js/commit/3c6e8ce044446735eb2e70d0061db8c6db050289), the `src` attribute of ngInclude no longer accepts an
|
||||
expression that returns the result of `$sce.trustAsResourceUrl`. This will now cause an infinite digest:
|
||||
|
||||
Before:
|
||||
```html
|
||||
<div ng-include="findTemplate('https://example.com/templates/myTemplate.html')"></div>
|
||||
```
|
||||
|
||||
```js
|
||||
$scope.findTemplate = function(templateName) {
|
||||
return $sce.trustAsResourceUrl(templateName);
|
||||
};
|
||||
```
|
||||
|
||||
To migrate, either cache the result of `trustAsResourceUrl()`, or put the template url in the resource
|
||||
whitelist in the `config()` function:
|
||||
|
||||
After:
|
||||
|
||||
```js
|
||||
var templateCache = {};
|
||||
$scope.findTemplate = function(templateName) {
|
||||
if (!templateCache[templateName]) {
|
||||
templateCache[templateName] = $sce.trustAsResourceUrl(templateName);
|
||||
}
|
||||
|
||||
return templateCache[templateName];
|
||||
};
|
||||
|
||||
// Alternatively, use `$sceDelegateProvider.resourceUrlWhitelist()`, which means you don't
|
||||
// have to use `$sce.trustAsResourceUrl()` at all:
|
||||
|
||||
angular.module('myApp', []).config(function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhitelist(['self', 'https://example.com/templates/**'])
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Cookies (`ngCookies`)
|
||||
|
||||
@@ -583,8 +621,6 @@ has been moved to `$cookies`, to which `$cookieStore` now simply
|
||||
delegates calls.
|
||||
|
||||
|
||||
|
||||
|
||||
### Server Requests (`$http`)
|
||||
|
||||
Due to [5da1256](https://github.com/angular/angular.js/commit/5da1256fc2812d5b28fb0af0de81256054856369),
|
||||
@@ -616,8 +652,6 @@ $http.get(url, {
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### Filters (`filter`, `limitTo`)
|
||||
|
||||
#### `filter` filter
|
||||
@@ -636,7 +670,6 @@ Now, instead of returning empty object/array, it returns unchanged input.
|
||||
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.2 to 1.3
|
||||
|
||||
### Controllers
|
||||
|
||||
@@ -87,6 +87,10 @@ Protection from JSON Hijacking is provided if the server prefixes all JSON reque
|
||||
Angular will automatically strip the prefix before processing it as JSON.
|
||||
For more information please visit {@link $http#json-vulnerability-protection JSON Hijacking Protection}.
|
||||
|
||||
Bear in mind that calling `$http.jsonp`, like in [our Yahoo! finance example](https://docs.angularjs.org/guide/concepts#accessing-the-backend),
|
||||
gives the remote server (and, if the request is not secured, any Man-in-the-Middle attackers)
|
||||
instant remote code execution in your application: the result of these requests is handed off
|
||||
to the browser as regular `<script>` tag.
|
||||
|
||||
## Strict Contextual Escaping
|
||||
|
||||
|
||||
@@ -2,85 +2,121 @@
|
||||
@name Downloading
|
||||
@description
|
||||
|
||||
# Including angular scripts from the Google CDN
|
||||
The quickest way to get started is to point your html `<script>` tag to a Google CDN URL.
|
||||
# Including Angular scripts from the Google CDN
|
||||
The quickest way to get started is to point your html `<script>` tag to a
|
||||
[Google CDN](https://developers.google.com/speed/libraries/#angularjs) URL.
|
||||
This way, you don't have to download anything or maintain a local copy.
|
||||
|
||||
There are two types of angular script URLs you can point to, one for development and one for
|
||||
There are two types of Angular script URLs you can point to, one for development and one for
|
||||
production:
|
||||
|
||||
* __angular.js__ — This is the human-readable, non-minified version, suitable for web
|
||||
development.
|
||||
* __angular.js__ — This is the human-readable, non-minified version, suitable for web development.
|
||||
* __angular.min.js__ — This is the minified version, which we strongly suggest you use in
|
||||
production.
|
||||
production.
|
||||
|
||||
To point your code to an angular script on the Google CDN server, use the following template. This
|
||||
example points to the minified version 1.4.5:
|
||||
To point your code to an angular script on the Google CDN server, use the following template. This
|
||||
example points to the minified version 1.5.6:
|
||||
|
||||
```
|
||||
```html
|
||||
<!doctype html>
|
||||
<html ng-app>
|
||||
<head>
|
||||
<title>My Angular App</title>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Note that only versions 1.0.1 and above are available on the CDN, if you need an earlier version
|
||||
you can use the <http://code.angularjs.org/> URL which was the previous recommended location for
|
||||
hosted code source. If you're still using the angular server you should switch to the CDN version
|
||||
for even faster loading times.
|
||||
<div class="alert alert-info">
|
||||
Note that only versions 1.0.1 and above are available on the CDN. If you need an earlier version
|
||||
(which you shouldn't) you can use the https://code.angularjs.org/ URL, which was the previous
|
||||
recommended location for hosted code source. If you're still using the Angular server you should
|
||||
switch to the CDN version for even faster loading times.
|
||||
</div>
|
||||
|
||||
<br />
|
||||
# Downloading and hosting angular files locally
|
||||
This option is for those who want to work with angular offline, or those who want to host the
|
||||
angular files on their own servers.
|
||||
This option is for those who want to work with Angular offline, or those who want to host the
|
||||
Angular files on their own servers.
|
||||
|
||||
If you navigate to <http://code.angularjs.org/>, you'll see a directory listing with all of the
|
||||
angular versions since we started releasing versioned build artifacts (quite late in the project
|
||||
lifetime). Each directory contains all artifacts that we released for a particular version.
|
||||
Download the version you want and have fun.
|
||||
If you navigate to https://code.angularjs.org/, you'll see a directory listing with all of the
|
||||
Angular versions since we started releasing versioned build artifacts. Each directory contains all
|
||||
artifacts that we released for a particular version. Download the version you want and have fun.
|
||||
|
||||
Each directory under <http://code.angularjs.org/> includes the following set of files:
|
||||
<div class="alert alert-warning">
|
||||
You can ignore directories starting with `2.` (e.g. `2.0.0-beta.17`) — they are not related to
|
||||
AngularJS. They contain build artifacts from [Angular 2](https://angular.io) versions.
|
||||
</div>
|
||||
|
||||
* __`angular.js`__ — This file is non-obfuscated, non-minified, and human-readable by
|
||||
opening it in any editor or browser. In order to get better error messages during development, you
|
||||
should always use this non-minified angular script.
|
||||
<br />
|
||||
Each directory under https://code.angularjs.org/ includes a set of files that comprise the
|
||||
corresponding version. All JavaScript files (except for `angular-mocks` which is only used during
|
||||
development) come in two flavors — one suitable for development, the other for production:
|
||||
|
||||
* __`angular.min.js`__ — This is a minified and obfuscated version of
|
||||
`angular.js` created with the Closure compiler. Use this version for production in order
|
||||
to minimize the size of the application that is downloaded by your user's browser.
|
||||
* __`<filename>.js`__ — These files are non-obfuscated, non-minified, and human-readable by opening
|
||||
them in any editor or browser. In order to get better error messages during development, you
|
||||
should always use these non-minified scripts.
|
||||
|
||||
* __`angular.zip`__ — This is a zip archive that contains all of the files released
|
||||
for this angular version. Use this file to get everything in a single download.
|
||||
* __`<filename>.min.js`__ — These are minified and obfuscated versions, created with the
|
||||
[Closure compiler](https://developers.google.com/closure/compiler/). Use these versions for
|
||||
production in order to minimize the size of the application that is downloaded by your user's
|
||||
browser.
|
||||
|
||||
* __`angular-mocks.js`__ — This file contains an implementation of mocks that makes
|
||||
testing angular apps even easier. Your unit/integration test harness should load this file after
|
||||
`angular.js` is loaded.
|
||||
* __`<filename>.min.js.map`__ — These are
|
||||
[sourcemap files](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/). You can
|
||||
serve them alongside the `.min.js` files in order to be able to debug the minified code (e.g. on a
|
||||
production deployment) more easily, but without impacting performance.
|
||||
|
||||
* __`angular-scenario.js`__ — This file is a very nifty JavaScript file that allows you
|
||||
to write and execute end-to-end tests for angular applications.
|
||||
<br />
|
||||
The set of files included in each version directory are:
|
||||
|
||||
* __`angular-loader.min.js`__ — Module loader for Angular modules. If you are loading multiple script files containing
|
||||
Angular modules, you can load them asynchronously and in any order as long as you load this file first. Often the
|
||||
contents of this file are copy&pasted into the `index.html` to avoid even the initial request to `angular-loader.min.js`.
|
||||
See [angular-seed](https://github.com/angular/angular-seed/blob/master/app/index-async.html) for an example of usage.
|
||||
* __`angular.zip`__ — This is a zip archive that contains all of the files released for this Angular
|
||||
version. Use this file to get everything in a single download.
|
||||
|
||||
* __Additional Angular modules:__ optional modules with additional functionality. These files should be loaded
|
||||
after the core `angular.js` file:
|
||||
* __`angular-animate.js`__ - Enable animation support
|
||||
* __`angular-cookies.js`__ - A convenient wrapper for reading and writing browser cookies
|
||||
* __`angular-resource.js`__ - Interaction support with RESTful services via the $resource service
|
||||
* __`angular-route.js`__ - Routing and deeplinking services and directives for angular apps
|
||||
* __`angular-sanitize.js`__ - Functionality to sanitize HTML
|
||||
* __`angular-touch.js`__ - Touch events and other helpers for touch-enabled devices
|
||||
* __`angular.js`__ — The core Angular framework. This is all you need to get your Angular app
|
||||
running.
|
||||
|
||||
* __`angular-csp.css`__ — You only need this file if you are using
|
||||
[CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP). See
|
||||
{@link directive:ngCsp here} for more info.
|
||||
|
||||
* __`angular-mocks.js`__ — This file contains an implementation of mocks that makes testing angular
|
||||
apps even easier. Your unit/integration test harness should load this file after `angular.js` is
|
||||
loaded.
|
||||
|
||||
* __`angular-loader.js`__ — Module loader for Angular modules. If you are loading multiple
|
||||
script files containing Angular modules, you can load them asynchronously and in any order as long
|
||||
as you load this file first. Often the contents of this file are copy&pasted into the `index.html`
|
||||
to avoid even the initial request to `angular-loader[.min].js`.
|
||||
See [angular-seed](https://github.com/angular/angular-seed/blob/master/app/index-async.html) for
|
||||
an example of usage.
|
||||
|
||||
* __Additional Angular modules:__ Optional modules with additional functionality. These files
|
||||
should be loaded after the core `angular[.min].js` file:
|
||||
* __`angular-animate.js`__ — Enable animation support. ({@link module:ngAnimate API docs})
|
||||
* __`angular-aria.js`__ — Make your apps [accessible](http://www.w3.org/TR/wai-aria/) to users of
|
||||
assistive technologies. ({@link module:ngAria API docs})
|
||||
* __`angular-cookies.js`__ — A convenient wrapper for reading and writing browser cookies.
|
||||
({@link module:ngCookies API docs})
|
||||
<!-- * __`angular-message-format.js`__ — ({@link module:ngMessageFormat API docs}) -->
|
||||
* __`angular-messages.js`__ — Enhanced support for displaying validation messages.
|
||||
({@link module:ngMessages API docs})
|
||||
* __`angular-parse-ext.js`__ — Allow Unicode characters in identifiers inside Angular expressions.
|
||||
({@link module:ngParseExt API docs})
|
||||
* __`angular-resource.js`__ — Easy interaction with RESTful services.
|
||||
({@link module:ngResource API docs})
|
||||
* __`angular-route.js`__ — Routing and deep-linking services and directives for Angular apps.
|
||||
({@link module:ngRoute API docs})
|
||||
* __`angular-sanitize.js`__ — Functionality to sanitize HTML. ({@link module:ngSanitize API docs})
|
||||
* __`angular-touch.js`__ — Touch events and other helpers for touch-enabled devices.
|
||||
({@link module:ngTouch API docs})
|
||||
|
||||
|
||||
* __`docs`__ — this directory contains all the files that compose the
|
||||
<http://docs.angularjs.org/> documentation app. These files are handy to see the older version of
|
||||
our docs, or even more importantly, view the docs offline.
|
||||
* __`docs/`__ — This directory contains all the files that compose the https://docs.angularjs.org/
|
||||
documentation app. These files are handy to see the older versions of our docs, or even more
|
||||
importantly, view the docs offline.
|
||||
|
||||
* __`i18n`__ - this directory contains locale specific `ngLocale` angular modules to override the defaults
|
||||
defined in the `ng` module.
|
||||
* __`i18n/`__ - This directory contains [locale specific](https://docs.angularjs.org/guide/i18n)
|
||||
`ngLocale` Angular modules to override the defaults defined in the main `ng` module.
|
||||
|
||||
@@ -171,7 +171,7 @@ will make all entities registered on `phoneList` available on `phonecatApp` as w
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>
|
||||
Don't forget to also update your `index.html` adding a `<script>` tag for each JacaScript file
|
||||
Don't forget to also update your `index.html` adding a `<script>` tag for each JavaScript file
|
||||
we have created. This might seem tedious, but is totally worth it.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -87,7 +87,7 @@ feature, we can implement [deep linking][deep-linking], which lets us utilize th
|
||||
|
||||
<div class="alert alert-success">
|
||||
<p>
|
||||
`ngRoute` allows as to associate a controller and a template with a specific URL (or URL
|
||||
`ngRoute` lets us associate a controller and a template with a specific URL (or URL
|
||||
pattern). This is pretty close to what we did with `ngController` and `index.html` back in
|
||||
{@link step_02 step 2}.
|
||||
</p>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var isOnlineAsync = require('is-online');
|
||||
|
||||
isOnlineAsync(function(err, isOnline) {
|
||||
console.log(isOnline ? 'online' : 'offline');
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
var childProcess = require('child_process');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
@@ -7,7 +8,9 @@ var semver = require('semver');
|
||||
var _ = require('lodash');
|
||||
|
||||
var currentPackage, previousVersions, cdnVersion, gitRepoInfo;
|
||||
|
||||
var connectivityStatus = childProcess.execSync('node is-online.js', { cwd: __dirname, encoding: 'utf8' }).trim();
|
||||
var isOnline = connectivityStatus == 'online';
|
||||
console.log('Running ' + connectivityStatus);
|
||||
|
||||
/**
|
||||
* Load information about this project from the package.json
|
||||
@@ -100,58 +103,68 @@ var getPreviousVersions = function() {
|
||||
// not contain all commits when cloned with git clone --depth=...
|
||||
// Needed e.g. for Travis
|
||||
var repo_url = currentPackage.repository.url;
|
||||
var tagResults = shell.exec('git ls-remote --tags ' + repo_url,
|
||||
{silent: true});
|
||||
if (tagResults.code === 0) {
|
||||
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
|
||||
.map(function(tag) {
|
||||
var version = semver.parse(tag);
|
||||
return version;
|
||||
})
|
||||
.filter()
|
||||
.map(function(version) {
|
||||
// angular.js didn't follow semantic version until 1.20rc1
|
||||
if ((version.major === 1 && version.minor === 0 && version.prerelease.length > 0) || (version.major === 1 && version.minor === 2 && version.prerelease[0] === 'rc1')) {
|
||||
version.version = [version.major, version.minor, version.patch].join('.') + version.prerelease.join('');
|
||||
version.raw = 'v' + version.version;
|
||||
}
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
if (version.major < 1 || (version.major === 1 && version.minor === 0 && version.patch < 2)) {
|
||||
version.docsUrl += '-' + version.version;
|
||||
version.isOldDocsUrl = true;
|
||||
}
|
||||
return version;
|
||||
})
|
||||
.sort(semver.compare)
|
||||
.value();
|
||||
if (isOnline) {
|
||||
console.log('looking up remote git tags...');
|
||||
var tagResults = shell.exec('git ls-remote --tags ' + repo_url,
|
||||
{silent: true});
|
||||
if (tagResults.code === 0) {
|
||||
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
|
||||
.map(function(tag) {
|
||||
var version = semver.parse(tag);
|
||||
return version;
|
||||
})
|
||||
.filter()
|
||||
.map(function(version) {
|
||||
// angular.js didn't follow semantic version until 1.20rc1
|
||||
if ((version.major === 1 && version.minor === 0 && version.prerelease.length > 0) || (version.major === 1 && version.minor === 2 && version.prerelease[0] === 'rc1')) {
|
||||
version.version = [version.major, version.minor, version.patch].join('.') + version.prerelease.join('');
|
||||
version.raw = 'v' + version.version;
|
||||
}
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
if (version.major < 1 || (version.major === 1 && version.minor === 0 && version.patch < 2)) {
|
||||
version.docsUrl += '-' + version.version;
|
||||
version.isOldDocsUrl = true;
|
||||
}
|
||||
return version;
|
||||
})
|
||||
.sort(semver.compare)
|
||||
.value();
|
||||
}
|
||||
} else {
|
||||
return [];
|
||||
console.log('-- skipping remote git tag lookup');
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
var getCdnVersion = function() {
|
||||
return _(previousVersions)
|
||||
.filter(function(tag) {
|
||||
return semver.satisfies(tag, currentPackage.branchVersion);
|
||||
})
|
||||
.reverse()
|
||||
.reduce(function(cdnVersion, version) {
|
||||
if (!cdnVersion) {
|
||||
// Note: need to use shell.exec and curl here
|
||||
// as version-infos returns its result synchronously...
|
||||
var cdnResult = shell.exec('curl http://ajax.googleapis.com/ajax/libs/angularjs/' + version + '/angular.min.js ' +
|
||||
'--head --write-out "%{http_code}" -o /dev/null -silent',
|
||||
{silent: true});
|
||||
if (cdnResult.code === 0) {
|
||||
var statusCode = cdnResult.output.trim();
|
||||
if (statusCode === '200') {
|
||||
cdnVersion = version;
|
||||
if (isOnline) {
|
||||
console.log('searching for CDN versions...');
|
||||
return _(previousVersions)
|
||||
.filter(function(tag) {
|
||||
return semver.satisfies(tag, currentPackage.branchVersion);
|
||||
})
|
||||
.reverse()
|
||||
.reduce(function(cdnVersion, version) {
|
||||
if (!cdnVersion) {
|
||||
// Note: need to use shell.exec and curl here
|
||||
// as version-infos returns its result synchronously...
|
||||
var cdnResult = shell.exec('curl http://ajax.googleapis.com/ajax/libs/angularjs/' + version + '/angular.min.js ' +
|
||||
'--head --write-out "%{http_code}" -o /dev/null -silent',
|
||||
{silent: true});
|
||||
if (cdnResult.code === 0) {
|
||||
var statusCode = cdnResult.output.trim();
|
||||
if (statusCode === '200') {
|
||||
console.log('-- found CDN version ' + version);
|
||||
cdnVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cdnVersion;
|
||||
}, null);
|
||||
return cdnVersion;
|
||||
}, null);
|
||||
} else {
|
||||
console.log('-- skipping CDN version lookup');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -198,7 +211,6 @@ var getSnapshotVersion = function() {
|
||||
return version;
|
||||
};
|
||||
|
||||
|
||||
exports.currentPackage = currentPackage = getPackage();
|
||||
exports.gitRepoInfo = gitRepoInfo = getGitRepoInfo();
|
||||
exports.previousVersions = previousVersions = getPreviousVersions();
|
||||
|
||||
+105
-65
@@ -3943,10 +3943,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"grunt-jasmine-node": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "git://github.com/vojtajina/grunt-jasmine-node.git#ced17cbe52c1412b2ada53160432a5b681f37cd7"
|
||||
},
|
||||
"grunt-jscs": {
|
||||
"version": "2.8.0",
|
||||
"dependencies": {
|
||||
@@ -6396,6 +6392,50 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-online": {
|
||||
"version": "5.1.1",
|
||||
"dependencies": {
|
||||
"is-reachable": {
|
||||
"version": "1.3.0",
|
||||
"dependencies": {
|
||||
"arrify": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"each-async": {
|
||||
"version": "1.1.1",
|
||||
"dependencies": {
|
||||
"set-immediate-shim": {
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-port-reachable": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"router-ips": {
|
||||
"version": "0.2.0"
|
||||
},
|
||||
"url-parse-lax": {
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"prepend-http": {
|
||||
"version": "1.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"onetime": {
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"random-item": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"root-hints": {
|
||||
"version": "1.0.10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"jasmine-core": {
|
||||
"version": "2.4.1"
|
||||
},
|
||||
@@ -6946,15 +6986,15 @@
|
||||
"ansi": {
|
||||
"version": "0.3.1"
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.2"
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3"
|
||||
},
|
||||
@@ -6964,12 +7004,12 @@
|
||||
"async": {
|
||||
"version": "1.5.2"
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.6.0"
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.0.3"
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.6.0"
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
@@ -6979,35 +7019,38 @@
|
||||
"caseless": {
|
||||
"version": "0.11.0"
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.9.0"
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3"
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5"
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.2.0"
|
||||
"commander": {
|
||||
"version": "2.9.0"
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.1"
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0"
|
||||
"cryptiles": {
|
||||
"version": "2.0.5"
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1"
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1"
|
||||
"debug": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5"
|
||||
@@ -7015,20 +7058,17 @@
|
||||
"extsprintf": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1"
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.8"
|
||||
"gauge": {
|
||||
"version": "1.2.7"
|
||||
},
|
||||
"form-data": {
|
||||
"version": "1.0.0-rc4"
|
||||
},
|
||||
"gauge": {
|
||||
"version": "1.2.7"
|
||||
"fstream": {
|
||||
"version": "1.0.8"
|
||||
},
|
||||
"generate-object-property": {
|
||||
"version": "1.2.0"
|
||||
@@ -7048,21 +7088,21 @@
|
||||
"has-ansi": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"hawk": {
|
||||
"version": "3.1.3"
|
||||
},
|
||||
"hoek": {
|
||||
"version": "2.16.3"
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1"
|
||||
"has-unicode": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.4"
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
@@ -7072,24 +7112,24 @@
|
||||
"is-property": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"is-typedarray": {
|
||||
"isarray": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"isarray": {
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2"
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"jodid25519": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.2"
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1"
|
||||
},
|
||||
@@ -7102,10 +7142,10 @@
|
||||
"lodash.pad": {
|
||||
"version": "4.1.0"
|
||||
},
|
||||
"lodash.padstart": {
|
||||
"lodash.padend": {
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"lodash.padend": {
|
||||
"lodash.padstart": {
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"lodash.repeat": {
|
||||
@@ -7114,11 +7154,14 @@
|
||||
"lodash.tostring": {
|
||||
"version": "4.1.2"
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.10"
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.22.0"
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.10"
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1"
|
||||
@@ -7126,15 +7169,12 @@
|
||||
"ms": {
|
||||
"version": "0.7.1"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
"node-uuid": {
|
||||
"version": "1.4.7"
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "2.0.3"
|
||||
},
|
||||
"node-uuid": {
|
||||
"version": "1.4.7"
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.1"
|
||||
},
|
||||
@@ -7147,12 +7187,12 @@
|
||||
"pinkie-promise": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.6"
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.0.2"
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.6"
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.0.6"
|
||||
},
|
||||
@@ -7168,11 +7208,14 @@
|
||||
"sshpk": {
|
||||
"version": "1.7.4"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31"
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31"
|
||||
"strip-json-comments": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1"
|
||||
@@ -7180,12 +7223,6 @@
|
||||
"supports-color": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.2.2"
|
||||
},
|
||||
@@ -7195,18 +7232,21 @@
|
||||
"tunnel-agent": {
|
||||
"version": "0.4.2"
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.3"
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.6"
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.3.6"
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.6"
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
|
||||
Generated
+2495
-2432
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -41,7 +41,6 @@
|
||||
"grunt-contrib-copy": "~0.6.0",
|
||||
"grunt-contrib-jshint": "^1.0.0",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs": "^2.1.0",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-shell": "~1.1.1",
|
||||
@@ -53,6 +52,7 @@
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"is-online": "^5.1.1",
|
||||
"jasmine-core": "^2.4.0",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "~1.0.1",
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
includes: true,
|
||||
arrayRemove: true,
|
||||
copy: true,
|
||||
shallowCopy: true,
|
||||
equals: true,
|
||||
csp: true,
|
||||
jq: true,
|
||||
@@ -935,31 +934,6 @@ function copy(source, destination) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of an object, an array or a primitive.
|
||||
*
|
||||
* Assumes that there are no proto properties for objects.
|
||||
*/
|
||||
function shallowCopy(src, dst) {
|
||||
if (isArray(src)) {
|
||||
dst = dst || [];
|
||||
|
||||
for (var i = 0, ii = src.length; i < ii; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
} else if (isObject(src)) {
|
||||
dst = dst || {};
|
||||
|
||||
for (var key in src) {
|
||||
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dst || src;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@
|
||||
* ## Angular's jqLite
|
||||
* jqLite provides only the following jQuery methods:
|
||||
*
|
||||
* - [`addClass()`](http://api.jquery.com/addClass/)
|
||||
* - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
|
||||
* - [`after()`](http://api.jquery.com/after/)
|
||||
* - [`append()`](http://api.jquery.com/append/)
|
||||
* - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
|
||||
@@ -80,7 +80,7 @@
|
||||
* - [`ready()`](http://api.jquery.com/ready/)
|
||||
* - [`remove()`](http://api.jquery.com/remove/)
|
||||
* - [`removeAttr()`](http://api.jquery.com/removeAttr/)
|
||||
* - [`removeClass()`](http://api.jquery.com/removeClass/)
|
||||
* - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
|
||||
* - [`removeData()`](http://api.jquery.com/removeData/)
|
||||
* - [`replaceWith()`](http://api.jquery.com/replaceWith/)
|
||||
* - [`text()`](http://api.jquery.com/text/)
|
||||
|
||||
+49
-7
@@ -420,7 +420,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {DOMElement} parent the parent element which will append the element as
|
||||
* a child (so long as the after element is not present)
|
||||
* @param {DOMElement=} after the sibling element after which the element will be appended
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -446,7 +452,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {DOMElement} parent the parent element which will append the element as
|
||||
* a child (so long as the after element is not present)
|
||||
* @param {DOMElement=} after the sibling element after which the element will be appended
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -467,7 +479,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* digest once the animation has completed.
|
||||
*
|
||||
* @param {DOMElement} element the element which will be removed from the DOM
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -491,7 +509,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
*
|
||||
* @param {DOMElement} element the element which the CSS classes will be applied to
|
||||
* @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -515,7 +539,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
*
|
||||
* @param {DOMElement} element the element which the CSS classes will be applied to
|
||||
* @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -540,7 +570,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {DOMElement} element the element which the CSS classes will be applied to
|
||||
* @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
|
||||
* @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -581,7 +617,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
|
||||
* this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
|
||||
* (Note that if no animation is detected then this value will not be applied to the element.)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
|
||||
+28
-14
@@ -1311,11 +1311,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
// We must run this hook in an apply since the $$postDigest runs outside apply
|
||||
$rootScope.$apply(function() {
|
||||
var errors = [];
|
||||
for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
|
||||
onChangesQueue[i]();
|
||||
try {
|
||||
onChangesQueue[i]();
|
||||
} catch (e) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
// Reset the queue to trigger a new schedule next time there is a change
|
||||
onChangesQueue = undefined;
|
||||
if (errors.length) {
|
||||
throw errors;
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
onChangesTtl++;
|
||||
@@ -2491,10 +2499,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
forEach(elementControllers, function(controller) {
|
||||
var controllerInstance = controller.instance;
|
||||
if (isFunction(controllerInstance.$onChanges)) {
|
||||
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
|
||||
try {
|
||||
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
if (isFunction(controllerInstance.$onInit)) {
|
||||
controllerInstance.$onInit();
|
||||
try {
|
||||
controllerInstance.$onInit();
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
if (isFunction(controllerInstance.$onDestroy)) {
|
||||
controllerScope.$on('$destroy', function callOnDestroyHook() {
|
||||
@@ -2758,18 +2774,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
// copy the new attributes on the old attrs object
|
||||
forEach(src, function(value, key) {
|
||||
if (key == 'class') {
|
||||
safeAddClass($element, value);
|
||||
dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
|
||||
} else if (key == 'style') {
|
||||
$element.attr('style', $element.attr('style') + ';' + value);
|
||||
dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
|
||||
// `dst` will never contain hasOwnProperty as DOM parser won't let it.
|
||||
// You will get an "InvalidCharacterError: DOM Exception 5" error if you
|
||||
// have an attribute like "has-own-property" or "data-has-own-property", etc.
|
||||
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
|
||||
// Check if we already set this attribute in the loop above.
|
||||
// `dst` will never contain hasOwnProperty as DOM parser won't let it.
|
||||
// You will get an "InvalidCharacterError: DOM Exception 5" error if you
|
||||
// have an attribute like "has-own-property" or "data-has-own-property", etc.
|
||||
if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
|
||||
dst[key] = value;
|
||||
dstAttr[key] = srcAttr[key];
|
||||
|
||||
if (key !== 'class' && key !== 'style') {
|
||||
dstAttr[key] = srcAttr[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-
|
||||
// 9. Fragment
|
||||
// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
|
||||
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
||||
/* jshint maxlen:220 */
|
||||
var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
|
||||
/* jshint maxlen:200 */
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
|
||||
var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
* it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
|
||||
*
|
||||
* - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
|
||||
* objects, and will throw if used with one.
|
||||
* objects, and will throw an error if used with one.
|
||||
*
|
||||
* If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
|
||||
* that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
|
||||
|
||||
+14
-11
@@ -15,17 +15,20 @@
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* ```js
|
||||
* angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
|
||||
* return function(exception, cause) {
|
||||
* exception.message += ' (caused by "' + cause + '")';
|
||||
* throw exception;
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
* The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
|
||||
* errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
|
||||
* of `$log.error()`.
|
||||
*
|
||||
* This example will override the normal action of `$exceptionHandler`, to make angular
|
||||
* exceptions fail hard when they happen, instead of just logging to the console.
|
||||
* ```js
|
||||
* angular.
|
||||
* module('exceptionOverwrite', []).
|
||||
* factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
|
||||
* return function myExceptionHandler(exception, cause) {
|
||||
* logErrorsToBackend(exception, cause);
|
||||
* $log.warn(exception, cause);
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* <hr />
|
||||
* Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
|
||||
@@ -36,7 +39,7 @@
|
||||
* `try { ... } catch(e) { $exceptionHandler(e); }`
|
||||
*
|
||||
* @param {Error} exception Exception associated with the error.
|
||||
* @param {string=} cause optional information about the context in which
|
||||
* @param {string=} cause Optional information about the context in which
|
||||
* the error was thrown.
|
||||
*
|
||||
*/
|
||||
|
||||
+21
-14
@@ -6,21 +6,22 @@
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Creates a new array or string containing only a specified number of elements. The elements
|
||||
* are taken from either the beginning or the end of the source array, string or number, as specified by
|
||||
* the value and sign (positive or negative) of `limit`. If a number is used as input, it is
|
||||
* converted to a string.
|
||||
* Creates a new array or string containing only a specified number of elements. The elements are
|
||||
* taken from either the beginning or the end of the source array, string or number, as specified by
|
||||
* the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
|
||||
* (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
|
||||
* it is converted to a string.
|
||||
*
|
||||
* @param {Array|string|number} input Source array, string or number to be limited.
|
||||
* @param {string|number} limit The length of the returned array or string. If the `limit` number
|
||||
* @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
|
||||
* @param {string|number} limit - The length of the returned array or string. If the `limit` number
|
||||
* is positive, `limit` number of items from the beginning of the source array/string are copied.
|
||||
* If the number is negative, `limit` number of items from the end of the source array/string
|
||||
* are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
|
||||
* the input will be returned unchanged.
|
||||
* @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
|
||||
* indicates an offset from the end of `input`. Defaults to `0`.
|
||||
* @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
|
||||
* had less than `limit` elements.
|
||||
* @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
|
||||
* `begin` indicates an offset from the end of `input`. Defaults to `0`.
|
||||
* @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
|
||||
* less than `limit` elements.
|
||||
*
|
||||
* @example
|
||||
<example module="limitToExample">
|
||||
@@ -108,19 +109,25 @@ function limitToFilter() {
|
||||
if (isNaN(limit)) return input;
|
||||
|
||||
if (isNumber(input)) input = input.toString();
|
||||
if (!isArray(input) && !isString(input)) return input;
|
||||
if (!isArrayLike(input)) return input;
|
||||
|
||||
begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
|
||||
begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
|
||||
|
||||
if (limit >= 0) {
|
||||
return input.slice(begin, begin + limit);
|
||||
return sliceFn(input, begin, begin + limit);
|
||||
} else {
|
||||
if (begin === 0) {
|
||||
return input.slice(limit, input.length);
|
||||
return sliceFn(input, limit, input.length);
|
||||
} else {
|
||||
return input.slice(Math.max(0, begin + limit), begin);
|
||||
return sliceFn(input, Math.max(0, begin + limit), begin);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function sliceFn(input, begin, end) {
|
||||
if (isString(input)) return input.slice(begin, end);
|
||||
|
||||
return slice.call(input, begin, end);
|
||||
}
|
||||
|
||||
+534
-159
@@ -6,44 +6,128 @@
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
|
||||
* for strings and numerically for numbers. Note: if you notice numbers are not being sorted
|
||||
* as expected, make sure they are actually being saved as numbers and not strings.
|
||||
* Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
|
||||
* Returns an array containing the items from the specified `collection`, ordered by a `comparator`
|
||||
* function based on the values computed using the `expression` predicate.
|
||||
*
|
||||
* @param {Array} array The array (or array-like object) to sort.
|
||||
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
|
||||
* used by the comparator to determine the order of elements.
|
||||
* For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
|
||||
* `[{id: 'bar'}, {id: 'foo'}]`.
|
||||
*
|
||||
* The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
|
||||
* String, etc).
|
||||
*
|
||||
* The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
|
||||
* for the preceeding one. The `expression` is evaluated against each item and the output is used
|
||||
* for comparing with other items.
|
||||
*
|
||||
* You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
|
||||
* ascending order.
|
||||
*
|
||||
* The comparison is done using the `comparator` function. If none is specified, a default, built-in
|
||||
* comparator is used (see below for details - in a nutshell, it compares numbers numerically and
|
||||
* strings alphabetically).
|
||||
*
|
||||
* ### Under the hood
|
||||
*
|
||||
* Ordering the specified `collection` happens in two phases:
|
||||
*
|
||||
* 1. All items are passed through the predicate (or predicates), and the returned values are saved
|
||||
* along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
|
||||
* through a predicate that extracts the value of the `label` property, would be transformed to:
|
||||
* ```
|
||||
* {
|
||||
* value: 'foo',
|
||||
* type: 'string',
|
||||
* index: ...
|
||||
* }
|
||||
* ```
|
||||
* 2. The comparator function is used to sort the items, based on the derived values, types and
|
||||
* indices.
|
||||
*
|
||||
* If you use a custom comparator, it will be called with pairs of objects of the form
|
||||
* `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
|
||||
* (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
|
||||
* second, or `1` otherwise.
|
||||
*
|
||||
* In order to ensure that the sorting will be deterministic across platforms, if none of the
|
||||
* specified predicates can distinguish between two items, `orderBy` will automatically introduce a
|
||||
* dummy predicate that returns the item's index as `value`.
|
||||
* (If you are using a custom comparator, make sure it can handle this predicate as well.)
|
||||
*
|
||||
* Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
|
||||
* value for an item, `orderBy` will try to convert that object to a primitive value, before passing
|
||||
* it to the comparator. The following rules govern the conversion:
|
||||
*
|
||||
* 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
|
||||
* used instead.<br />
|
||||
* (If the object has a `valueOf()` method that returns another object, then the returned object
|
||||
* will be used in subsequent steps.)
|
||||
* 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
|
||||
* returns a primitive, its return value will be used instead.<br />
|
||||
* (If the object has a `toString()` method that returns another object, then the returned object
|
||||
* will be used in subsequent steps.)
|
||||
* 3. No conversion; the object itself is used.
|
||||
*
|
||||
* ### The default comparator
|
||||
*
|
||||
* The default, built-in comparator should be sufficient for most usecases. In short, it compares
|
||||
* numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
|
||||
* using their index in the original collection, and sorts values of different types by type.
|
||||
*
|
||||
* More specifically, it follows these steps to determine the relative order of items:
|
||||
*
|
||||
* 1. If the compared values are of different types, compare the types themselves alphabetically.
|
||||
* 2. If both values are of type `string`, compare them alphabetically in a case- and
|
||||
* locale-insensitive way.
|
||||
* 3. If both values are objects, compare their indices instead.
|
||||
* 4. Otherwise, return:
|
||||
* - `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
|
||||
* - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
|
||||
* - `1`, otherwise.
|
||||
*
|
||||
* **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
|
||||
* saved as numbers and not strings.
|
||||
*
|
||||
* @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
|
||||
* @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
|
||||
* predicates) to be used by the comparator to determine the order of elements.
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
* - `function`: Getter function. The result of this function will be sorted using the
|
||||
* `<`, `===`, `>` operator.
|
||||
* - `string`: An Angular expression. The result of this expression is used to compare elements
|
||||
* (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
|
||||
* 3 first characters of a property called `name`). The result of a constant expression
|
||||
* is interpreted as a property name to be used in comparisons (for example `"special name"`
|
||||
* to sort object by the value of their `special name` property). An expression can be
|
||||
* optionally prefixed with `+` or `-` to control ascending or descending sort order
|
||||
* (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
|
||||
* element itself is used to compare where sorting.
|
||||
* - `Array`: An array of function or string predicates. The first predicate in the array
|
||||
* is used for sorting, but when two items are equivalent, the next predicate is used.
|
||||
* - `Function`: A getter function. This function will be called with each item as argument and
|
||||
* the return value will be used for sorting.
|
||||
* - `string`: An Angular expression. This expression will be evaluated against each item and the
|
||||
* result will be used for sorting. For example, use `'label'` to sort by a property called
|
||||
* `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
|
||||
* property.<br />
|
||||
* (The result of a constant expression is interpreted as a property name to be used for
|
||||
* comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
|
||||
* property called `special name`.)<br />
|
||||
* An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
|
||||
* ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
|
||||
* (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
|
||||
* - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
|
||||
* relative order of two items, the next predicate is used as a tie-breaker.
|
||||
*
|
||||
* If the predicate is missing or empty then it defaults to `'+'`.
|
||||
* **Note:** If the predicate is missing or empty then it defaults to `'+'`.
|
||||
*
|
||||
* @param {boolean=} reverse Reverse the order of the array.
|
||||
* @returns {Array} Sorted copy of the source array.
|
||||
* @param {boolean=} reverse - If `true`, reverse the sorting order.
|
||||
* @param {(Function)=} comparator - The comparator function used to determine the relative order of
|
||||
* value pairs. If omitted, the built-in comparator will be used.
|
||||
*
|
||||
* @returns {Array} - The sorted array.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* The example below demonstrates a simple ngRepeat, where the data is sorted
|
||||
* by age in descending order (predicate is set to `'-age'`).
|
||||
* `reverse` is not set, which means it defaults to `false`.
|
||||
<example module="orderByExample">
|
||||
* ### Ordering a table with `ngRepeat`
|
||||
*
|
||||
* The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
|
||||
* age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
|
||||
* it defaults to the built-in comparator.
|
||||
*
|
||||
<example name="orderBy-static" module="orderByExample1">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<table class="friend">
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Phone Number</th>
|
||||
@@ -58,43 +142,77 @@
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample', [])
|
||||
angular.module('orderByExample1', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.friends =
|
||||
[{name:'John', phone:'555-1212', age:10},
|
||||
{name:'Mary', phone:'555-9876', age:19},
|
||||
{name:'Mike', phone:'555-4321', age:21},
|
||||
{name:'Adam', phone:'555-5678', age:35},
|
||||
{name:'Julie', phone:'555-8765', age:29}];
|
||||
$scope.friends = [
|
||||
{name: 'John', phone: '555-1212', age: 10},
|
||||
{name: 'Mary', phone: '555-9876', age: 19},
|
||||
{name: 'Mike', phone: '555-4321', age: 21},
|
||||
{name: 'Adam', phone: '555-5678', age: 35},
|
||||
{name: 'Julie', phone: '555-8765', age: 29}
|
||||
];
|
||||
}]);
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var names = element.all(by.repeater('friends').column('friend.name'));
|
||||
|
||||
it('should sort friends by age in reverse order', function() {
|
||||
expect(names.get(0).getText()).toBe('Adam');
|
||||
expect(names.get(1).getText()).toBe('Julie');
|
||||
expect(names.get(2).getText()).toBe('Mike');
|
||||
expect(names.get(3).getText()).toBe('Mary');
|
||||
expect(names.get(4).getText()).toBe('John');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
* <hr />
|
||||
*
|
||||
* The predicate and reverse parameters can be controlled dynamically through scope properties,
|
||||
* as shown in the next example.
|
||||
* @example
|
||||
<example module="orderByExample">
|
||||
* ### Changing parameters dynamically
|
||||
*
|
||||
* All parameters can be changed dynamically. The next example shows how you can make the columns of
|
||||
* a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
|
||||
*
|
||||
<example name="orderBy-dynamic" module="orderByExample2">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
|
||||
<pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
|
||||
<hr/>
|
||||
<button ng-click="predicate=''">Set to unsorted</button>
|
||||
<table class="friend">
|
||||
<button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
|
||||
<hr/>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>
|
||||
<button ng-click="order('name')">Name</button>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('age')">Age</button>
|
||||
<span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('name')">Name</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('age')">Age</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
|
||||
<tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<td>{{friend.age}}</td>
|
||||
@@ -103,100 +221,335 @@
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample', [])
|
||||
angular.module('orderByExample2', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.friends =
|
||||
[{name:'John', phone:'555-1212', age:10},
|
||||
{name:'Mary', phone:'555-9876', age:19},
|
||||
{name:'Mike', phone:'555-4321', age:21},
|
||||
{name:'Adam', phone:'555-5678', age:35},
|
||||
{name:'Julie', phone:'555-8765', age:29}];
|
||||
$scope.predicate = 'age';
|
||||
var friends = [
|
||||
{name: 'John', phone: '555-1212', age: 10},
|
||||
{name: 'Mary', phone: '555-9876', age: 19},
|
||||
{name: 'Mike', phone: '555-4321', age: 21},
|
||||
{name: 'Adam', phone: '555-5678', age: 35},
|
||||
{name: 'Julie', phone: '555-8765', age: 29}
|
||||
];
|
||||
|
||||
$scope.propertyName = 'age';
|
||||
$scope.reverse = true;
|
||||
$scope.order = function(predicate) {
|
||||
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
|
||||
$scope.predicate = predicate;
|
||||
$scope.friends = friends;
|
||||
|
||||
$scope.sortBy = function(propertyName) {
|
||||
$scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
|
||||
$scope.propertyName = propertyName;
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.sortorder:after {
|
||||
content: '\25b2';
|
||||
content: '\25b2'; // BLACK UP-POINTING TRIANGLE
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc';
|
||||
content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var unsortButton = element(by.partialButtonText('unsorted'));
|
||||
var nameHeader = element(by.partialButtonText('Name'));
|
||||
var phoneHeader = element(by.partialButtonText('Phone'));
|
||||
var ageHeader = element(by.partialButtonText('Age'));
|
||||
var firstName = element(by.repeater('friends').column('friend.name').row(0));
|
||||
var lastName = element(by.repeater('friends').column('friend.name').row(4));
|
||||
|
||||
it('should sort friends by some property, when clicking on the column header', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
phoneHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Mary');
|
||||
|
||||
nameHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('Mike');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
});
|
||||
|
||||
it('should sort friends in reverse order, when clicking on the same column', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
});
|
||||
|
||||
it('should restore the original order, when clicking "Set to unsorted"', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
unsortButton.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Julie');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
* <hr />
|
||||
*
|
||||
* @example
|
||||
* ### Using `orderBy` inside a controller
|
||||
*
|
||||
* It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
|
||||
* calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
|
||||
* and retrieve the `orderBy` filter with `$filter('orderBy')`.)
|
||||
*
|
||||
<example name="orderBy-call-manually" module="orderByExample3">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
|
||||
<hr/>
|
||||
<button ng-click="sortBy(null)">Set to unsorted</button>
|
||||
<hr/>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>
|
||||
<button ng-click="sortBy('name')">Name</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('age')">Age</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<td>{{friend.age}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample3', [])
|
||||
.controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
|
||||
var friends = [
|
||||
{name: 'John', phone: '555-1212', age: 10},
|
||||
{name: 'Mary', phone: '555-9876', age: 19},
|
||||
{name: 'Mike', phone: '555-4321', age: 21},
|
||||
{name: 'Adam', phone: '555-5678', age: 35},
|
||||
{name: 'Julie', phone: '555-8765', age: 29}
|
||||
];
|
||||
|
||||
$scope.propertyName = 'age';
|
||||
$scope.reverse = true;
|
||||
$scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
|
||||
|
||||
$scope.sortBy = function(propertyName) {
|
||||
$scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
|
||||
? !$scope.reverse : false;
|
||||
$scope.propertyName = propertyName;
|
||||
$scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.sortorder:after {
|
||||
content: '\25b2'; // BLACK UP-POINTING TRIANGLE
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var unsortButton = element(by.partialButtonText('unsorted'));
|
||||
var nameHeader = element(by.partialButtonText('Name'));
|
||||
var phoneHeader = element(by.partialButtonText('Phone'));
|
||||
var ageHeader = element(by.partialButtonText('Age'));
|
||||
var firstName = element(by.repeater('friends').column('friend.name').row(0));
|
||||
var lastName = element(by.repeater('friends').column('friend.name').row(4));
|
||||
|
||||
it('should sort friends by some property, when clicking on the column header', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
phoneHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Mary');
|
||||
|
||||
nameHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('Mike');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
});
|
||||
|
||||
it('should sort friends in reverse order, when clicking on the same column', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
});
|
||||
|
||||
it('should restore the original order, when clicking "Set to unsorted"', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
unsortButton.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Julie');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
* <hr />
|
||||
*
|
||||
* @example
|
||||
* ### Using a custom comparator
|
||||
*
|
||||
* If you have very specific requirements about the way items are sorted, you can pass your own
|
||||
* comparator function. For example, you might need to compare some strings in a locale-sensitive
|
||||
* way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
|
||||
* argument - passing `false` retains the default sorting order, i.e. ascending.)
|
||||
*
|
||||
<example name="orderBy-custom-comparator" module="orderByExample4">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<div class="friends-container custom-comparator">
|
||||
<h3>Locale-sensitive Comparator</h3>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Favorite Letter</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.favoriteLetter}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="friends-container default-comparator">
|
||||
<h3>Default Comparator</h3>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Favorite Letter</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.favoriteLetter}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample4', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.friends = [
|
||||
{name: 'John', favoriteLetter: 'Ä'},
|
||||
{name: 'Mary', favoriteLetter: 'Ü'},
|
||||
{name: 'Mike', favoriteLetter: 'Ö'},
|
||||
{name: 'Adam', favoriteLetter: 'H'},
|
||||
{name: 'Julie', favoriteLetter: 'Z'}
|
||||
];
|
||||
|
||||
$scope.localeSensitiveComparator = function(v1, v2) {
|
||||
// If we don't get strings, just compare by index
|
||||
if (v1.type !== 'string' || v2.type !== 'string') {
|
||||
return (v1.index < v2.index) ? -1 : 1;
|
||||
}
|
||||
|
||||
// Compare strings alphabetically, taking locale into account
|
||||
return v1.value.localeCompare(v2.value);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends-container {
|
||||
display: inline-block;
|
||||
margin: 0 30px;
|
||||
}
|
||||
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var container = element(by.css('.custom-comparator'));
|
||||
var names = container.all(by.repeater('friends').column('friend.name'));
|
||||
|
||||
it('should sort friends by favorite letter (in correct alphabetical order)', function() {
|
||||
expect(names.get(0).getText()).toBe('John');
|
||||
expect(names.get(1).getText()).toBe('Adam');
|
||||
expect(names.get(2).getText()).toBe('Mike');
|
||||
expect(names.get(3).getText()).toBe('Mary');
|
||||
expect(names.get(4).getText()).toBe('Julie');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
* It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
|
||||
* filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
|
||||
* desired parameters.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @example
|
||||
<example module="orderByExample">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
|
||||
<table class="friend">
|
||||
<tr>
|
||||
<th>
|
||||
<button ng-click="order('name')">Name</button>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('age')">Age</button>
|
||||
<span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<td>{{friend.age}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample', [])
|
||||
.controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
|
||||
var orderBy = $filter('orderBy');
|
||||
$scope.friends = [
|
||||
{ name: 'John', phone: '555-1212', age: 10 },
|
||||
{ name: 'Mary', phone: '555-9876', age: 19 },
|
||||
{ name: 'Mike', phone: '555-4321', age: 21 },
|
||||
{ name: 'Adam', phone: '555-5678', age: 35 },
|
||||
{ name: 'Julie', phone: '555-8765', age: 29 }
|
||||
];
|
||||
$scope.order = function(predicate) {
|
||||
$scope.predicate = predicate;
|
||||
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
|
||||
$scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
|
||||
};
|
||||
$scope.order('age', true);
|
||||
}]);
|
||||
</file>
|
||||
|
||||
<file name="style.css">
|
||||
.sortorder:after {
|
||||
content: '\25b2';
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc';
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
orderByFilter.$inject = ['$parse'];
|
||||
function orderByFilter($parse) {
|
||||
return function(array, sortPredicate, reverseOrder) {
|
||||
return function(array, sortPredicate, reverseOrder, compareFn) {
|
||||
|
||||
if (array == null) return array;
|
||||
if (!isArrayLike(array)) {
|
||||
@@ -206,11 +559,12 @@ function orderByFilter($parse) {
|
||||
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
|
||||
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
|
||||
|
||||
var predicates = processPredicates(sortPredicate, reverseOrder);
|
||||
// Add a predicate at the end that evaluates to the element index. This makes the
|
||||
// sort stable as it works as a tie-breaker when all the input predicates cannot
|
||||
// distinguish between two elements.
|
||||
predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
|
||||
var predicates = processPredicates(sortPredicate);
|
||||
|
||||
var descending = reverseOrder ? -1 : 1;
|
||||
|
||||
// Define the `compare()` function. Use a default comparator if none is specified.
|
||||
var compare = isFunction(compareFn) ? compareFn : defaultCompare;
|
||||
|
||||
// The next three lines are a version of a Swartzian Transform idiom from Perl
|
||||
// (sometimes called the Decorate-Sort-Undecorate idiom)
|
||||
@@ -222,8 +576,12 @@ function orderByFilter($parse) {
|
||||
return array;
|
||||
|
||||
function getComparisonObject(value, index) {
|
||||
// NOTE: We are adding an extra `tieBreaker` value based on the element's index.
|
||||
// This will be used to keep the sort stable when none of the input predicates can
|
||||
// distinguish between two elements.
|
||||
return {
|
||||
value: value,
|
||||
tieBreaker: {value: index, type: 'number', index: index},
|
||||
predicateValues: predicates.map(function(predicate) {
|
||||
return getPredicateValue(predicate.get(value), index);
|
||||
})
|
||||
@@ -231,18 +589,19 @@ function orderByFilter($parse) {
|
||||
}
|
||||
|
||||
function doComparison(v1, v2) {
|
||||
var result = 0;
|
||||
for (var index=0, length = predicates.length; index < length; ++index) {
|
||||
result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
|
||||
if (result) break;
|
||||
for (var i = 0, ii = predicates.length; i < ii; i++) {
|
||||
var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
|
||||
if (result) {
|
||||
return result * predicates[i].descending * descending;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
return compare(v1.tieBreaker, v2.tieBreaker) * descending;
|
||||
}
|
||||
};
|
||||
|
||||
function processPredicates(sortPredicate, reverseOrder) {
|
||||
reverseOrder = reverseOrder ? -1 : 1;
|
||||
return sortPredicate.map(function(predicate) {
|
||||
function processPredicates(sortPredicates) {
|
||||
return sortPredicates.map(function(predicate) {
|
||||
var descending = 1, get = identity;
|
||||
|
||||
if (isFunction(predicate)) {
|
||||
@@ -260,7 +619,7 @@ function orderByFilter($parse) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return { get: get, descending: descending * reverseOrder };
|
||||
return {get: get, descending: descending};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -275,9 +634,9 @@ function orderByFilter($parse) {
|
||||
}
|
||||
}
|
||||
|
||||
function objectValue(value, index) {
|
||||
function objectValue(value) {
|
||||
// If `valueOf` is a valid function use that
|
||||
if (typeof value.valueOf === 'function') {
|
||||
if (isFunction(value.valueOf)) {
|
||||
value = value.valueOf();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
@@ -286,8 +645,8 @@ function orderByFilter($parse) {
|
||||
value = value.toString();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
// We have a basic object so we use the position of the object in the collection
|
||||
return index;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getPredicateValue(value, index) {
|
||||
@@ -295,23 +654,39 @@ function orderByFilter($parse) {
|
||||
if (value === null) {
|
||||
type = 'string';
|
||||
value = 'null';
|
||||
} else if (type === 'string') {
|
||||
value = value.toLowerCase();
|
||||
} else if (type === 'object') {
|
||||
value = objectValue(value, index);
|
||||
value = objectValue(value);
|
||||
}
|
||||
return { value: value, type: type };
|
||||
return {value: value, type: type, index: index};
|
||||
}
|
||||
|
||||
function compare(v1, v2) {
|
||||
function defaultCompare(v1, v2) {
|
||||
var result = 0;
|
||||
if (v1.type === v2.type) {
|
||||
if (v1.value !== v2.value) {
|
||||
result = v1.value < v2.value ? -1 : 1;
|
||||
var type1 = v1.type;
|
||||
var type2 = v2.type;
|
||||
|
||||
if (type1 === type2) {
|
||||
var value1 = v1.value;
|
||||
var value2 = v2.value;
|
||||
|
||||
if (type1 === 'string') {
|
||||
// Compare strings case-insensitively
|
||||
value1 = value1.toLowerCase();
|
||||
value2 = value2.toLowerCase();
|
||||
} else if (type1 === 'object') {
|
||||
// For basic objects, use the position of the object
|
||||
// in the collection instead of the value
|
||||
if (isObject(value1)) value1 = v1.index;
|
||||
if (isObject(value2)) value2 = v2.index;
|
||||
}
|
||||
|
||||
if (value1 !== value2) {
|
||||
result = value1 < value2 ? -1 : 1;
|
||||
}
|
||||
} else {
|
||||
result = v1.type < v2.type ? -1 : 1;
|
||||
result = type1 < type2 ? -1 : 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,6 +328,12 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
|
||||
|
||||
var locationPrototype = {
|
||||
|
||||
/**
|
||||
* Ensure absolute url is initialized.
|
||||
* @private
|
||||
*/
|
||||
$$absUrl:'',
|
||||
|
||||
/**
|
||||
* Are we in html5 mode?
|
||||
* @private
|
||||
|
||||
+1
-1
@@ -545,7 +545,7 @@ AST.prototype = {
|
||||
var args = [];
|
||||
if (this.peekToken().text !== ')') {
|
||||
do {
|
||||
args.push(this.expression());
|
||||
args.push(this.filterChain());
|
||||
} while (this.expect(','));
|
||||
}
|
||||
return args;
|
||||
|
||||
@@ -348,8 +348,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
|
||||
// may attempt more elements, but custom drivers are more particular
|
||||
for (var i = drivers.length - 1; i >= 0; i--) {
|
||||
var driverName = drivers[i];
|
||||
if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check
|
||||
|
||||
var factory = $injector.get(driverName);
|
||||
var driver = factory(animationDetails);
|
||||
if (driver) {
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
* @name ngComponentRouter
|
||||
* @installation
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* **Deprecation Notice:** In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
* </div>
|
||||
*
|
||||
* Currently, the **Component Router** module must be installed via `npm`, it is not yet available
|
||||
* on Bower or the Google CDN.
|
||||
*
|
||||
@@ -15,7 +20,8 @@
|
||||
* <script src="/node_modules/@angular/router/angular1/angular_1_router.js"></script>
|
||||
*```
|
||||
*
|
||||
* You also need to include ES6 shims to support running on Internet Explorer:
|
||||
* You also need to include ES6 shims for browsers that do not support ES6 code (Internet Explorer,
|
||||
iOs < 8, Android < 5.0, Windows Mobile < 10):
|
||||
* ```html
|
||||
* <!-- IE required polyfills, in this exact order -->
|
||||
* <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
|
||||
@@ -45,6 +51,11 @@
|
||||
* You can see the state of a router by inspecting the read-only field `router.navigating`.
|
||||
* This may be useful for showing a spinner, for instance.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -57,6 +68,12 @@
|
||||
* Apart from the **Top Level Component** ({@link $routerRootComponent}) which is associated with
|
||||
* the {@link $rootRouter}, every **Routing Component** is associated with a `ChildRouter`,
|
||||
* which manages the routing for that **Routing Component**.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -69,12 +86,19 @@
|
||||
* There is only one instance of this type in a Component Router application injectable as the
|
||||
* {@link $rootRouter} service. This **Router** is associate with the **Top Level Component**
|
||||
* ({@link $routerRootComponent}). It acts as the connection between the **Routers** and the **Location**.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name ComponentInstruction
|
||||
* @description
|
||||
*
|
||||
* A `ComponentInstruction` represents the route state for a single component. An `Instruction` is
|
||||
* composed of a tree of these `ComponentInstruction`s.
|
||||
*
|
||||
@@ -82,6 +106,12 @@
|
||||
* to route lifecycle hooks, like `$routerCanActivate`.
|
||||
*
|
||||
* You should not modify this object. It should be treated as immutable.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -96,14 +126,27 @@
|
||||
* * `component`, `loader`, `redirectTo` (requires exactly one of these)
|
||||
* * `name` - the name used to identify the **Route Definition** when generating links
|
||||
* * `data` (optional)
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name RouteParams
|
||||
* @description
|
||||
*
|
||||
* A map of parameters for a given route, passed as part of the {@link ComponentInstruction} to
|
||||
* the Lifecycle Hooks, such as `$routerOnActivate` and `$routerOnDeactivate`.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -114,6 +157,12 @@
|
||||
* @description
|
||||
*
|
||||
* The directive that identifies where the {@link Router} should render its **Components**.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -128,6 +177,12 @@
|
||||
* ```html
|
||||
* <a ng-link="['Parent', {param: 1}, 'Child']">Link to Child View</a>
|
||||
* ````
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
|
||||
@@ -135,8 +190,15 @@
|
||||
* @ngdoc service
|
||||
* @name $rootRouter
|
||||
* @description
|
||||
*
|
||||
* The singleton instance of the {@link RootRouter} type, which is associated
|
||||
* with the top level {@link $routerRootComponent}.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
|
||||
@@ -146,4 +208,10 @@
|
||||
* @description
|
||||
*
|
||||
* The top level **Routing Component** associated with the {@link $rootRouter}.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
+346
-335
@@ -264,377 +264,387 @@ var jqLite = angular.element;
|
||||
*/
|
||||
angular.module('ngMessages', [])
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @module ngMessages
|
||||
* @name ngMessages
|
||||
* @restrict AE
|
||||
*
|
||||
* @description
|
||||
* `ngMessages` is a directive that is designed to show and hide messages based on the state
|
||||
* of a key/value object that it listens on. The directive itself complements error message
|
||||
* reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
|
||||
*
|
||||
* `ngMessages` manages the state of internal messages within its container element. The internal
|
||||
* messages use the `ngMessage` directive and will be inserted/removed from the page depending
|
||||
* on if they're present within the key/value object. By default, only one message will be displayed
|
||||
* at a time and this depends on the prioritization of the messages within the template. (This can
|
||||
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
|
||||
*
|
||||
* A remote template can also be used to promote message reusability and messages can also be
|
||||
* overridden.
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {string} ngMessages an angular expression evaluating to a key/value object
|
||||
* (this is typically the $error object on an ngModel instance).
|
||||
* @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
|
||||
*
|
||||
* @example
|
||||
* <example name="ngMessages-directive" module="ngMessagesExample"
|
||||
* deps="angular-messages.js"
|
||||
* animations="true" fixBase="true">
|
||||
* <file name="index.html">
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Enter your name:
|
||||
* <input type="text"
|
||||
* name="myName"
|
||||
* ng-model="name"
|
||||
* ng-minlength="5"
|
||||
* ng-maxlength="20"
|
||||
* required />
|
||||
* </label>
|
||||
* <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
|
||||
*
|
||||
* <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
|
||||
* <div ng-message="required">You did not enter a field</div>
|
||||
* <div ng-message="minlength">Your field is too short</div>
|
||||
* <div ng-message="maxlength">Your field is too long</div>
|
||||
* </div>
|
||||
* </form>
|
||||
* </file>
|
||||
* <file name="script.js">
|
||||
* angular.module('ngMessagesExample', ['ngMessages']);
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
.directive('ngMessages', ['$animate', function($animate) {
|
||||
var ACTIVE_CLASS = 'ng-active';
|
||||
var INACTIVE_CLASS = 'ng-inactive';
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @module ngMessages
|
||||
* @name ngMessages
|
||||
* @restrict AE
|
||||
*
|
||||
* @description
|
||||
* `ngMessages` is a directive that is designed to show and hide messages based on the state
|
||||
* of a key/value object that it listens on. The directive itself complements error message
|
||||
* reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
|
||||
*
|
||||
* `ngMessages` manages the state of internal messages within its container element. The internal
|
||||
* messages use the `ngMessage` directive and will be inserted/removed from the page depending
|
||||
* on if they're present within the key/value object. By default, only one message will be displayed
|
||||
* at a time and this depends on the prioritization of the messages within the template. (This can
|
||||
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
|
||||
*
|
||||
* A remote template can also be used to promote message reusability and messages can also be
|
||||
* overridden.
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {string} ngMessages an angular expression evaluating to a key/value object
|
||||
* (this is typically the $error object on an ngModel instance).
|
||||
* @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
|
||||
*
|
||||
* @example
|
||||
* <example name="ngMessages-directive" module="ngMessagesExample"
|
||||
* deps="angular-messages.js"
|
||||
* animations="true" fixBase="true">
|
||||
* <file name="index.html">
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Enter your name:
|
||||
* <input type="text"
|
||||
* name="myName"
|
||||
* ng-model="name"
|
||||
* ng-minlength="5"
|
||||
* ng-maxlength="20"
|
||||
* required />
|
||||
* </label>
|
||||
* <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
|
||||
*
|
||||
* <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
|
||||
* <div ng-message="required">You did not enter a field</div>
|
||||
* <div ng-message="minlength">Your field is too short</div>
|
||||
* <div ng-message="maxlength">Your field is too long</div>
|
||||
* </div>
|
||||
* </form>
|
||||
* </file>
|
||||
* <file name="script.js">
|
||||
* angular.module('ngMessagesExample', ['ngMessages']);
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
.directive('ngMessages', ['$animate', function($animate) {
|
||||
var ACTIVE_CLASS = 'ng-active';
|
||||
var INACTIVE_CLASS = 'ng-inactive';
|
||||
|
||||
return {
|
||||
require: 'ngMessages',
|
||||
restrict: 'AE',
|
||||
controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
||||
var ctrl = this;
|
||||
var latestKey = 0;
|
||||
var nextAttachId = 0;
|
||||
return {
|
||||
require: 'ngMessages',
|
||||
restrict: 'AE',
|
||||
controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
||||
var ctrl = this;
|
||||
var latestKey = 0;
|
||||
var nextAttachId = 0;
|
||||
|
||||
this.getAttachId = function getAttachId() { return nextAttachId++; };
|
||||
this.getAttachId = function getAttachId() { return nextAttachId++; };
|
||||
|
||||
var messages = this.messages = {};
|
||||
var renderLater, cachedCollection;
|
||||
var messages = this.messages = {};
|
||||
var renderLater, cachedCollection;
|
||||
|
||||
this.render = function(collection) {
|
||||
collection = collection || {};
|
||||
this.render = function(collection) {
|
||||
collection = collection || {};
|
||||
|
||||
renderLater = false;
|
||||
cachedCollection = collection;
|
||||
renderLater = false;
|
||||
cachedCollection = collection;
|
||||
|
||||
// this is true if the attribute is empty or if the attribute value is truthy
|
||||
var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
|
||||
isAttrTruthy($scope, $attrs.multiple);
|
||||
// this is true if the attribute is empty or if the attribute value is truthy
|
||||
var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
|
||||
isAttrTruthy($scope, $attrs.multiple);
|
||||
|
||||
var unmatchedMessages = [];
|
||||
var matchedKeys = {};
|
||||
var messageItem = ctrl.head;
|
||||
var messageFound = false;
|
||||
var totalMessages = 0;
|
||||
var unmatchedMessages = [];
|
||||
var matchedKeys = {};
|
||||
var messageItem = ctrl.head;
|
||||
var messageFound = false;
|
||||
var totalMessages = 0;
|
||||
|
||||
// we use != instead of !== to allow for both undefined and null values
|
||||
while (messageItem != null) {
|
||||
totalMessages++;
|
||||
var messageCtrl = messageItem.message;
|
||||
// we use != instead of !== to allow for both undefined and null values
|
||||
while (messageItem != null) {
|
||||
totalMessages++;
|
||||
var messageCtrl = messageItem.message;
|
||||
|
||||
var messageUsed = false;
|
||||
if (!messageFound) {
|
||||
forEach(collection, function(value, key) {
|
||||
if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
|
||||
// this is to prevent the same error name from showing up twice
|
||||
if (matchedKeys[key]) return;
|
||||
matchedKeys[key] = true;
|
||||
var messageUsed = false;
|
||||
if (!messageFound) {
|
||||
forEach(collection, function(value, key) {
|
||||
if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
|
||||
// this is to prevent the same error name from showing up twice
|
||||
if (matchedKeys[key]) return;
|
||||
matchedKeys[key] = true;
|
||||
|
||||
messageUsed = true;
|
||||
messageCtrl.attach();
|
||||
}
|
||||
});
|
||||
}
|
||||
messageUsed = true;
|
||||
messageCtrl.attach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (messageUsed) {
|
||||
// unless we want to display multiple messages then we should
|
||||
// set a flag here to avoid displaying the next message in the list
|
||||
messageFound = !multiple;
|
||||
} else {
|
||||
unmatchedMessages.push(messageCtrl);
|
||||
}
|
||||
if (messageUsed) {
|
||||
// unless we want to display multiple messages then we should
|
||||
// set a flag here to avoid displaying the next message in the list
|
||||
messageFound = !multiple;
|
||||
} else {
|
||||
unmatchedMessages.push(messageCtrl);
|
||||
}
|
||||
|
||||
messageItem = messageItem.next;
|
||||
}
|
||||
messageItem = messageItem.next;
|
||||
}
|
||||
|
||||
forEach(unmatchedMessages, function(messageCtrl) {
|
||||
messageCtrl.detach();
|
||||
});
|
||||
forEach(unmatchedMessages, function(messageCtrl) {
|
||||
messageCtrl.detach();
|
||||
});
|
||||
|
||||
unmatchedMessages.length !== totalMessages
|
||||
unmatchedMessages.length !== totalMessages
|
||||
? $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS)
|
||||
: $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
|
||||
};
|
||||
};
|
||||
|
||||
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
||||
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
||||
|
||||
// If the element is destroyed, proactively destroy all the currently visible messages
|
||||
$element.on('$destroy', function() {
|
||||
forEach(messages, function(item) {
|
||||
item.message.detach();
|
||||
});
|
||||
});
|
||||
// If the element is destroyed, proactively destroy all the currently visible messages
|
||||
$element.on('$destroy', function() {
|
||||
forEach(messages, function(item) {
|
||||
item.message.detach();
|
||||
});
|
||||
});
|
||||
|
||||
this.reRender = function() {
|
||||
if (!renderLater) {
|
||||
renderLater = true;
|
||||
$scope.$evalAsync(function() {
|
||||
if (renderLater) {
|
||||
cachedCollection && ctrl.render(cachedCollection);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
this.reRender = function() {
|
||||
if (!renderLater) {
|
||||
renderLater = true;
|
||||
$scope.$evalAsync(function() {
|
||||
if (renderLater) {
|
||||
cachedCollection && ctrl.render(cachedCollection);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.register = function(comment, messageCtrl) {
|
||||
var nextKey = latestKey.toString();
|
||||
messages[nextKey] = {
|
||||
message: messageCtrl
|
||||
};
|
||||
insertMessageNode($element[0], comment, nextKey);
|
||||
comment.$$ngMessageNode = nextKey;
|
||||
latestKey++;
|
||||
this.register = function(comment, messageCtrl) {
|
||||
var nextKey = latestKey.toString();
|
||||
messages[nextKey] = {
|
||||
message: messageCtrl
|
||||
};
|
||||
insertMessageNode($element[0], comment, nextKey);
|
||||
comment.$$ngMessageNode = nextKey;
|
||||
latestKey++;
|
||||
|
||||
ctrl.reRender();
|
||||
};
|
||||
ctrl.reRender();
|
||||
};
|
||||
|
||||
this.deregister = function(comment) {
|
||||
var key = comment.$$ngMessageNode;
|
||||
delete comment.$$ngMessageNode;
|
||||
removeMessageNode($element[0], comment, key);
|
||||
delete messages[key];
|
||||
ctrl.reRender();
|
||||
};
|
||||
this.deregister = function(comment) {
|
||||
var key = comment.$$ngMessageNode;
|
||||
delete comment.$$ngMessageNode;
|
||||
removeMessageNode($element[0], comment, key);
|
||||
delete messages[key];
|
||||
ctrl.reRender();
|
||||
};
|
||||
|
||||
function findPreviousMessage(parent, comment) {
|
||||
var prevNode = comment;
|
||||
var parentLookup = [];
|
||||
function findPreviousMessage(parent, comment) {
|
||||
var prevNode = comment;
|
||||
var parentLookup = [];
|
||||
|
||||
while (prevNode && prevNode !== parent) {
|
||||
var prevKey = prevNode.$$ngMessageNode;
|
||||
if (prevKey && prevKey.length) {
|
||||
return messages[prevKey];
|
||||
}
|
||||
while (prevNode && prevNode !== parent) {
|
||||
var prevKey = prevNode.$$ngMessageNode;
|
||||
if (prevKey && prevKey.length) {
|
||||
return messages[prevKey];
|
||||
}
|
||||
|
||||
// dive deeper into the DOM and examine its children for any ngMessage
|
||||
// comments that may be in an element that appears deeper in the list
|
||||
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
|
||||
parentLookup.push(prevNode);
|
||||
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
|
||||
} else if (prevNode.previousSibling) {
|
||||
prevNode = prevNode.previousSibling;
|
||||
} else {
|
||||
prevNode = prevNode.parentNode;
|
||||
parentLookup.push(prevNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// dive deeper into the DOM and examine its children for any ngMessage
|
||||
// comments that may be in an element that appears deeper in the list
|
||||
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) === -1) {
|
||||
parentLookup.push(prevNode);
|
||||
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
|
||||
} else if (prevNode.previousSibling) {
|
||||
prevNode = prevNode.previousSibling;
|
||||
} else {
|
||||
prevNode = prevNode.parentNode;
|
||||
parentLookup.push(prevNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
if (!ctrl.head) {
|
||||
ctrl.head = messageNode;
|
||||
} else {
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
messageNode.next = match.next;
|
||||
match.next = messageNode;
|
||||
} else {
|
||||
messageNode.next = ctrl.head;
|
||||
ctrl.head = messageNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
function insertMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
if (!ctrl.head) {
|
||||
ctrl.head = messageNode;
|
||||
} else {
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
messageNode.next = match.next;
|
||||
match.next = messageNode;
|
||||
} else {
|
||||
messageNode.next = ctrl.head;
|
||||
ctrl.head = messageNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
function removeMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
match.next = messageNode.next;
|
||||
} else {
|
||||
ctrl.head = messageNode.next;
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
match.next = messageNode.next;
|
||||
} else {
|
||||
ctrl.head = messageNode.next;
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
function isAttrTruthy(scope, attr) {
|
||||
return (isString(attr) && attr.length === 0) || //empty attribute
|
||||
truthy(scope.$eval(attr));
|
||||
}
|
||||
function isAttrTruthy(scope, attr) {
|
||||
return (isString(attr) && attr.length === 0) || //empty attribute
|
||||
truthy(scope.$eval(attr));
|
||||
}
|
||||
|
||||
function truthy(val) {
|
||||
return isString(val) ? val.length : !!val;
|
||||
}
|
||||
}])
|
||||
function truthy(val) {
|
||||
return isString(val) ? val.length : !!val;
|
||||
}
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessagesInclude
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
|
||||
* code from a remote template and place the downloaded template code into the exact spot
|
||||
* that the ngMessagesInclude directive is placed within the ngMessages container. This allows
|
||||
* for a series of pre-defined messages to be reused and also allows for the developer to
|
||||
* determine what messages are overridden due to the placement of the ngMessagesInclude directive.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-messages-include="remoteTplString">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-messages-include src="expressionValue1">...</ng-messages-include>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
|
||||
*/
|
||||
.directive('ngMessagesInclude',
|
||||
['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessagesInclude
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
|
||||
* code from a remote template and place the downloaded template code into the exact spot
|
||||
* that the ngMessagesInclude directive is placed within the ngMessages container. This allows
|
||||
* for a series of pre-defined messages to be reused and also allows for the developer to
|
||||
* determine what messages are overridden due to the placement of the ngMessagesInclude directive.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-messages-include="remoteTplString">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-messages-include src="expressionValue1">...</ng-messages-include>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
|
||||
*/
|
||||
.directive('ngMessagesInclude',
|
||||
['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
|
||||
|
||||
return {
|
||||
restrict: 'AE',
|
||||
require: '^^ngMessages', // we only require this for validation sake
|
||||
link: function($scope, element, attrs) {
|
||||
var src = attrs.ngMessagesInclude || attrs.src;
|
||||
$templateRequest(src).then(function(html) {
|
||||
if ($scope.$$destroyed) return;
|
||||
return {
|
||||
restrict: 'AE',
|
||||
require: '^^ngMessages', // we only require this for validation sake
|
||||
link: function($scope, element, attrs) {
|
||||
var src = attrs.ngMessagesInclude || attrs.src;
|
||||
$templateRequest(src).then(function(html) {
|
||||
if ($scope.$$destroyed) return;
|
||||
|
||||
$compile(html)($scope, function(contents) {
|
||||
element.after(contents);
|
||||
if (isString(html) && !html.trim()) {
|
||||
// Empty template - nothing to compile
|
||||
replaceElementWithMarker(element, src);
|
||||
} else {
|
||||
// Non-empty template - compile and link
|
||||
$compile(html)($scope, function(contents) {
|
||||
element.after(contents);
|
||||
replaceElementWithMarker(element, src);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// the anchor is placed for debugging purposes
|
||||
var comment = $compile.$$createComment ?
|
||||
$compile.$$createComment('ngMessagesInclude', src) :
|
||||
$document[0].createComment(' ngMessagesInclude: ' + src + ' ');
|
||||
var anchor = jqLite(comment);
|
||||
element.after(anchor);
|
||||
// Helpers
|
||||
function replaceElementWithMarker(element, src) {
|
||||
// A comment marker is placed for debugging purposes
|
||||
var comment = $compile.$$createComment ?
|
||||
$compile.$$createComment('ngMessagesInclude', src) :
|
||||
$document[0].createComment(' ngMessagesInclude: ' + src + ' ');
|
||||
var marker = jqLite(comment);
|
||||
element.after(marker);
|
||||
|
||||
// we don't want to pollute the DOM anymore by keeping an empty directive element
|
||||
element.remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}])
|
||||
// Don't pollute the DOM anymore by keeping an empty directive element
|
||||
element.remove();
|
||||
}
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessage
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessage` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* More information about using `ngMessage` can be found in the
|
||||
* {@link module:ngMessages `ngMessages` module documentation}.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {expression} ngMessage|when a string value corresponding to the message key.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessage
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessage` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* More information about using `ngMessage` can be found in the
|
||||
* {@link module:ngMessages `ngMessages` module documentation}.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {expression} ngMessage|when a string value corresponding to the message key.
|
||||
*/
|
||||
.directive('ngMessage', ngMessageDirectiveFactory())
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessageExp
|
||||
* @restrict AE
|
||||
* @priority 1
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessageExp` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression">
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression">
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessageExp
|
||||
* @restrict AE
|
||||
* @priority 1
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessageExp` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression">
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression">
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
|
||||
*/
|
||||
.directive('ngMessageExp', ngMessageDirectiveFactory());
|
||||
|
||||
function ngMessageDirectiveFactory() {
|
||||
@@ -654,8 +664,8 @@ function ngMessageDirectiveFactory() {
|
||||
var assignRecords = function(items) {
|
||||
records = items
|
||||
? (isArray(items)
|
||||
? items
|
||||
: items.split(/[\s,]+/))
|
||||
? items
|
||||
: items.split(/[\s,]+/))
|
||||
: null;
|
||||
ngMessagesCtrl.reRender();
|
||||
};
|
||||
@@ -674,7 +684,7 @@ function ngMessageDirectiveFactory() {
|
||||
},
|
||||
attach: function() {
|
||||
if (!currentElement) {
|
||||
$transclude(scope, function(elm) {
|
||||
$transclude(function(elm, newScope) {
|
||||
$animate.enter(elm, null, element);
|
||||
currentElement = elm;
|
||||
|
||||
@@ -690,6 +700,7 @@ function ngMessageDirectiveFactory() {
|
||||
ngMessagesCtrl.deregister(commentNode);
|
||||
messageCtrl.detach();
|
||||
}
|
||||
newScope.$destroy();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Vendored
+10
-3
@@ -2194,9 +2194,15 @@ angular.mock.$RootElementProvider = function() {
|
||||
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
||||
return function(expression, locals, later, ident) {
|
||||
if (later && typeof later === 'object') {
|
||||
var create = $delegate(expression, locals, true, ident);
|
||||
angular.extend(create.instance, later);
|
||||
return create();
|
||||
var instantiate = $delegate(expression, locals, true, ident);
|
||||
angular.extend(instantiate.instance, later);
|
||||
|
||||
var instance = instantiate();
|
||||
if (instance !== instantiate.instance) {
|
||||
angular.extend(instance, later);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
return $delegate(expression, locals, later, ident);
|
||||
};
|
||||
@@ -2318,6 +2324,7 @@ angular.module('ngMock', ['ng']).provider({
|
||||
* the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
|
||||
*/
|
||||
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
$provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
|
||||
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
|
||||
}]);
|
||||
|
||||
|
||||
@@ -56,7 +56,21 @@ function shallowClearAndCopy(src, dst) {
|
||||
*
|
||||
* <div doc-module-components="ngResource"></div>
|
||||
*
|
||||
* See {@link ngResource.$resource `$resource`} for usage.
|
||||
* See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $resourceProvider
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
|
||||
* service.
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngResource } module to be installed.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -251,6 +265,14 @@ function shallowClearAndCopy(src, dst) {
|
||||
* - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
|
||||
* collection, calling this method will abort the request.
|
||||
*
|
||||
* The Resource instances have these additional methods:
|
||||
*
|
||||
* - `toJSON`: It returns a simple object without any of the extra properties added as part of
|
||||
* the Resource API. This object can be serialized through {@link angular.toJson} safely
|
||||
* without attaching Angular-specific fields. Notice that `JSON.stringify` (and
|
||||
* `angular.toJson`) automatically use this method when serializing a Resource instance
|
||||
* (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Credit card resource
|
||||
@@ -402,10 +424,78 @@ angular.module('ngResource', ['ng']).
|
||||
var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
|
||||
var provider = this;
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $resourceProvider#defaults
|
||||
* @description
|
||||
* Object containing default options used when creating `$resource` instances.
|
||||
*
|
||||
* The default values satisfy a wide range of usecases, but you may choose to overwrite any of
|
||||
* them to further customize your instances. The available properties are:
|
||||
*
|
||||
* - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any
|
||||
* calculated URL will be stripped.<br />
|
||||
* (Defaults to true.)
|
||||
* - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be
|
||||
* cancelled (if not already completed) by calling `$cancelRequest()` on the call's return
|
||||
* value. For more details, see {@link ngResource.$resource}. This can be overwritten per
|
||||
* resource class or action.<br />
|
||||
* (Defaults to false.)
|
||||
* - **actions** - `{Object.<Object>}` - A hash with default actions declarations. Actions are
|
||||
* high-level methods corresponding to RESTful actions/methods on resources. An action may
|
||||
* specify what HTTP method to use, what URL to hit, if the return value will be a single
|
||||
* object or a collection (array) of objects etc. For more details, see
|
||||
* {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource
|
||||
* class.<br />
|
||||
* The default actions are:
|
||||
* ```js
|
||||
* {
|
||||
* get: {method: 'GET'},
|
||||
* save: {method: 'POST'},
|
||||
* query: {method: 'GET', isArray: true},
|
||||
* remove: {method: 'DELETE'},
|
||||
* delete: {method: 'DELETE'}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* #### Example
|
||||
*
|
||||
* For example, you can specify a new `update` action that uses the `PUT` HTTP verb:
|
||||
*
|
||||
* ```js
|
||||
* angular.
|
||||
* module('myApp').
|
||||
* config(['resourceProvider', function ($resourceProvider) {
|
||||
* $resourceProvider.defaults.actions.update = {
|
||||
* method: 'PUT'
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Or you can even overwrite the whole `actions` list and specify your own:
|
||||
*
|
||||
* ```js
|
||||
* angular.
|
||||
* module('myApp').
|
||||
* config(['resourceProvider', function ($resourceProvider) {
|
||||
* $resourceProvider.defaults.actions = {
|
||||
* create: {method: 'POST'}
|
||||
* get: {method: 'GET'},
|
||||
* getAll: {method: 'GET', isArray:true},
|
||||
* update: {method: 'PUT'},
|
||||
* delete: {method: 'DELETE'}
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
this.defaults = {
|
||||
// Strip slashes by default
|
||||
stripTrailingSlashes: true,
|
||||
|
||||
// Make non-instance requests cancellable (via `$cancelRequest()`)
|
||||
cancellable: false,
|
||||
|
||||
// Default actions configuration
|
||||
actions: {
|
||||
'get': {method: 'GET'},
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
/* global shallowCopy: false */
|
||||
|
||||
// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`)
|
||||
var isArray = angular.isArray;
|
||||
var isObject = angular.isObject;
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngRoute
|
||||
@@ -154,7 +160,7 @@ function $RouteProvider() {
|
||||
*/
|
||||
this.when = function(path, route) {
|
||||
//copy original route object to preserve params inherited from proto chain
|
||||
var routeCopy = angular.copy(route);
|
||||
var routeCopy = shallowCopy(route);
|
||||
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
|
||||
routeCopy.reloadOnSearch = true;
|
||||
}
|
||||
|
||||
@@ -141,6 +141,11 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
if (text == null || text === '') return text;
|
||||
if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
|
||||
|
||||
var attributesFn =
|
||||
angular.isFunction(attributes) ? attributes :
|
||||
angular.isObject(attributes) ? function getAttributesObject() {return attributes;} :
|
||||
function getEmptyAttributesObject() {return {};};
|
||||
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
@@ -169,19 +174,14 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
var key;
|
||||
var key, linkAttributes = attributesFn(url);
|
||||
html.push('<a ');
|
||||
if (angular.isFunction(attributes)) {
|
||||
attributes = attributes(url);
|
||||
|
||||
for (key in linkAttributes) {
|
||||
html.push(key + '="' + linkAttributes[key] + '" ');
|
||||
}
|
||||
if (angular.isObject(attributes)) {
|
||||
for (key in attributes) {
|
||||
html.push(key + '="' + attributes[key] + '" ');
|
||||
}
|
||||
} else {
|
||||
attributes = {};
|
||||
}
|
||||
if (angular.isDefined(target) && !('target' in attributes)) {
|
||||
|
||||
if (angular.isDefined(target) && !('target' in linkAttributes)) {
|
||||
html.push('target="',
|
||||
target,
|
||||
'" ');
|
||||
|
||||
@@ -80,6 +80,17 @@
|
||||
}
|
||||
} else if (/touch/.test(eventType) && supportsTouchEvents()) {
|
||||
evnt = createTouchEvent(element, eventType, x, y);
|
||||
} else if (/key/.test(eventType)) {
|
||||
evnt = window.document.createEvent('Events');
|
||||
evnt.initEvent(eventType, eventData.bubbles, eventData.cancelable);
|
||||
evnt.view = window;
|
||||
evnt.ctrlKey = pressed('ctrl');
|
||||
evnt.altKey = pressed('alt');
|
||||
evnt.shiftKey = pressed('shift');
|
||||
evnt.metaKey = pressed('meta');
|
||||
evnt.keyCode = eventData.keyCode;
|
||||
evnt.charCode = eventData.charCode;
|
||||
evnt.which = eventData.which;
|
||||
} else {
|
||||
evnt = window.document.createEvent('MouseEvents');
|
||||
x = x || 0;
|
||||
@@ -108,7 +119,12 @@
|
||||
return originalPreventDefault.apply(evnt, arguments);
|
||||
};
|
||||
|
||||
element.dispatchEvent(evnt);
|
||||
if (!eventData.bubbles || supportsEventBubblingInDetachedTree() || isAttachedToDocument(element)) {
|
||||
element.dispatchEvent(evnt);
|
||||
} else {
|
||||
triggerForPath(element, evnt);
|
||||
}
|
||||
|
||||
finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
|
||||
|
||||
delete angular['ff-684208-preventDefault'];
|
||||
@@ -146,4 +162,52 @@
|
||||
|
||||
return evnt;
|
||||
}
|
||||
|
||||
function supportsEventBubblingInDetachedTree() {
|
||||
if ('_cached' in supportsEventBubblingInDetachedTree) {
|
||||
return supportsEventBubblingInDetachedTree._cached;
|
||||
}
|
||||
supportsEventBubblingInDetachedTree._cached = false;
|
||||
var doc = window.document;
|
||||
if (doc) {
|
||||
var parent = doc.createElement('div'),
|
||||
child = parent.cloneNode();
|
||||
parent.appendChild(child);
|
||||
parent.addEventListener('e', function() {
|
||||
supportsEventBubblingInDetachedTree._cached = true;
|
||||
});
|
||||
var evnt = window.document.createEvent('Events');
|
||||
evnt.initEvent('e', true, true);
|
||||
child.dispatchEvent(evnt);
|
||||
}
|
||||
return supportsEventBubblingInDetachedTree._cached;
|
||||
}
|
||||
|
||||
function triggerForPath(element, evnt) {
|
||||
var stop = false;
|
||||
|
||||
var _stopPropagation = evnt.stopPropagation;
|
||||
evnt.stopPropagation = function() {
|
||||
stop = true;
|
||||
_stopPropagation.apply(evnt, arguments);
|
||||
};
|
||||
patchEventTargetForBubbling(evnt, element);
|
||||
do {
|
||||
element.dispatchEvent(evnt);
|
||||
} while (!stop && (element = element.parentNode));
|
||||
}
|
||||
|
||||
function patchEventTargetForBubbling(event, target) {
|
||||
event._target = target;
|
||||
Object.defineProperty(event, "target", {get: function() { return this._target;}});
|
||||
}
|
||||
|
||||
function isAttachedToDocument(element) {
|
||||
while (element = element.parentNode) {
|
||||
if (element === window) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}());
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
/* global shallowCopy: true */
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of an object, an array or a primitive.
|
||||
*
|
||||
* Assumes that there are no proto properties for objects.
|
||||
*/
|
||||
function shallowCopy(src, dst) {
|
||||
if (isArray(src)) {
|
||||
dst = dst || [];
|
||||
|
||||
for (var i = 0, ii = src.length; i < ii; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
} else if (isObject(src)) {
|
||||
dst = dst || {};
|
||||
|
||||
for (var key in src) {
|
||||
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dst || src;
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/* global: toDebugString: true */
|
||||
/* global toDebugString: true */
|
||||
|
||||
function serializeObject(obj) {
|
||||
var seen = [];
|
||||
|
||||
@@ -233,6 +233,11 @@ beforeEach(function() {
|
||||
compare: generateCompare(false),
|
||||
negativeCompare: generateCompare(true)
|
||||
};
|
||||
function hasClass(element, selector) {
|
||||
if (!element.getAttribute) return false;
|
||||
return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
|
||||
indexOf(" " + selector + " ") > -1);
|
||||
}
|
||||
function generateCompare(isNot) {
|
||||
return function(actual, clazz) {
|
||||
var message = function() {
|
||||
@@ -240,7 +245,7 @@ beforeEach(function() {
|
||||
};
|
||||
var classes = clazz.trim().split(/\s+/);
|
||||
for (var i = 0; i < classes.length; ++i) {
|
||||
if (!jqLiteHasClass(actual[0], classes[i])) {
|
||||
if (!hasClass(actual[0], classes[i])) {
|
||||
return { pass: isNot };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -979,6 +979,29 @@ describe('$compile', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should not set merged attributes twice in $attrs', function() {
|
||||
var attrs;
|
||||
|
||||
module(function() {
|
||||
directive('logAttrs', function() {
|
||||
return {
|
||||
link: function($scope, $element, $attrs) {
|
||||
attrs = $attrs;
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile(
|
||||
'<div><div log-attrs replace class="myLog"></div><div>')($rootScope);
|
||||
var div = element.find('div');
|
||||
expect(div.attr('class')).toBe('myLog log');
|
||||
expect(attrs.class).toBe('myLog log');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should prevent multiple templates per element', inject(function($compile) {
|
||||
try {
|
||||
$compile('<div><span replace class="replace"></span></div>');
|
||||
@@ -3650,6 +3673,46 @@ describe('$compile', function() {
|
||||
expect(Controller2.prototype.$onInit).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
it('should continue to trigger other `$onInit` hooks if one throws an error', function() {
|
||||
function ThrowingController() {
|
||||
this.$onInit = function() {
|
||||
throw new Error('bad hook');
|
||||
};
|
||||
}
|
||||
function LoggingController($log) {
|
||||
this.$onInit = function() {
|
||||
$log.info('onInit');
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('my', [])
|
||||
.component('c1', {
|
||||
controller: ThrowingController,
|
||||
bindings: {'prop': '<'}
|
||||
})
|
||||
.component('c2', {
|
||||
controller: LoggingController,
|
||||
bindings: {'prop': '<'}
|
||||
})
|
||||
.config(function($exceptionHandlerProvider) {
|
||||
// We need to test with the exceptionHandler not rethrowing...
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
});
|
||||
|
||||
module('my');
|
||||
inject(function($compile, $rootScope, $exceptionHandler, $log) {
|
||||
|
||||
// Setup the directive with bindings that will keep updating the bound value forever
|
||||
element = $compile('<div><c1 prop="a"></c1><c2 prop="a"></c2>')($rootScope);
|
||||
|
||||
// The first component's error should be logged
|
||||
expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook'));
|
||||
|
||||
// The second component's hook should still be called
|
||||
expect($log.info.logs.pop()).toEqual(['onInit']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4121,6 +4184,93 @@ describe('$compile', function() {
|
||||
expect($exceptionHandler.errors[0].toString()).toContain('[$compile:infchng] 10 $onChanges() iterations reached.');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should continue to trigger other `$onChanges` hooks if one throws an error', function() {
|
||||
function ThrowingController() {
|
||||
this.$onChanges = function(change) {
|
||||
throw new Error('bad hook');
|
||||
};
|
||||
}
|
||||
function LoggingController($log) {
|
||||
this.$onChanges = function(change) {
|
||||
$log.info('onChange');
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('my', [])
|
||||
.component('c1', {
|
||||
controller: ThrowingController,
|
||||
bindings: {'prop': '<'}
|
||||
})
|
||||
.component('c2', {
|
||||
controller: LoggingController,
|
||||
bindings: {'prop': '<'}
|
||||
})
|
||||
.config(function($exceptionHandlerProvider) {
|
||||
// We need to test with the exceptionHandler not rethrowing...
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
});
|
||||
|
||||
module('my');
|
||||
inject(function($compile, $rootScope, $exceptionHandler, $log) {
|
||||
|
||||
// Setup the directive with bindings that will keep updating the bound value forever
|
||||
element = $compile('<div><c1 prop="a"></c1><c2 prop="a"></c2>')($rootScope);
|
||||
|
||||
// The first component's error should be logged
|
||||
expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook'));
|
||||
|
||||
// The second component's changes should still be called
|
||||
expect($log.info.logs.pop()).toEqual(['onChange']);
|
||||
|
||||
$rootScope.$apply('a = 42');
|
||||
|
||||
// The first component's error should be logged
|
||||
var errors = $exceptionHandler.errors.pop();
|
||||
expect(errors[0]).toEqual(new Error('bad hook'));
|
||||
|
||||
// The second component's changes should still be called
|
||||
expect($log.info.logs.pop()).toEqual(['onChange']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should collect up all `$onChanges` errors into one throw', function() {
|
||||
function ThrowingController() {
|
||||
this.$onChanges = function(change) {
|
||||
throw new Error('bad hook: ' + this.prop);
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('my', [])
|
||||
.component('c1', {
|
||||
controller: ThrowingController,
|
||||
bindings: {'prop': '<'}
|
||||
})
|
||||
.config(function($exceptionHandlerProvider) {
|
||||
// We need to test with the exceptionHandler not rethrowing...
|
||||
$exceptionHandlerProvider.mode('log');
|
||||
});
|
||||
|
||||
module('my');
|
||||
inject(function($compile, $rootScope, $exceptionHandler, $log) {
|
||||
|
||||
// Setup the directive with bindings that will keep updating the bound value forever
|
||||
element = $compile('<div><c1 prop="a"></c1><c1 prop="a * 2"></c1>')($rootScope);
|
||||
|
||||
// Both component's errors should be logged
|
||||
expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook: NaN'));
|
||||
expect($exceptionHandler.errors.pop()).toEqual(new Error('bad hook: undefined'));
|
||||
|
||||
$rootScope.$apply('a = 42');
|
||||
|
||||
// Both component's error should be logged
|
||||
var errors = $exceptionHandler.errors.pop();
|
||||
expect(errors.pop()).toEqual(new Error('bad hook: 84'));
|
||||
expect(errors.pop()).toEqual(new Error('bad hook: 42'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2784,14 +2784,103 @@ describe('input', function() {
|
||||
describe('EMAIL_REGEXP', function() {
|
||||
/* global EMAIL_REGEXP: false */
|
||||
it('should validate email', function() {
|
||||
/* basic functionality */
|
||||
expect(EMAIL_REGEXP.test('a@b.com')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@b.museum')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@B.c')).toBe(true);
|
||||
/* domain label separation, hyphen-minus, syntax */
|
||||
expect(EMAIL_REGEXP.test('a@b.c.')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@.b.c')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@-b.c')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@b-.c')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@b-c')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@-')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@.')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@host_name')).toBe(false);
|
||||
/* leading or sole digit */
|
||||
expect(EMAIL_REGEXP.test('a@3b.c')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@3')).toBe(true);
|
||||
/* TLD eMail address */
|
||||
expect(EMAIL_REGEXP.test('a@b')).toBe(true);
|
||||
/* domain valid characters */
|
||||
expect(EMAIL_REGEXP.test('a@abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789')).toBe(true);
|
||||
/* domain invalid characters */
|
||||
expect(EMAIL_REGEXP.test('a@')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@ ')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@!')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@"')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@#')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@$')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@%')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@&')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test("a@'")).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@(')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@)')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@*')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@+')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@,')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@/')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@:')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@;')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@<')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@=')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@>')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@?')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@@')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@[')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@\\')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@]')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@^')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@_')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@`')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@{')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@|')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@}')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@~')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@İ')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a@ı')).toBe(false);
|
||||
/* domain length, label and total */
|
||||
expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')).toBe(false);
|
||||
/* jshint maxlen:320 */
|
||||
expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.x')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xx')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xx')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxx')).toBe(false);
|
||||
/* jshint maxlen:200 */
|
||||
/* local-part valid characters and dot-atom syntax */
|
||||
expect(EMAIL_REGEXP.test("'@x")).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('-!#$%&*+/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ@x')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('^_`abcdefghijklmnopqrstuvwxyz{|}~@x')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test(".@x")).toBe(false);
|
||||
expect(EMAIL_REGEXP.test("'.@x")).toBe(false);
|
||||
expect(EMAIL_REGEXP.test(".'@x")).toBe(false);
|
||||
expect(EMAIL_REGEXP.test("'.'@x")).toBe(true);
|
||||
/* local-part invalid characters */
|
||||
expect(EMAIL_REGEXP.test('@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test(' @x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('"@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('(@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test(')@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test(',@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test(':@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test(';@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('<@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('>@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('@@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('[@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('\\@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test(']@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('İ@x')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('ı@x')).toBe(false);
|
||||
/* local-part size limit */
|
||||
expect(EMAIL_REGEXP.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@x')).toBe(true);
|
||||
expect(EMAIL_REGEXP.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@x')).toBe(false);
|
||||
/* content (local-part + ‘@’ + domain) is required */
|
||||
expect(EMAIL_REGEXP.test('')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('a')).toBe(false);
|
||||
expect(EMAIL_REGEXP.test('aa')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,12 +4,26 @@ describe('Filter: limitTo', function() {
|
||||
var items;
|
||||
var str;
|
||||
var number;
|
||||
var arrayLike;
|
||||
var limitTo;
|
||||
|
||||
beforeEach(inject(function($filter) {
|
||||
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
|
||||
str = "tuvwxyz";
|
||||
number = 100.045;
|
||||
arrayLike = {
|
||||
0: 'a',
|
||||
1: 'b',
|
||||
2: 'c',
|
||||
3: 'd',
|
||||
4: 'e',
|
||||
5: 'f',
|
||||
6: 'g',
|
||||
7: 'h',
|
||||
get length() {
|
||||
return Object.keys(this).length - 1;
|
||||
}
|
||||
};
|
||||
limitTo = $filter('limitTo');
|
||||
}));
|
||||
|
||||
@@ -21,6 +35,8 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(str, '3')).toEqual("tuv");
|
||||
expect(limitTo(number, 3)).toEqual("100");
|
||||
expect(limitTo(number, '3')).toEqual("100");
|
||||
expect(limitTo(arrayLike, 3)).toEqual(['a', 'b', 'c']);
|
||||
expect(limitTo(arrayLike, '3')).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
it('should return the first X items beginning from index Y when X and Y are positive', function() {
|
||||
@@ -28,6 +44,8 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, '3', 3)).toEqual(['d', 'e', 'f']);
|
||||
expect(limitTo(str, 3, 3)).toEqual("wxy");
|
||||
expect(limitTo(str, '3', '3')).toEqual("wxy");
|
||||
expect(limitTo(arrayLike, 3, 3)).toEqual(['d', 'e', 'f']);
|
||||
expect(limitTo(arrayLike, '3', '3')).toEqual(['d', 'e', 'f']);
|
||||
});
|
||||
|
||||
it('should return the first X items beginning from index Y when X is positive and Y is negative', function() {
|
||||
@@ -35,6 +53,8 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, '3', -3)).toEqual(['f', 'g', 'h']);
|
||||
expect(limitTo(str, 3, -3)).toEqual("xyz");
|
||||
expect(limitTo(str, '3', '-3')).toEqual("xyz");
|
||||
expect(limitTo(arrayLike, 3, '-3')).toEqual(['f', 'g', 'h']);
|
||||
expect(limitTo(arrayLike, '3', -3)).toEqual(['f', 'g', 'h']);
|
||||
});
|
||||
|
||||
it('should return the last X items when X is negative', function() {
|
||||
@@ -44,6 +64,8 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(str, '-3')).toEqual("xyz");
|
||||
expect(limitTo(number, -3)).toEqual("045");
|
||||
expect(limitTo(number, '-3')).toEqual("045");
|
||||
expect(limitTo(arrayLike, -3)).toEqual(['f', 'g', 'h']);
|
||||
expect(limitTo(arrayLike, '-3')).toEqual(['f', 'g', 'h']);
|
||||
});
|
||||
|
||||
it('should return the last X items until index Y when X and Y are negative', function() {
|
||||
@@ -51,6 +73,8 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, '-3', -3)).toEqual(['c', 'd', 'e']);
|
||||
expect(limitTo(str, -3, -3)).toEqual("uvw");
|
||||
expect(limitTo(str, '-3', '-3')).toEqual("uvw");
|
||||
expect(limitTo(arrayLike, -3, '-3')).toEqual(['c', 'd', 'e']);
|
||||
expect(limitTo(arrayLike, '-3', -3)).toEqual(['c', 'd', 'e']);
|
||||
});
|
||||
|
||||
it('should return the last X items until index Y when X is negative and Y is positive', function() {
|
||||
@@ -58,19 +82,28 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, '-3', 4)).toEqual(['b', 'c', 'd']);
|
||||
expect(limitTo(str, -3, 4)).toEqual("uvw");
|
||||
expect(limitTo(str, '-3', '4')).toEqual("uvw");
|
||||
expect(limitTo(arrayLike, -3, '4')).toEqual(['b', 'c', 'd']);
|
||||
expect(limitTo(arrayLike, '-3', 4)).toEqual(['b', 'c', 'd']);
|
||||
});
|
||||
|
||||
it('should return an empty array when X = 0', function() {
|
||||
expect(limitTo(items, 0)).toEqual([]);
|
||||
expect(limitTo(items, '0')).toEqual([]);
|
||||
expect(limitTo(arrayLike, 0)).toEqual([]);
|
||||
expect(limitTo(arrayLike, '0')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return entire array when X cannot be parsed', function() {
|
||||
expect(limitTo(items, 'bogus')).toEqual(items);
|
||||
expect(limitTo(items, 'null')).toEqual(items);
|
||||
expect(limitTo(items, 'undefined')).toEqual(items);
|
||||
expect(limitTo(items, null)).toEqual(items);
|
||||
expect(limitTo(items, undefined)).toEqual(items);
|
||||
expect(limitTo(items, 'bogus')).toBe(items);
|
||||
expect(limitTo(items, 'null')).toBe(items);
|
||||
expect(limitTo(items, 'undefined')).toBe(items);
|
||||
expect(limitTo(items, null)).toBe(items);
|
||||
expect(limitTo(items, undefined)).toBe(items);
|
||||
expect(limitTo(arrayLike, 'bogus')).toBe(arrayLike);
|
||||
expect(limitTo(arrayLike, 'null')).toBe(arrayLike);
|
||||
expect(limitTo(arrayLike, 'undefined')).toBe(arrayLike);
|
||||
expect(limitTo(arrayLike, null)).toBe(arrayLike);
|
||||
expect(limitTo(arrayLike, undefined)).toBe(arrayLike);
|
||||
});
|
||||
|
||||
it('should return an empty string when X = 0', function() {
|
||||
@@ -97,9 +130,14 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(str, '3', 'undefined')).toEqual(limitTo(str, '3'));
|
||||
expect(limitTo(str, '-3', null)).toEqual(limitTo(str, '-3', 0));
|
||||
expect(limitTo(str, 3, undefined)).toEqual(limitTo(str, 3));
|
||||
expect(limitTo(arrayLike, 3, 'bogus')).toEqual(limitTo(arrayLike, 3, 0));
|
||||
expect(limitTo(arrayLike, -3, 'null')).toEqual(limitTo(arrayLike, -3));
|
||||
expect(limitTo(arrayLike, '3', 'undefined')).toEqual(limitTo(arrayLike, '3', 0));
|
||||
expect(limitTo(arrayLike, '-3', null)).toEqual(limitTo(arrayLike, '-3'));
|
||||
expect(limitTo(arrayLike, 3, undefined)).toEqual(limitTo(arrayLike, 3, 0));
|
||||
});
|
||||
|
||||
it('should return input if not String or Array or Number', function() {
|
||||
it('should return input if not array-like or Number', function() {
|
||||
expect(limitTo(null, 1)).toEqual(null);
|
||||
expect(limitTo(undefined, 1)).toEqual(undefined);
|
||||
expect(limitTo({}, 1)).toEqual({});
|
||||
@@ -111,8 +149,13 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, '9')).toEqual(items);
|
||||
expect(limitTo(items, -9)).toEqual(items);
|
||||
expect(limitTo(items, '-9')).toEqual(items);
|
||||
expect(limitTo(arrayLike, 9)).toEqual(items);
|
||||
expect(limitTo(arrayLike, '9')).toEqual(items);
|
||||
expect(limitTo(arrayLike, -9)).toEqual(items);
|
||||
expect(limitTo(arrayLike, '-9')).toEqual(items);
|
||||
|
||||
expect(limitTo(items, 9)).not.toBe(items);
|
||||
expect(limitTo(arrayLike, 9)).not.toBe(arrayLike);
|
||||
});
|
||||
|
||||
it('should return the entire string if X exceeds input length', function() {
|
||||
@@ -129,6 +172,10 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, 'Infinity')).toEqual(items);
|
||||
expect(limitTo(items, -Infinity)).toEqual(items);
|
||||
expect(limitTo(items, '-Infinity')).toEqual(items);
|
||||
expect(limitTo(arrayLike, Infinity)).toEqual(items);
|
||||
expect(limitTo(arrayLike, 'Infinity')).toEqual(items);
|
||||
expect(limitTo(arrayLike, -Infinity)).toEqual(items);
|
||||
expect(limitTo(arrayLike, '-Infinity')).toEqual(items);
|
||||
});
|
||||
|
||||
it('should return the entire string when limited by Infinity', function() {
|
||||
@@ -141,6 +188,8 @@ describe('Filter: limitTo', function() {
|
||||
it('should return an empty array if Y exceeds input length', function() {
|
||||
expect(limitTo(items, '3', 12)).toEqual([]);
|
||||
expect(limitTo(items, -3, '12')).toEqual([]);
|
||||
expect(limitTo(arrayLike, '3', 12)).toEqual([]);
|
||||
expect(limitTo(arrayLike, -3, '12')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty string if Y exceeds input length', function() {
|
||||
@@ -153,6 +202,8 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, '-4', -12)).toEqual(['e', 'f', 'g', 'h']);
|
||||
expect(limitTo(str, 4, '-12')).toEqual("tuvw");
|
||||
expect(limitTo(str, '-4', -12)).toEqual("wxyz");
|
||||
expect(limitTo(arrayLike, 4, '-12')).toEqual(['a', 'b', 'c', 'd']);
|
||||
expect(limitTo(arrayLike, '-4', -12)).toEqual(['e', 'f', 'g', 'h']);
|
||||
});
|
||||
|
||||
it('should return the entire string beginning from Y if X is positive and X+Y exceeds input length', function() {
|
||||
@@ -160,6 +211,8 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, 7, -3)).toEqual(['f', 'g', 'h']);
|
||||
expect(limitTo(str, 6, 3)).toEqual("wxyz");
|
||||
expect(limitTo(str, 6, -3)).toEqual("xyz");
|
||||
expect(limitTo(arrayLike, 7, 3)).toEqual(['d', 'e', 'f', 'g', 'h']);
|
||||
expect(limitTo(arrayLike, 7, -3)).toEqual(['f', 'g', 'h']);
|
||||
});
|
||||
|
||||
it('should return the entire string until index Y if X is negative and X+Y exceeds input length', function() {
|
||||
@@ -167,5 +220,20 @@ describe('Filter: limitTo', function() {
|
||||
expect(limitTo(items, -7, -3)).toEqual(['a', 'b', 'c', 'd', 'e']);
|
||||
expect(limitTo(str, -6, 3)).toEqual("tuv");
|
||||
expect(limitTo(str, -6, -3)).toEqual("tuvw");
|
||||
expect(limitTo(arrayLike, -7, 3)).toEqual(['a', 'b', 'c']);
|
||||
expect(limitTo(arrayLike, -7, -3)).toEqual(['a', 'b', 'c', 'd', 'e']);
|
||||
});
|
||||
|
||||
it('should not throw an error if used with an array like object', function() {
|
||||
function getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
var argsObj = getArguments({name: 'Misko'}, {name: 'Igor'}, {name: 'Brad'});
|
||||
|
||||
var nodeList = jqLite("<p><span>Misko</span><span>Igor</span><span>Brad</span></p>")[0].childNodes;
|
||||
|
||||
expect(limitTo(argsObj, 2).length).toBe(2);
|
||||
expect(limitTo('abc', 1).length).toBe(1);
|
||||
expect(limitTo(nodeList, 2).length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
+344
-23
@@ -13,15 +13,18 @@ describe('Filter: orderBy', function() {
|
||||
toThrowMinErr('orderBy', 'notarray', 'Expected array but received: {}');
|
||||
});
|
||||
|
||||
|
||||
it('should not throw an exception if a null or undefined value is provided', function() {
|
||||
expect(orderBy(null)).toEqual(null);
|
||||
expect(orderBy(undefined)).toEqual(undefined);
|
||||
});
|
||||
|
||||
|
||||
it('should not throw an exception if an array-like object is provided', function() {
|
||||
expect(orderBy('cba')).toEqual(['a', 'b', 'c']);
|
||||
});
|
||||
|
||||
|
||||
it('should return sorted array if predicate is not provided', function() {
|
||||
expect(orderBy([2, 1, 3])).toEqual([1, 2, 3]);
|
||||
|
||||
@@ -37,13 +40,6 @@ describe('Filter: orderBy', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should reverse collection if `reverseOrder` param is truthy', function() {
|
||||
expect(orderBy([{a:15}, {a:2}], 'a', true)).toEqualData([{a:15}, {a:2}]);
|
||||
expect(orderBy([{a:15}, {a:2}], 'a', "T")).toEqualData([{a:15}, {a:2}]);
|
||||
expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);
|
||||
});
|
||||
|
||||
|
||||
it('should sort inherited from array', function() {
|
||||
function BaseCollection() {}
|
||||
BaseCollection.prototype = Array.prototype;
|
||||
@@ -93,6 +89,7 @@ describe('Filter: orderBy', function() {
|
||||
{ a:new Date('01/01/2014'), b:3 }]);
|
||||
});
|
||||
|
||||
|
||||
it('should compare timestamps when sorting dates', function() {
|
||||
expect(orderBy([
|
||||
new Date('01/01/2015'),
|
||||
@@ -140,22 +137,6 @@ describe('Filter: orderBy', function() {
|
||||
expect(orderBy(array)).toEqualData(array);
|
||||
});
|
||||
|
||||
it('should reverse array of objects with no predicate and reverse is `true`', function() {
|
||||
var array = [
|
||||
{ id: 2 },
|
||||
{ id: 1 },
|
||||
{ id: 4 },
|
||||
{ id: 3 }
|
||||
];
|
||||
var reversedArray = [
|
||||
{ id: 3 },
|
||||
{ id: 4 },
|
||||
{ id: 1 },
|
||||
{ id: 2 }
|
||||
];
|
||||
expect(orderBy(array, '', true)).toEqualData(reversedArray);
|
||||
});
|
||||
|
||||
|
||||
it('should reverse array of objects with predicate of "-"', function() {
|
||||
var array = [
|
||||
@@ -237,6 +218,346 @@ describe('Filter: orderBy', function() {
|
||||
{foo: 1, bar: 8}, {foo: 1, bar: 5}, {foo: 1, bar: 2}
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
describe('(reversing order)', function() {
|
||||
it('should not reverse collection if `reverse` param is falsy',
|
||||
function() {
|
||||
var items = [{a: 2}, {a: 15}];
|
||||
var expr = 'a';
|
||||
var sorted = [{a: 2}, {a: 15}];
|
||||
|
||||
expect(orderBy(items, expr, false)).toEqual(sorted);
|
||||
expect(orderBy(items, expr, 0)).toEqual(sorted);
|
||||
expect(orderBy(items, expr, '')).toEqual(sorted);
|
||||
expect(orderBy(items, expr, NaN)).toEqual(sorted);
|
||||
expect(orderBy(items, expr, null)).toEqual(sorted);
|
||||
expect(orderBy(items, expr, undefined)).toEqual(sorted);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should reverse collection if `reverse` param is truthy',
|
||||
function() {
|
||||
var items = [{a: 2}, {a: 15}];
|
||||
var expr = 'a';
|
||||
var sorted = [{a: 15}, {a: 2}];
|
||||
|
||||
expect(orderBy(items, expr, true)).toEqual(sorted);
|
||||
expect(orderBy(items, expr, 1)).toEqual(sorted);
|
||||
expect(orderBy(items, expr, 'reverse')).toEqual(sorted);
|
||||
expect(orderBy(items, expr, {})).toEqual(sorted);
|
||||
expect(orderBy(items, expr, [])).toEqual(sorted);
|
||||
expect(orderBy(items, expr, noop)).toEqual(sorted);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should reverse collection if `reverse` param is `true`, even without an `expression`',
|
||||
function() {
|
||||
var originalItems = [{id: 2}, {id: 1}, {id: 4}, {id: 3}];
|
||||
var reversedItems = [{id: 3}, {id: 4}, {id: 1}, {id: 2}];
|
||||
expect(orderBy(originalItems, null, true)).toEqual(reversedItems);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
describe('(built-in comparator)', function() {
|
||||
it('should compare numbers numarically', function() {
|
||||
var items = [100, 3, 20];
|
||||
var expr = null;
|
||||
var sorted = [3, 20, 100];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should compare strings alphabetically', function() {
|
||||
var items = ['100', '3', '20', '_b', 'a'];
|
||||
var expr = null;
|
||||
var sorted = ['100', '20', '3', '_b', 'a'];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should compare strings case-insensitively', function() {
|
||||
var items = ['c', 'B', 'a'];
|
||||
var expr = null;
|
||||
var sorted = ['a', 'B', 'c'];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should compare objects based on `index`', function() {
|
||||
var items = [{c: 3}, {b: 2}, {a: 1}];
|
||||
var expr = null;
|
||||
var sorted = [{c: 3}, {b: 2}, {a: 1}];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should compare values of different type alphabetically by type', function() {
|
||||
var items = [undefined, '1', {}, 999, noop, false];
|
||||
var expr = null;
|
||||
var sorted = [false, noop, 999, {}, '1', undefined];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
});
|
||||
});
|
||||
|
||||
describe('(custom comparator)', function() {
|
||||
it('should support a custom comparator', function() {
|
||||
var items = [4, 42, 2];
|
||||
var expr = null;
|
||||
var reverse = null;
|
||||
var sorted = [42, 2, 4];
|
||||
|
||||
var comparator = function(o1, o2) {
|
||||
var v1 = o1.value;
|
||||
var v2 = o2.value;
|
||||
|
||||
// 42 always comes first
|
||||
if (v1 === v2) return 0;
|
||||
if (v1 === 42) return -1;
|
||||
if (v2 === 42) return 1;
|
||||
|
||||
// Default comparison for other values
|
||||
return (v1 < v2) ? -1 : 1;
|
||||
};
|
||||
|
||||
expect(orderBy(items, expr, reverse, comparator)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should support `reverseOrder` with a custom comparator', function() {
|
||||
var items = [4, 42, 2];
|
||||
var expr = null;
|
||||
var reverse = true;
|
||||
var sorted = [4, 2, 42];
|
||||
|
||||
var comparator = function(o1, o2) {
|
||||
var v1 = o1.value;
|
||||
var v2 = o2.value;
|
||||
|
||||
// 42 always comes first
|
||||
if (v1 === v2) return 0;
|
||||
if (v1 === 42) return -1;
|
||||
if (v2 === 42) return 1;
|
||||
|
||||
// Default comparison for other values
|
||||
return (v1 < v2) ? -1 : 1;
|
||||
};
|
||||
|
||||
expect(orderBy(items, expr, reverse, comparator)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should pass `{value, type, index}` objects to comparators', function() {
|
||||
var items = [false, noop, 999, {}, '', undefined];
|
||||
var expr = null;
|
||||
var reverse = null;
|
||||
var comparator = jasmine.createSpy('comparator').and.returnValue(-1);
|
||||
|
||||
orderBy(items, expr, reverse, comparator);
|
||||
var allArgsFlat = Array.prototype.concat.apply([], comparator.calls.allArgs());
|
||||
|
||||
expect(allArgsFlat).toContain({index: 0, type: 'boolean', value: false });
|
||||
expect(allArgsFlat).toContain({index: 1, type: 'function', value: noop });
|
||||
expect(allArgsFlat).toContain({index: 2, type: 'number', value: 999 });
|
||||
expect(allArgsFlat).toContain({index: 3, type: 'object', value: {} });
|
||||
expect(allArgsFlat).toContain({index: 4, type: 'string', value: '' });
|
||||
expect(allArgsFlat).toContain({index: 5, type: 'undefined', value: undefined});
|
||||
});
|
||||
|
||||
|
||||
it('should treat a value of `null` as `"null"`', function() {
|
||||
var items = [null, null];
|
||||
var expr = null;
|
||||
var reverse = null;
|
||||
var comparator = jasmine.createSpy('comparator').and.returnValue(-1);
|
||||
|
||||
orderBy(items, expr, reverse, comparator);
|
||||
var arg = comparator.calls.argsFor(0)[0];
|
||||
|
||||
expect(arg).toEqual(jasmine.objectContaining({
|
||||
type: 'string',
|
||||
value: 'null'
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
it('should not convert strings to lower-case', function() {
|
||||
var items = ['c', 'B', 'a'];
|
||||
var expr = null;
|
||||
var reverse = null;
|
||||
var sorted = ['B', 'a', 'c'];
|
||||
|
||||
var comparator = function(o1, o2) {
|
||||
return (o1.value < o2.value) ? -1 : 1;
|
||||
};
|
||||
|
||||
expect(orderBy(items, expr, reverse, comparator)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should use `index` as `value` if no other predicate can distinguish between two items',
|
||||
function() {
|
||||
var items = ['foo', 'bar'];
|
||||
var expr = null;
|
||||
var reverse = null;
|
||||
var comparator = jasmine.createSpy('comparator').and.returnValue(0);
|
||||
|
||||
orderBy(items, expr, reverse, comparator);
|
||||
|
||||
expect(comparator).toHaveBeenCalledTimes(2);
|
||||
var lastArgs = comparator.calls.mostRecent().args;
|
||||
|
||||
expect(lastArgs).toContain(jasmine.objectContaining({value: 0, type: 'number'}));
|
||||
expect(lastArgs).toContain(jasmine.objectContaining({value: 1, type: 'number'}));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should support multiple predicates and per-predicate sorting direction', function() {
|
||||
var items = [
|
||||
{owner: 'ownerA', type: 'typeA'},
|
||||
{owner: 'ownerB', type: 'typeB'},
|
||||
{owner: 'ownerC', type: 'typeB'},
|
||||
{owner: 'ownerD', type: 'typeB'}
|
||||
];
|
||||
var expr = ['type', '-owner'];
|
||||
var reverse = null;
|
||||
var sorted = [
|
||||
{owner: 'ownerA', type: 'typeA'},
|
||||
{owner: 'ownerC', type: 'typeB'},
|
||||
{owner: 'ownerB', type: 'typeB'},
|
||||
{owner: 'ownerD', type: 'typeB'}
|
||||
];
|
||||
|
||||
var comparator = function(o1, o2) {
|
||||
var v1 = o1.value;
|
||||
var v2 = o2.value;
|
||||
var isNerd1 = v1.toLowerCase().indexOf('nerd') !== -1;
|
||||
var isNerd2 = v2.toLowerCase().indexOf('nerd') !== -1;
|
||||
|
||||
// Shamelessly promote "nerds"
|
||||
if (isNerd1 || isNerd2) {
|
||||
return (isNerd1 && isNerd2) ? 0 : (isNerd1) ? -1 : 1;
|
||||
}
|
||||
|
||||
// No "nerd"; alpabetical order
|
||||
return (v1 === v2) ? 0 : (v1 < v2) ? -1 : 1;
|
||||
};
|
||||
|
||||
expect(orderBy(items, expr, reverse, comparator)).toEqual(sorted);
|
||||
});
|
||||
});
|
||||
|
||||
describe('(object as `value`)', function() {
|
||||
it('should use the return value of `valueOf()` (if primitive)', function() {
|
||||
var o1 = {k: 1, valueOf: function() { return 2; }};
|
||||
var o2 = {k: 2, valueOf: function() { return 1; }};
|
||||
|
||||
var items = [o1, o2];
|
||||
var expr = null;
|
||||
var sorted = [o2, o1];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should use the return value of `toString()` (if primitive)', function() {
|
||||
var o1 = {k: 1, toString: function() { return 2; }};
|
||||
var o2 = {k: 2, toString: function() { return 1; }};
|
||||
|
||||
var items = [o1, o2];
|
||||
var expr = null;
|
||||
var sorted = [o2, o1];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
});
|
||||
|
||||
|
||||
it('should ignore the `toString()` inherited from `Object`', function() {
|
||||
/* globals toString: true */
|
||||
|
||||
// The global `toString` variable (in 'src/Angular.js')
|
||||
// has already captured `Object.prototype.toString`
|
||||
var originalToString = toString;
|
||||
toString = jasmine.createSpy('toString').and.callFake(originalToString);
|
||||
|
||||
var o1 = Object.create({toString: toString});
|
||||
var o2 = Object.create({toString: toString});
|
||||
|
||||
var items = [o1, o2];
|
||||
var expr = null;
|
||||
|
||||
orderBy(items, expr);
|
||||
|
||||
expect(o1.toString).not.toHaveBeenCalled();
|
||||
expect(o2.toString).not.toHaveBeenCalled();
|
||||
|
||||
toString = originalToString;
|
||||
});
|
||||
|
||||
|
||||
it('should use the return value of `valueOf()` for subsequent steps (if non-primitive)',
|
||||
function() {
|
||||
var o1 = {k: 1, valueOf: function() { return o3; }};
|
||||
var o2 = {k: 2, valueOf: function() { return o4; }};
|
||||
var o3 = {k: 3, toString: function() { return 4; }};
|
||||
var o4 = {k: 4, toString: function() { return 3; }};
|
||||
|
||||
var items = [o1, o2];
|
||||
var expr = null;
|
||||
var sorted = [o2, o1];
|
||||
|
||||
expect(orderBy(items, expr)).toEqual(sorted);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should use the return value of `toString()` for subsequent steps (if non-primitive)',
|
||||
function() {
|
||||
var o1 = {k: 1, toString: function() { return o3; }};
|
||||
var o2 = {k: 2, toString: function() { return o4; }};
|
||||
var o3 = {k: 3};
|
||||
var o4 = {k: 4};
|
||||
|
||||
var items = [o1, o2];
|
||||
var expr = null;
|
||||
var reverse = null;
|
||||
var comparator = jasmine.createSpy('comparator').and.returnValue(-1);
|
||||
|
||||
orderBy(items, expr, reverse, comparator);
|
||||
var args = comparator.calls.argsFor(0);
|
||||
|
||||
expect(args).toContain(jasmine.objectContaining({value: o3, type: 'object'}));
|
||||
expect(args).toContain(jasmine.objectContaining({value: o4, type: 'object'}));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should use the object itself as `value` if no conversion took place', function() {
|
||||
var o1 = {k: 1};
|
||||
var o2 = {k: 2};
|
||||
|
||||
var items = [o1, o2];
|
||||
var expr = null;
|
||||
var reverse = null;
|
||||
var comparator = jasmine.createSpy('comparator').and.returnValue(-1);
|
||||
|
||||
orderBy(items, expr, reverse, comparator);
|
||||
var args = comparator.calls.argsFor(0);
|
||||
|
||||
expect(args).toContain(jasmine.objectContaining({value: o1, type: 'object'}));
|
||||
expect(args).toContain(jasmine.objectContaining({value: o2, type: 'object'}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -2537,6 +2537,18 @@ describe('$location', function() {
|
||||
it('should throw on url(urlString, stateObject)', function() {
|
||||
expectThrowOnStateChange(locationUrl);
|
||||
});
|
||||
|
||||
it('should not throw when base path is another domain', function() {
|
||||
initService({html5Mode: true, hashPrefix: '!', supportHistory: true});
|
||||
inject(
|
||||
initBrowser({url: 'http://domain.com/base/', basePath: 'http://otherdomain.com/base/'}),
|
||||
function($location) {
|
||||
expect(function() {
|
||||
$location.absUrl();
|
||||
}).not.toThrow();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -2138,6 +2138,15 @@ describe('parser', function() {
|
||||
expect(scope.$eval("add(1,2)")).toEqual(3);
|
||||
});
|
||||
|
||||
it('should allow filter chains as arguments', function() {
|
||||
scope.concat = function(a, b) {
|
||||
return a + b;
|
||||
};
|
||||
scope.begin = 1;
|
||||
scope.limit = 2;
|
||||
expect(scope.$eval("concat('abcd'|limitTo:limit:begin,'abcd'|limitTo:2:1|uppercase)")).toEqual("bcBC");
|
||||
});
|
||||
|
||||
it('should evaluate function call from a return value', function() {
|
||||
scope.getter = function() { return function() { return 33; }; };
|
||||
expect(scope.$eval("getter()()")).toBe(33);
|
||||
|
||||
+117
-117
@@ -56,9 +56,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
inject(function($animateCss, $sniffer, $rootElement, $document) {
|
||||
|
||||
var animator;
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
$sniffer.transitions = $sniffer.animations = false;
|
||||
animator = $animateCss(element, {
|
||||
@@ -76,9 +76,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
$animate.enabled(false);
|
||||
|
||||
var animator, element = jqLite('<div></div>');
|
||||
var animator, element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
animator = $animateCss(element, {
|
||||
duration: 10,
|
||||
@@ -91,7 +91,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should silently quit the animation and not throw when an element has no parent during preparation",
|
||||
inject(function($animateCss, $rootScope, $document, $rootElement) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
expect(function() {
|
||||
$animateCss(element, {
|
||||
duration: 1000,
|
||||
@@ -108,8 +108,8 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should silently quit the animation and not throw when an element has no parent before starting",
|
||||
inject(function($animateCss, $$rAF, $rootScope, $document, $rootElement) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
var element = angular.element('<div></div>');
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, {
|
||||
@@ -129,12 +129,12 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should buffer all requests into a single requestAnimationFrame call",
|
||||
inject(function($animateCss, $$rAF, $rootScope, $document, $rootElement) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var count = 0;
|
||||
var runners = [];
|
||||
function makeRequest() {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
var runner = $animateCss(element, { duration: 5, to: fakeStyle }).start();
|
||||
runner.then(function() {
|
||||
@@ -168,10 +168,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
});
|
||||
});
|
||||
inject(function($animateCss, $$rAF, $document, $rootElement) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
function makeRequest() {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
$animateCss(element, { duration: 5, to: fakeStyle }).start();
|
||||
}
|
||||
@@ -188,9 +188,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
var animationDuration = 5;
|
||||
var element, animator;
|
||||
beforeEach(inject(function($animateCss, $rootElement, $document) {
|
||||
element = jqLite('<div></div>');
|
||||
element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
animator = $animateCss(element, {
|
||||
event: 'enter',
|
||||
@@ -374,9 +374,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}
|
||||
|
||||
beforeEach(inject(function($rootElement, $document) {
|
||||
element = jqLite('<div></div>');
|
||||
element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
options = { event: 'enter', structural: true };
|
||||
}));
|
||||
|
||||
@@ -648,7 +648,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should apply a stagger based when an active ng-EVENT-stagger class with a transition-delay is detected",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:0.2s');
|
||||
ss.addRule('.ng-enter', 'transition:2s linear all');
|
||||
@@ -658,7 +658,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
var elm;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
elements.push(elm);
|
||||
$rootElement.append(elm);
|
||||
|
||||
@@ -689,7 +689,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should apply a stagger based when for all provided addClass/removeClass CSS classes",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.red-add-stagger,' +
|
||||
'.blue-remove-stagger,' +
|
||||
@@ -704,7 +704,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
var elm;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div class="blue"></div>');
|
||||
elm = angular.element('<div class="blue"></div>');
|
||||
elements.push(elm);
|
||||
$rootElement.append(elm);
|
||||
|
||||
@@ -759,7 +759,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should block the transition animation between start and animate when staggered",
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:0.2s');
|
||||
ss.addRule('.ng-enter', 'transition:2s linear all;');
|
||||
@@ -769,7 +769,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
var elms = [];
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
element = jqLite('<div class="transition-animation"></div>');
|
||||
element = angular.element('<div class="transition-animation"></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, { event: 'enter', structural: true }).start();
|
||||
@@ -790,14 +790,14 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should block (pause) the keyframe animation between start and animate when staggered",
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addPossiblyPrefixedRule('.ng-enter-stagger', 'animation-delay:0.2s');
|
||||
ss.addPossiblyPrefixedRule('.ng-enter', 'animation:my_animation 2s;');
|
||||
|
||||
var i, element, elements = [];
|
||||
for (i = 0; i < 5; i++) {
|
||||
element = jqLite('<div class="transition-animation"></div>');
|
||||
element = angular.element('<div class="transition-animation"></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, { event: 'enter', structural: true }).start();
|
||||
@@ -819,12 +819,12 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should not apply a stagger if the transition delay value is inherited from a earlier CSS class",
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.transition-animation', 'transition:2s 5s linear all;');
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var element = jqLite('<div class="transition-animation"></div>');
|
||||
var element = angular.element('<div class="transition-animation"></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, { event: 'enter', structural: true }).start();
|
||||
@@ -838,7 +838,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should apply a stagger only if the transition duration value is zero when inherited from a earlier CSS class",
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.transition-animation', 'transition:2s 5s linear all;');
|
||||
ss.addRule('.transition-animation.ng-enter-stagger',
|
||||
@@ -846,7 +846,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
var element, i, elms = [];
|
||||
for (i = 0; i < 5; i++) {
|
||||
element = jqLite('<div class="transition-animation"></div>');
|
||||
element = angular.element('<div class="transition-animation"></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
elms.push(element);
|
||||
@@ -864,13 +864,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should ignore animation staggers if only transition animations were detected",
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', prefix + 'animation-delay:0.2s');
|
||||
ss.addRule('.transition-animation', 'transition:2s 5s linear all;');
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var element = jqLite('<div class="transition-animation"></div>');
|
||||
var element = angular.element('<div class="transition-animation"></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, { event: 'enter', structural: true }).start();
|
||||
@@ -884,13 +884,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should ignore transition staggers if only keyframe animations were detected",
|
||||
inject(function($animateCss, $document, $rootElement) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:0.2s');
|
||||
ss.addPossiblyPrefixedRule('.transition-animation', 'animation: 2s 5s my_animation;');
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
var elm = jqLite('<div class="transition-animation"></div>');
|
||||
var elm = angular.element('<div class="transition-animation"></div>');
|
||||
$rootElement.append(elm);
|
||||
|
||||
var animator = $animateCss(elm, { event: 'enter', structural: true }).start();
|
||||
@@ -904,7 +904,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should start on the highest stagger value if both transition and keyframe staggers are used together",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $browser) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addPossiblyPrefixedRule('.ng-enter-stagger', 'transition-delay: 0.5s; ' +
|
||||
'animation-delay: 1s');
|
||||
@@ -914,7 +914,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
var i, elm, elements = [];
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
elements.push(elm);
|
||||
$rootElement.append(elm);
|
||||
|
||||
@@ -942,14 +942,14 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should apply the closing timeout ontop of the stagger timeout",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $browser) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:1s;');
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var elm, i, elms = [];
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
elms.push(elm);
|
||||
$rootElement.append(elm);
|
||||
|
||||
@@ -969,14 +969,14 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should apply the closing timeout ontop of the stagger timeout with an added delay",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $browser) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.ng-enter-stagger', 'transition-delay:1s;');
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all; transition-delay:50s;');
|
||||
|
||||
var elm, i, elms = [];
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
elms.push(elm);
|
||||
$rootElement.append(elm);
|
||||
|
||||
@@ -996,12 +996,12 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should issue a stagger if a stagger value is provided in the options",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
ss.addRule('.ng-enter', 'transition:2s linear all');
|
||||
|
||||
var elm, i, elements = [];
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
elements.push(elm);
|
||||
$rootElement.append(elm);
|
||||
|
||||
@@ -1035,9 +1035,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should only add/remove classes once the stagger timeout has passed",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout) {
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var element = jqLite('<div class="green"></div>');
|
||||
var element = angular.element('<div class="green"></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, {
|
||||
@@ -1064,9 +1064,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1087,9 +1087,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all; transition-delay:30s;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1110,9 +1110,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
|
||||
@@ -1135,9 +1135,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
|
||||
@@ -1183,9 +1183,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
ss.addRule('.elm.blue', 'transition:2s linear all; background:blue;');
|
||||
ss.addRule('.elm.green', 'background:green;');
|
||||
|
||||
var element = jqLite('<div class="elm"></div>');
|
||||
var element = angular.element('<div class="elm"></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
// timeout will be at 1500s
|
||||
animate(element, 'red', doneSpy);
|
||||
@@ -1228,9 +1228,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should not throw an error any pending timeout requests resolve after the element has already been removed",
|
||||
inject(function($animateCss, $document, $rootElement, $timeout, $animate) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.red', 'transition:1s linear all;');
|
||||
|
||||
@@ -1246,9 +1246,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should consider a positive options.delay value for the closing timeout",
|
||||
inject(function($animateCss, $rootElement, $timeout, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var options = {
|
||||
delay: 3,
|
||||
@@ -1279,9 +1279,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should ignore a boolean options.delay value for the closing timeout",
|
||||
inject(function($animateCss, $rootElement, $timeout, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var options = {
|
||||
delay: true,
|
||||
@@ -1315,9 +1315,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
ss.addRule('.ng-enter', 'transition:10s linear all;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1343,7 +1343,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
beforeEach(module(function($provide) {
|
||||
count = {};
|
||||
$provide.value('$window', extend({}, window, {
|
||||
document: jqLite(window.document),
|
||||
document: angular.element(window.document),
|
||||
getComputedStyle: function(node) {
|
||||
var key = node.className.indexOf('stagger') >= 0
|
||||
? 'stagger' : 'normal';
|
||||
@@ -1354,7 +1354,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
}));
|
||||
|
||||
return function($document, $rootElement) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -1363,7 +1363,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
var i, elm, animator;
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
$rootElement.append(elm);
|
||||
animator = $animateCss(elm, { event: 'enter', structural: true });
|
||||
var runner = animator.start();
|
||||
@@ -1372,7 +1372,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
expect(count.normal).toBe(1);
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
$rootElement.append(elm);
|
||||
animator = $animateCss(elm, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1384,7 +1384,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
expect(count.normal).toBe(2);
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
$rootElement.append(elm);
|
||||
animator = $animateCss(elm, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1396,7 +1396,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should cache frequent calls to getComputedStyle for stagger animations before the next animation frame kicks in",
|
||||
inject(function($animateCss, $document, $rootElement, $$rAF) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
var animator = $animateCss(element, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1406,7 +1406,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
var i, elm;
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
$rootElement.append(elm);
|
||||
animator = $animateCss(elm, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1415,7 +1415,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
expect(count.stagger).toBe(1);
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
$rootElement.append(elm);
|
||||
animator = $animateCss(elm, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1425,7 +1425,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
$$rAF.flush();
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
elm = jqLite('<div></div>');
|
||||
elm = angular.element('<div></div>');
|
||||
$rootElement.append(elm);
|
||||
animator = $animateCss(elm, { event: 'enter', structural: true });
|
||||
animator.start();
|
||||
@@ -1454,9 +1454,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
}
|
||||
|
||||
beforeEach(inject(function($rootElement, $document) {
|
||||
element = jqLite('<div></div>');
|
||||
element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
elementOnSpy = spyOn(element, 'on').and.callThrough();
|
||||
elementOffSpy = spyOn(element, 'off').and.callThrough();
|
||||
@@ -1585,9 +1585,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
triggerAnimationStartFrame();
|
||||
}
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
startAnimation(element, 0.5, 'red');
|
||||
expect(element.attr('style')).toContain('transition');
|
||||
@@ -1608,13 +1608,13 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
it("should clear cache if no animation so follow-up animation on the same element will not be from cache",
|
||||
inject(function($animateCss, $rootElement, $document, $$rAF) {
|
||||
var element = jqLite('<div class="rclass"></div>');
|
||||
var element = angular.element('<div class="rclass"></div>');
|
||||
var options = {
|
||||
event: 'enter',
|
||||
structural: true
|
||||
};
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
var animator = $animateCss(element, options);
|
||||
expect(animator.$$willAnimate).toBeFalsy();
|
||||
|
||||
@@ -1628,9 +1628,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
it('should apply a custom temporary class when a non-structural animation is used',
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
$animateCss(element, {
|
||||
event: 'super',
|
||||
@@ -1647,9 +1647,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should decorate the element with the ng-$prop CSS class',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
$animateCss(element, {
|
||||
event: event,
|
||||
@@ -1664,9 +1664,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should decorate the element with the ng-$prop-active CSS class',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, {
|
||||
event: event,
|
||||
@@ -1685,9 +1685,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should remove the ng-$prop and ng-$prop-active CSS classes from the element once the animation is done',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var animator = $animateCss(element, {
|
||||
event: event,
|
||||
@@ -1711,7 +1711,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should allow additional CSS classes to be added and removed alongside the $prop animation',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement) {
|
||||
var element = jqLite('<div class="green"></div>');
|
||||
var element = angular.element('<div class="green"></div>');
|
||||
$rootElement.append(element);
|
||||
var animator = $animateCss(element, {
|
||||
event: event,
|
||||
@@ -1742,9 +1742,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
['enter', 'leave', 'move', 'addClass', 'removeClass'], function(event) {
|
||||
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.cool-animation', 'transition:1.5s linear all;');
|
||||
element.addClass('cool-animation');
|
||||
@@ -1772,9 +1772,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
['enter', 'leave', 'move', 'addClass', 'removeClass'], function(event) {
|
||||
|
||||
inject(function($animateCss, $rootElement, $document, $window) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.cool-animation', 'transition:1.5s linear all;');
|
||||
element.addClass('cool-animation');
|
||||
@@ -1813,9 +1813,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
['enter', 'leave', 'move', 'addClass', 'removeClass'], function(event) {
|
||||
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
ss.addRule('.cool-animation', 'transition:1.5s linear all;');
|
||||
element.addClass('cool-animation');
|
||||
@@ -1848,9 +1848,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
it('should allow multiple events to be animated at the same time',
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
$animateCss(element, {
|
||||
event: ['enter', 'leave', 'move'],
|
||||
@@ -1882,10 +1882,10 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
it('should not break when running anchored animations without duration',
|
||||
inject(function($animate, $document, $rootElement) {
|
||||
var element1 = jqLite('<div class="item" ng-animate-ref="test">Item 1</div>');
|
||||
var element2 = jqLite('<div class="item" ng-animate-ref="test">Item 2</div>');
|
||||
var element1 = angular.element('<div class="item" ng-animate-ref="test">Item 1</div>');
|
||||
var element2 = angular.element('<div class="item" ng-animate-ref="test">Item 2</div>');
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
$rootElement.append(element1);
|
||||
|
||||
expect($rootElement.text()).toBe('Item 1');
|
||||
@@ -1903,7 +1903,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should decorate the element with the class-$prop CSS class',
|
||||
['add', 'remove'], function(event) {
|
||||
inject(function($animateCss, $rootElement) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
var options = {};
|
||||
@@ -1918,7 +1918,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should decorate the element with the class-$prop-active CSS class',
|
||||
['add', 'remove'], function(event) {
|
||||
inject(function($animateCss, $rootElement) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
var options = {};
|
||||
@@ -1937,9 +1937,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
they('should remove the class-$prop-add and class-$prop-active CSS classes from the element once the animation is done',
|
||||
['enter', 'leave', 'move'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var options = {};
|
||||
options.event = event;
|
||||
@@ -1963,7 +1963,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
['add', 'remove'], function(event) {
|
||||
inject(function($animateCss, $rootElement, $document) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
|
||||
if (event == 'add') {
|
||||
ss.addRule('.natural-class', 'transition:1s linear all;');
|
||||
@@ -1976,7 +1976,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
}
|
||||
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var options = {};
|
||||
options[event + 'Class'] = 'natural-class';
|
||||
@@ -2001,9 +2001,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
ss.addRule('.blue.ng-' + event, 'transition:2s linear all;');
|
||||
|
||||
var element = jqLite('<div class="red"></div>');
|
||||
var element = angular.element('<div class="red"></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
var runner = $animateCss(element, {
|
||||
addClass: 'blue',
|
||||
@@ -2039,9 +2039,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
var element;
|
||||
beforeEach(module(function() {
|
||||
return function($rootElement, $document) {
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
|
||||
element = jqLite('<div></div>');
|
||||
element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
};
|
||||
}));
|
||||
@@ -2140,7 +2140,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
describe("[duration]", function() {
|
||||
it("should be applied for a transition directly", inject(function($animateCss, $rootElement) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
var options = {
|
||||
@@ -2268,7 +2268,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
describe("[delay]", function() {
|
||||
it("should be applied for a transition directly", inject(function($animateCss, $rootElement) {
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
var options = {
|
||||
@@ -2290,7 +2290,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
it("should return false for the animator if a delay is provided but not a duration",
|
||||
inject(function($animateCss, $rootElement) {
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
var options = {
|
||||
@@ -2312,7 +2312,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
'-webkit-transition-delay:10s;' +
|
||||
'transition-delay:10s;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
var options = {
|
||||
@@ -2337,7 +2337,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
'-webkit-transition-delay:10s;' +
|
||||
'transition-delay:10s;');
|
||||
|
||||
var element = jqLite('<div></div>');
|
||||
var element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
|
||||
var options = {
|
||||
@@ -3043,9 +3043,9 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
var element;
|
||||
beforeEach(inject(function($document, $rootElement) {
|
||||
element = jqLite('<div></div>');
|
||||
element = angular.element('<div></div>');
|
||||
$rootElement.append(element);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
}));
|
||||
|
||||
it("should apply easing to a transition animation if it exists", inject(function($animateCss) {
|
||||
@@ -3184,7 +3184,7 @@ describe("ngAnimate $animateCss", function() {
|
||||
'<circle cx="15" cy="5" r="100" fill="orange" />' +
|
||||
'</svg>')($rootScope);
|
||||
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
angular.element($document[0].body).append($rootElement);
|
||||
$rootElement.append(element);
|
||||
|
||||
$animateCss(element, {
|
||||
@@ -3195,17 +3195,17 @@ describe("ngAnimate $animateCss", function() {
|
||||
|
||||
triggerAnimationStartFrame();
|
||||
|
||||
expect(jqLiteHasClass(element[0], 'ng-enter')).toBe(true);
|
||||
expect(jqLiteHasClass(element[0], 'ng-enter-active')).toBe(true);
|
||||
expect(element).toHaveClass('ng-enter');
|
||||
expect(element).toHaveClass('ng-enter-active');
|
||||
|
||||
browserTrigger(element, 'transitionend', { timeStamp: Date.now() + 1000, elapsedTime: 10 });
|
||||
|
||||
expect(jqLiteHasClass(element[0], 'ng-enter')).toBe(false);
|
||||
expect(jqLiteHasClass(element[0], 'ng-enter-active')).toBe(false);
|
||||
expect(element).not.toHaveClass('ng-enter');
|
||||
expect(element).not.toHaveClass('ng-enter-active');
|
||||
}));
|
||||
|
||||
it('should properly remove classes from SVG elements', inject(function($animateCss) {
|
||||
var element = jqLite('<svg width="500" height="500">' +
|
||||
var element = angular.element('<svg width="500" height="500">' +
|
||||
'<rect class="class-of-doom"></rect>' +
|
||||
'</svg>');
|
||||
var child = element.find('rect');
|
||||
|
||||
@@ -12,6 +12,10 @@ describe('ngMessages', function() {
|
||||
return str.replace(/\s+/g,'');
|
||||
}
|
||||
|
||||
function trim(value) {
|
||||
return isString(value) ? value.trim() : value;
|
||||
}
|
||||
|
||||
var element;
|
||||
afterEach(function() {
|
||||
dealoc(element);
|
||||
@@ -605,6 +609,34 @@ describe('ngMessages', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should clean-up the ngMessage scope when a message is removed',
|
||||
inject(function($compile, $rootScope) {
|
||||
|
||||
var html =
|
||||
'<div ng-messages="items">' +
|
||||
'<div ng-message="a">{{forA}}</div>' +
|
||||
'</div>';
|
||||
|
||||
element = $compile(html)($rootScope);
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.forA = 'A';
|
||||
$rootScope.items = {a: true};
|
||||
});
|
||||
|
||||
expect(element.text()).toBe('A');
|
||||
var watchers = $rootScope.$countWatchers();
|
||||
|
||||
$rootScope.$apply('items.a = false');
|
||||
|
||||
expect(element.text()).toBe('');
|
||||
// We don't know exactly how many watchers are on the scope, only that there should be
|
||||
// one less now
|
||||
expect($rootScope.$countWatchers()).toBe(watchers - 1);
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
describe('when including templates', function() {
|
||||
they('should work with a dynamic collection model which is managed by ngRepeat',
|
||||
{'<div ng-messages-include="...">': '<div ng-messages="item">' +
|
||||
@@ -811,7 +843,6 @@ describe('ngMessages', function() {
|
||||
expect(trim(element.text())).toEqual("C");
|
||||
}));
|
||||
|
||||
|
||||
it('should properly detect a previous message, even if it was registered later',
|
||||
inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('include.html', '<div ng-message="a">A</div>');
|
||||
@@ -861,6 +892,25 @@ describe('ngMessages', function() {
|
||||
$httpBackend.flush();
|
||||
}).not.toThrow();
|
||||
}));
|
||||
|
||||
it('should not throw if the template is empty',
|
||||
inject(function($compile, $rootScope, $templateCache) {
|
||||
var html =
|
||||
'<div ng-messages="items">' +
|
||||
'<div ng-messages-include="messages1.html"></div>' +
|
||||
'<div ng-messages-include="messages2.html"></div>' +
|
||||
'</div>';
|
||||
|
||||
$templateCache.put('messages1.html', '');
|
||||
$templateCache.put('messages2.html', ' ');
|
||||
element = $compile(html)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.text()).toBe('');
|
||||
expect(element.children().length).toBe(0);
|
||||
expect(element.contents().length).toBe(2);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('when multiple', function() {
|
||||
|
||||
Vendored
+49
-3
@@ -1893,6 +1893,52 @@ describe('ngMock', function() {
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support assigning bindings when a value is returned from the constructor',
|
||||
function() {
|
||||
var called = false;
|
||||
var data = [
|
||||
{ name: 'derp1', id: 0 },
|
||||
{ name: 'testname', id: 1 },
|
||||
{ name: 'flurp', id: 2 }
|
||||
];
|
||||
module(function($controllerProvider) {
|
||||
$controllerProvider.register('testCtrl', function() {
|
||||
called = true;
|
||||
expect(this.data).toBe(data);
|
||||
|
||||
return {};
|
||||
});
|
||||
});
|
||||
inject(function($controller, $rootScope) {
|
||||
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
|
||||
expect(ctrl.data).toBe(data);
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (/chrome/.test(navigator.userAgent)) {
|
||||
it('should support assigning bindings to class-based controller', function() {
|
||||
var called = false;
|
||||
var data = [
|
||||
{ name: 'derp1', id: 0 },
|
||||
{ name: 'testname', id: 1 },
|
||||
{ name: 'flurp', id: 2 }
|
||||
];
|
||||
module(function($controllerProvider) {
|
||||
//jshint evil: true
|
||||
var TestCtrl = eval('(class { constructor() { called = true; } })');
|
||||
//jshint evil: false
|
||||
$controllerProvider.register('testCtrl', TestCtrl);
|
||||
});
|
||||
inject(function($controller, $rootScope) {
|
||||
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
|
||||
expect(ctrl.data).toBe(data);
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2124,12 +2170,12 @@ describe('ngMockE2E', function() {
|
||||
var hb, realHttpBackend, callback;
|
||||
|
||||
beforeEach(function() {
|
||||
module(function($provide) {
|
||||
callback = jasmine.createSpy('callback');
|
||||
callback = jasmine.createSpy('callback');
|
||||
angular.module('ng').config(function($provide) {
|
||||
realHttpBackend = jasmine.createSpy('real $httpBackend');
|
||||
$provide.value('$httpBackend', realHttpBackend);
|
||||
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
|
||||
});
|
||||
module('ngMockE2E');
|
||||
inject(function($injector) {
|
||||
hb = $injector.get('$httpBackend');
|
||||
});
|
||||
|
||||
@@ -900,6 +900,87 @@ describe('$route', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not get affected by modifying the route definition object after route registration',
|
||||
function() {
|
||||
module(function($routeProvider) {
|
||||
var rdo = {};
|
||||
|
||||
rdo.templateUrl = 'foo.html';
|
||||
$routeProvider.when('/foo', rdo);
|
||||
|
||||
rdo.templateUrl = 'bar.html';
|
||||
$routeProvider.when('/bar', rdo);
|
||||
});
|
||||
|
||||
inject(function($location, $rootScope, $route) {
|
||||
$location.path('/bar');
|
||||
$rootScope.$digest();
|
||||
expect($location.path()).toBe('/bar');
|
||||
expect($route.current.templateUrl).toBe('bar.html');
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
expect($location.path()).toBe('/foo');
|
||||
expect($route.current.templateUrl).toBe('foo.html');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should use the property values of the passed in route definition object directly',
|
||||
function() {
|
||||
var $routeProvider;
|
||||
|
||||
module(function(_$routeProvider_) {
|
||||
$routeProvider = _$routeProvider_;
|
||||
});
|
||||
|
||||
inject(function($location, $rootScope, $route, $sce) {
|
||||
var sceWrappedUrl = $sce.trustAsResourceUrl('foo.html');
|
||||
$routeProvider.when('/foo', {templateUrl: sceWrappedUrl});
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
expect($location.path()).toBe('/foo');
|
||||
expect($route.current.templateUrl).toBe(sceWrappedUrl);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
it('should support custom `$sce` implementations', function() {
|
||||
function MySafeResourceUrl(val) {
|
||||
var self = this;
|
||||
this._val = val;
|
||||
this.getVal = function() {
|
||||
return (this !== self) ? null : this._val;
|
||||
};
|
||||
}
|
||||
|
||||
var $routeProvider;
|
||||
|
||||
module(function($provide, _$routeProvider_) {
|
||||
$routeProvider = _$routeProvider_;
|
||||
|
||||
$provide.decorator('$sce', function($delegate) {
|
||||
$delegate.trustAsResourceUrl = function(url) { return new MySafeResourceUrl(url); };
|
||||
$delegate.getTrustedResourceUrl = function(v) { return v.getVal(); };
|
||||
$delegate.valueOf = function(v) { return v.getVal(); };
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
|
||||
inject(function($location, $rootScope, $route, $sce) {
|
||||
$routeProvider.when('/foo', {templateUrl: $sce.trustAsResourceUrl('foo.html')});
|
||||
|
||||
$location.path('/foo');
|
||||
$rootScope.$digest();
|
||||
expect($location.path()).toBe('/foo');
|
||||
expect($sce.valueOf($route.current.templateUrl)).toBe('foo.html');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('redirection', function() {
|
||||
it('should support redirection via redirectTo property by updating $location', function() {
|
||||
module(function($routeProvider) {
|
||||
|
||||
@@ -119,6 +119,13 @@ describe('linky', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should call the attribute function for all links in the input', function() {
|
||||
var attributeFn = jasmine.createSpy('attributeFn').and.returnValue({});
|
||||
linky("http://example.com and http://google.com", "_self", attributeFn);
|
||||
expect(attributeFn.calls.allArgs()).toEqual([['http://example.com'], ['http://google.com']]);
|
||||
});
|
||||
|
||||
|
||||
it('should strip unsafe attributes', function() {
|
||||
expect(linky("http://example.com", "_self", {"class": "blue", "onclick": "alert('Hi')"})).
|
||||
toBeOneOf('<a class="blue" target="_self" href="http://example.com">http://example.com</a>',
|
||||
|
||||
Reference in New Issue
Block a user