Compare commits

...

238 Commits

Author SHA1 Message Date
Peter Bacon Darwin 23c4ae522a chore(package.json): update version branch information 2015-12-09 13:50:58 +00:00
Peter Bacon Darwin 3112f8e910 docs(CHANGELOG): add 1.5.0-rc.0 changes 2015-12-09 13:45:50 +00:00
Matias Niemelä 21ab82906e chore(angularFiles): the animateRunner.js file doesn't exist for ngAnimate anymore 2015-12-08 13:57:18 -08:00
Peter Bacon Darwin 1dd206ef85 fix($compile): revert allowing non-normalized element names in transclude map
Closes #13455
2015-12-08 16:29:19 +00:00
Georgios Kalpakas da5db4b1b3 docs($resource): re-phrase note in the docs (has to --> would)
As discussed in https://github.com/angular/angular.js/pull/13462#discussion_r46891840.
2015-12-08 17:53:57 +02:00
Georgios Kalpakas 3694390c90 docs($resource): re-phrase warning message (has to --> would)
As discussed in https://github.com/angular/angular.js/pull/13462#discussion_r46891840.
2015-12-08 17:06:19 +02:00
Martin Staffa 36a3c81177 docs(guide/migration): add info for 1.3 checkbox breaking change
Introduced in commit https://github.com/angular/angular.js/commit/c90cefe16142d973a123e945fc9058e8a874c357

Closes #13464
2015-12-08 13:20:27 +01:00
Georgios Kalpakas b0e7d548d0 docs(guide/migration): better describe what the BC in c054288c is about
Also, updated the corresponding entry in changelog.
This came up in
https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251#commitcomment-14783993.
2015-12-08 09:29:06 +02:00
Andy Patterson e4bb838795 fix(input): add missing chars to URL validation regex
Update the list of permitted chars in URLs.

Closes #13379
Closes #13460
2015-12-07 22:31:03 +02:00
Lucas Mirelmann 0ea535035a feat($parse): provide a mechanism to access the locals object
Extends the built-in identifiers definitions by adding `$local`. This is a
non-assignable reference to the locals object.

Closes: #13247
Closes: #13454
2015-12-07 20:21:17 +01:00
Georgios Kalpakas e9aba90f7f docs(guide/$location): fix table header formatting
Closes #13456

Closes #13459
2015-12-07 19:20:05 +02:00
Martin Staffa f50b0cb393 docs(guide/Unit Testing): fix typo
Closes #13227
2015-12-07 14:26:17 +01:00
zainengineer 8b3bec7e07 docs(orderBy): make examples consistent
Updated example which manually injects the filter.
It matches sibling example in functionality.

Also put  html, js and css into separate files.

Also change anchors to buttons.

Closes #13402
2015-12-07 14:21:54 +01:00
Hovhannes Babayan 6752337629 docs(guide/Expressions): note that new operator is unavailable
You cannot create new objects inside Angular expressions. For example:
{{ new Date() }} expression fails.
2015-12-07 13:53:22 +01:00
Martin Staffa 48ad7486d9 docs(changelog, guide/migration): add BC notes for observing unset attributes
Closes #11163
2015-12-07 13:38:42 +01:00
Mil4n 341b834229 docs(tutorial/step_08): fix tense
The original statement is in the past tense (as if it were referring to a previous step of the
tutorial). The mentioned changes, however, are being done in this setp.

Closes #13452
2015-12-07 13:36:27 +02:00
Peter Bacon Darwin 7a668cdd7d fix($sanitize): blacklist SVG <use> elements
The use element can reference external svg's (same origin) and can include
xlink javascript urls or foreign object that can execute xss.

This change disallows `<use>` elements in sanitized SVG markup.

An example of a malicious SVG document would be:

SVG to sanitize:
```
<svg><use xlink:href="test.svg#xss" /></svg>
```

External SVG file (test.svg)
```
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg" width="100"
   height="100"
   id="xss">
<a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="javascript:alert(1)">
  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</a>
</svg>
```

Here the SVG to sanitize loads in the `test.svg` file via the `<use>` element.
The sanitizer is not able to parse this file, which contains malicious
executable mark-up.

This can only be taken advantage of if the external file is available via the
same origin restrictions in place.

Closes #13453

BREAKING CHANGE:

The `<use>` element is now removed from SVG passed to the `$sanitize` service.

This element is only used to import external SVG resources, which is a security
risk as the `$sanitize` service does not have access to the resource in order
to sanitize it.
2015-12-06 14:13:39 +00:00
Lucas Mirelmann 5a674f3bb9 fix($parse): prevent assignment on constructor properties
Prevent malicious attacks involving assignment on `constructor` properties.

Closes #13417
2015-12-06 14:06:26 +00:00
Peter Bacon Darwin e94b37e20e docs(ngTransclude): add better multi-slot translusion docs 2015-12-04 21:44:28 +00:00
Peter Bacon Darwin f5ebcbacf8 docs($compile): add better multi-slot transclusion information 2015-12-04 21:44:28 +00:00
Peter Bacon Darwin 0812af49bd feat(ngTransclude): don't overwrite the contents with an unfilled optional slot
Previously the contents of the `ngTransclude` element would always be emptied,
even if there was no transclusion to replace it.
Now, optional slots that have not been filled with content will not cause
the `ngTransclude` contents to be emptied.

Closes #13426
2015-12-04 21:42:07 +00:00
Dmitrij Maslov c3ae6ed78e fix(ngTransclude): don't replace existing content if no transcluded content exists
Closes #11839
2015-12-04 21:23:38 +00:00
Peter Bacon Darwin c3a2691115 fix($compile): swap keys and values for transclude definition object
Closes #13439
2015-12-04 21:23:38 +00:00
Marcus Nielsen 6976d6d8d8 docs($resource): fix mixed singular/plural
"any of the parameter value" contains plural (any of the) as well as singular (value).
Fixed to be singular to match the rest of the text block.

Closes #13448
2015-12-04 20:49:55 +00:00
Andy Gurden b75c0d8d05 feat(ngMock): destroy $rootScope after each test
Previously $rootScope would be new for each test, but old $rootScopes would never be destroyed.
Now that we are able to destroy the $rootScope, doing so provides an opportunity for code to clean
up things like long-lived event handlers between tests.

Closes #13433
2015-12-04 20:47:26 +00:00
Peter Bacon Darwin df6fade6e6 fix($compile): include non-elements in default transclusion slot
See https://github.com/angular/angular.js/commit/a4ada8ba9c4358273575e16778e76446ad080054#commitcomment-14738387
2015-12-04 13:06:28 +00:00
Shahar Talmi 983b059812 feat(ngView): reference resolved locals in scope
All the resolves for a route are now attached to the route's local scope,
as the property whose name is given by the `resolveAs` property on the
route definition.

If `resolveAs` is not specified it defaults to `$resolve`.

This will make it easier to use `ngRoute`, by being able to reference all
the resolve values for the route, directly on the scope, rather than having
to implement a controller just to copy the resolves across manually.

For example, rather than

```js
$routeProvider.when('/', {
  resolve: {
    item1: ($http) => $http.get(...),
    item2: ($http) => $http.get(...)
  },
  template: '<my-app item1="vm.item1" item2="vm.item2">'</my-app>`,
  controllerAs: 'vm',
  controller: ['item1', 'item2', function(item1, item2) {
    this.item1 = item1;
    this.item2 = item2;
  }]
});
```

one can now do

```js
$routeProvider.when('/', {
  resolve: {
    item1: ($http) => $http.get(...),
    item2: ($http) => $http.get(...)
  },
  template: '<my-app item1="$resolve.item1" item2="$resolve.item2">'</my-app>`
});
```

BREAKING CHANGE:

A new property is being attached to the scope of the route. The default name
for this property is `$resolve`. If your scope already contains a property
with this name then it will be hidden or overwritten.

In this case, you should choose a custom name for this property, that will
not collide with other properties on the scope, by specifying the `resolveAs`
property on the route.

Closes #13400
2015-12-04 12:22:23 +00:00
Martin Staffa 25e8c5927c docs(ngModelController): improve $rollbackViewValue description & example
The example has been expanded to make it easier to provoke the
behavior that the description is talking about (rollbackViewValue
and programmatic model updates)

Related #13340
2015-12-03 23:59:42 +01:00
Utsav Shah 6628b4f1e5 fix($http): throw if url passed is not a string
Throw to prevent hard to debug errors in functions that are
called later.

Fixes #12925
Closes #13444
2015-12-03 21:28:10 +01:00
Martin Staffa 6e18b50a5b feat(ngAnimate): provide ng-[event]-prepare class for structural animations
The new prepare class is added before the animation is pushed to the
queue and removed before the animation runs, i.e. it is immediately
available when a structural animation (enter, leave, move)
is initialized.

The class can be used to apply CSS to explicitly hide these elements
to prevent a flash of content before the animation runs.
This can happen if a structural animation (such as ng-if) sits at the
bottom of a tree which has ng-class animations on the parents.
Because child animations are spaced out with requestAnimationFrame,
the ng-enter class might not be applied in time, so the ng.if element is
briefly visible before its animation starts.
2015-12-03 15:14:22 +01:00
Martin Staffa 77419cf19f fix(ngAnimate): ignore children without animation data when closing them
During parent structural animations, ongoing animations on child elements
are closed. These child elements are identified by their data-ng-animate
attribute. If an element is the clone of an animating element,
it might have this attribute, but no animation runner associated with it,
so we need to ignore it.

Fixes #11992
Closes #13424
2015-12-02 20:16:45 +01:00
Matias Niemelä 193153c3d3 fix(ngAnimate): do not alter the provided options data
Prior to this fix the provided options object would be
altered as the animation kicks off due to the underlying
mechanics of ngAnimate. This patch ensures that a
copy of the provided options is used instead. This patch
also works for when `$animateCss` is used by itself.

Fixes #13040
Closes #13175
2015-12-02 12:23:11 +01:00
Peter Bacon Darwin 6a0686d434 fix(formatNumber): cope with large and small number corner cases
By manually parsing and rounding we can deal with the more tricky numbers

Closes #13394
Closes #8674
Closes #12709
Closes #8705
Closes #12707
Closes #10246
Closes #10252
2015-12-01 21:27:00 +00:00
xieranmaya 08c9a5e9e7 docs($controller): fix typo
Closes #13418
2015-12-01 19:11:31 +00:00
Peter Bacon Darwin a72c12bd70 fix(ngMock): clear out $providerInjector after each test
Closes #13397
Closes #13416
2015-12-01 19:03:56 +00:00
Adam Zerner 73e38658c4 docs(guide/Scopes): fix grammar
Closes #13413
2015-11-30 12:45:18 +01:00
Martin Staffa b641181b93 style($animateCssSpec): remove dump from test 2015-11-29 18:19:44 +01:00
Martin Staffa 7ffb2d3c17 fix($animateCss): consider options.delay value for closing timeout
Previously, options.delay was only considered when a class added an
extra transition style (which leads to style recalculation).

Fixes #13355
Closes #13363
2015-11-26 18:16:22 +01:00
Joan Claret 023b777a56 docs(tutorial/2 - Angular Templates): add closing parenthesis
Closes #13368
2015-11-26 16:22:59 +01:00
Thodoris Bais 0c9480de8c docs(ngTransclude): add a comma for better reading flow
Closes #13385
2015-11-26 16:22:58 +01:00
Martin Staffa 0bc275461c docs(input): note that pattern validates the $viewValue
Closes #13376
2015-11-26 16:22:57 +01:00
Peter Bacon Darwin 0b5ecc64f0 fix($resource): still use the cancellable value if invalid timeout value
We log a deprecation message if `timeout` contains an invalid value.
Now we also use the `callable` value if provided.
2015-11-26 13:15:25 +00:00
Peter Bacon Darwin b183eae7ae fix($resource): don't add noop $cancelRequest after request has resolved 2015-11-26 13:14:12 +00:00
Peter Bacon Darwin 7ddbc9aa35 fix(ngTransclude): fix case where ngTransclude attribute value equals its key
Some preprocessors such as Jade will automatically provide a value for an attribute
rather than leave it empty. E.g. `<div ng-transclude="ng-transclude">`.
In these situations we still want to use the default transclusion slot.

Closes #12934
Closes #13383
2015-11-26 11:11:23 +00:00
Peter Bacon Darwin b2a937d425 test($compile): add test for undefined non-optional reference binding
Demonstrates that #13373 fixes #13367
2015-11-25 10:48:59 +00:00
Lucas Mirelmann 4473b81cda fix($parse): handle interceptors with undefined expressions
When calling `$parse` with `undefined` as the expression and with
an interceptor, then when the function is evaluated, then call the
interceptor

Closes: #13367
Closes: #13373
2015-11-25 10:48:59 +00:00
Georgios Kalpakas 98528be311 feat($resource): add proper support for cancelling requests
Introduced changes:

- Deprecate passing a promise as `timeout` (for `$resource` actions).
  It never worked correctly anyway.
  Now a warning is logged (using `$log.debug()`) and the property is
  removed.
- Add support for a boolean `cancellable` property in actions'
  configuration, the `$resource` factory's `options` parameter and the
  `$resourceProvider`'s `defaults` property.
  If true, the `$cancelRequest` method (added to all returned values for
  non-instance calls) will abort the request (if it's not already
  completed or aborted).
  If there is a numeric `timeout` specified on the action's configuration,
  the value of `cancellable` will be ignored.

Example usage:

```js
var Post = $resource('/posts/:id', {id: '@id'}, {
  get: {
    method: 'GET',
    cancellable: true
  }
});

var currentPost = Post.get({id: 1});
...
// A moment later the user selects another post, so
// we don't need the previous request any more
currentPost.$cancelRequest();
currentPost = Post.get({id: 2});
...
```

BREAKING CHANGE:

Using a promise as `timeout` is no longer supported and will log a
warning. It never worked the way it was supposed to anyway.

Before:

```js
var deferred = $q.defer();
var User = $resource('/api/user/:id', {id: '@id'}, {
  get: {method: 'GET', timeout: deferred.promise}
});

var user = User.get({id: 1});   // sends a request
deferred.resolve();             // aborts the request

// Now, we need to re-define `User` passing a new promise as `timeout`
// or else all subsequent requests from `someAction` will be aborted
User = $resource(...);
user = User.get({id: 2});
```

After:

```js
var User = $resource('/api/user/:id', {id: '@id'}, {
  get: {method: 'GET', cancellable: true}
});

var user = User.get({id: 1});   // sends a request
instance.$cancelRequest();      // aborts the request

user = User.get({id: 2});
```

Fixes #9332
Closes #13050
Closes #13058
Closes #13210
2015-11-24 22:10:54 +00:00
Peter Bacon Darwin 9190d4c3ad chore(bower/publish): read dist-tag from correct package.json 2015-11-24 10:57:16 +00:00
J.P. Poveda d9ec9951e2 docs($timeout): reword sentence for clarity
Closes #13302
2015-11-23 13:10:32 +01:00
Matt Erickson fedafdc677 docs($swipe): remove reference to ngCarousel
ngCarousel no longer exists (or has ever existed).

Closes #13322
Closes #13353
2015-11-23 13:05:09 +01:00
Rahat Ahmed 26c36bb4d1 docs(numberFilter): change decimalPlaces to fractionSize
Replace `decimalPlaces` with `fractionSize`, as `decimalPlaces`
isn't defined anywhere and is most likely meant to be `fractionSize`.

Closes #13323
2015-11-23 13:01:18 +01:00
mohamed amr c7a2028ab3 fix(ngOptions): don't $dirty multiple select after compilation
Closes #13211
Closes #13326
2015-11-23 12:58:56 +01:00
Martin Staffa 596af70101 chore(i18n): update locale files with standalone months
Closes #12844
2015-11-23 10:31:14 +00:00
Martin Staffa 96c73a0672 feat(ngLocale): add support for standalone months
This is needed for languages for which the month on its own has a
different format (case) than when used as part of a date.

Closes #3744
Fixes #10247
Fixes #12642
Closes #12844
2015-11-23 10:31:03 +00:00
Anita Perala 1537651c8c docs(guide/Conceptual Overview): add missing object in sentence
docs: minor grammar fix
missing word in overview

Closes #13346
2015-11-20 18:13:18 +01:00
Martin Staffa d59aeb4e0b docs(angular.element): add more info, fix formatting
- add info about ngJq
- fix alert box
- add info about css function
2015-11-20 18:11:50 +01:00
Julián Salgado 551a33db56 docs(angular.element): note that it does not find elements by tag name / selector
Closes #13107
Closes #13113
2015-11-20 18:11:49 +01:00
Anas Qadrei cd91640146 docs(error/nobase): make base tag visible
Closes #13350
2015-11-20 18:11:26 +01:00
Martin Staffa ab5824ee12 chore: make jshint happy 2015-11-20 12:18:37 +01:00
Matias Niemelä 5c50723535 chore(CHANGELOG): update with changes for 1.4.8 2015-11-19 16:15:30 -08:00
Matias Niemelä 546a277d65 fix(core): ensure animate runner is the same with and without animations
The $$AnimateRunner class is now the same for the core $animate service
and the ngAnimate $animate service. Previously, the core used a different
implementation that didn't match the ngAnimate behavior with regard
to callbacks.

Closes #13205
Closes #13347
2015-11-20 00:22:47 +01:00
Peter Bacon Darwin a5ff651a59 fix($compile): support merging special attribute names in replace directives
When compiling a `replace` directive, the compiler merges the attributes from
the replaced element onto the template element.

Unfortunately, `setAttribute` and other related DOM methods do not allow certain
attribute names - in particular Angular 2 style names such as `(click)` and `[value]`.

This is relevant when using ngForward with Angular Material, since the `mgButton`
directive uses `replace` and in the former you often use `(click)`.

This fixes the problem but for those special attributes the speed is considerably
slow.

Closes #13317
Closes #13318
2015-11-19 08:52:56 +00:00
Peter Bacon Darwin ccd47ec904 test(filterFilter): modify Date test to prevent off by one year error in Firefox 2015-11-18 12:56:06 +00:00
Peter Bacon Darwin 2e23a3cdbc docs(CHANGELOG): tweak the latest changelog descriptions 2015-11-18 08:28:52 +00:00
Matias Niemelä ca7f4a387c chore(CHANGELOG): update with changes for 1.5.0-beta.2 2015-11-17 15:57:27 -08:00
Jason Bedard 898a3fd3b9 perf($q): only bind Deferred methods when returned publicly from $q.defer 2015-11-17 21:53:09 +01:00
Jason Bedard 9473371343 perf($q): reduce closures when resolving promises
- changes Deferred.$$resolve to only wrap the internal resolve/reject when wrapping another promise

Closes: #13293
2015-11-17 21:49:52 +01:00
Lucas Mirelmann e5275590db refactor($interval): do not use notify to trigger the callback
Do not use `$q.notify` to trigger the callback. This allows `$q` to be replaced
with another Promise/A+ compilant library

Closes: #13261
2015-11-17 21:46:55 +01:00
Matias Niemelä 78297d252d feat(ngAnimate): introduce ngAnimateSwap directive 2015-11-17 11:36:07 -08:00
Steve Mao e5e0884eaf docs($http): add a missing semicolon
Closes #13311
2015-11-16 16:55:33 +01:00
Leonardo Borges Avelino 551d1c20cf docs(guide/Conceptual Overview): change deprecated $http.success into .then
Using the standard then method instead success, because success is deprecated

Closes #13309
2015-11-16 16:54:13 +01:00
Peter Bacon Darwin f5aa207960 fix(jqLite): deregister special mouseenter / mouseleave events correctly
Closes #12795
Closes #12799
2015-11-12 18:44:19 +00:00
rrsivabalan 4412fe238f fix($location): ensure $locationChangeSuccess fires even if URL ends with #
Closes #12175
Closes #13251
2015-11-12 13:34:18 +00:00
Eric Lee Carraway 8088284f66 docs(readme): fix typo (setup => set up)
spell set up as two words
here, it is an adjective modifying the noun "environment"

Closes #13297
2015-11-12 10:22:50 +02:00
Eric Lee Carraway 25f1bbaad1 docs(contributing): fix typo (a unambiguous => an unambiguous)
use the article “an” before words that start with a vowel sound

Closes #13292
2015-11-11 14:21:41 +02:00
Peter Bacon Darwin bd7b217729 fix($compile): bind all directive controllers correctly when using bindToController
Previously only the first directive's controller would be bound correctly.

Closes #11343
Closes #11345
2015-11-10 20:30:53 +00:00
Georgios Kalpakas 50557a6cd3 fix($compile): evaluate against the correct scope with bindToController on new scope
Previously, the directive bindings were evaluated against the directive's
new (non-isolate) scope, instead of the correct (parent) scope.
This went unnoticed most of the time, since a property would be eventually
looked up in the parent scope due to prototypal inheritance. The incorrect
behaviour was exhibited when a property on the child scope was shadowing
that on the parent scope.

This commit fixes it.

Fixes #13021
Closes #13025
2015-11-10 20:30:53 +00:00
Jakub Torbicki 1c13a4f45d fix($compile): bind all directive controllers correctly when using bindToController
Previously only the first directive's controller would be bound correctly.

Closes #11343
Closes #11345
2015-11-10 20:30:53 +00:00
mzdunek93 1a98c0ee34 fix($compile): fix scoping of transclusion directives inside replace directive
Closes #12975
Closes #12936
Closes #13244
2015-11-10 20:30:53 +00:00
Jason Bedard b837fc3116 refactor($compile): simplify multi element directive check
Previously, we would check if an attribute indicates a multi-element
directive, now we only do this check if the attribute name actually
matches the multi-element name pattern.

Closes #12365
2015-11-07 15:31:34 +01:00
Martin Staffa aff74ec87b docs(changelog, migration): add BC notice for ngMessages evaluation
Introduced by

Closes #11616
Closes #12001
2015-11-06 17:17:20 +01:00
Martin Staffa 077ee37942 docs(changelog, migration): add BC notice for setting ngOptions as attribute
Caused by 7fda214c4f

Closes #13145
2015-11-06 16:41:14 +01:00
Martin Staffa 0efef2385f chore(docs): display search result areas in a fixed order 2015-11-06 16:30:33 +01:00
Martin Staffa 7da22e6685 chore(docs): improve layout of search results
The API section now uses a multi-column list. This preserves the actual
order of items. Note that only browser that support @supports and
columns get the new behavior.
The line-breaking behavior of search results is also improved. Previously,
long words would break onto new lines or run into the second column.
2015-11-06 16:30:32 +01:00
Doug Krugman 92bdd7627f docs(guide/Concepts): remove unused refresh property
Closes #13257
2015-11-06 10:15:19 +02:00
jody tate 39ebb06baf docs(guide/Directives): change "it" to possessive
Closes #13253
2015-11-05 14:24:57 +02:00
Georgii Dolzhykov 9cf6b197ab docs($rootScope.Scope): default value of objectEquality parameter
Closes: #13252
2015-11-04 21:53:32 +01:00
Martin Staffa 4971ef12d4 fix(ngMessage): make ngMessage compatible with ngBind
Fixes #8089
Closes #13074

BREAKING CHANGE:

ngMessage is now compiled with a priority of 1, which means directives
on the same element as ngMessage with a priority lower than 1 will
be applied when ngMessage calls the $transclude function.
Previously, they were applied during the initial compile phase and were
passed the comment element created by the transclusion of ngMessage.
To restore this behavior, custom directives need to have
their priority increased to at least "1".
2015-11-04 17:13:53 +01:00
Martin Staffa 7c792f4cc9 docs(ngRepeat): add more info about watching and tracking
- mention $watchCollection
- highlight that track by "id" can improve render performance

Related #9508
2015-11-03 21:38:06 +01:00
Georgios Kalpakas 4daafd3dbe perf(merge): remove unnecessary wrapping of jqLite element
Fixes https://github.com/angular/angular.js/commit/75292a6cb5e17d618902f7996e80eb3118eff7b0#commitcomment-14137538

Closes #13236
2015-11-03 17:45:44 +02:00
Peter Bacon Darwin fe11265fdc test(merge): fix check on jquery object 2015-11-02 20:12:12 +00:00
Peter Bacon Darwin 75292a6cb5 fix(merge): ensure that jqlite->jqlite and DOM->DOM
Previously we were wrapping DOM elements into jqlite objects when cloning
and vice versa.

Fixes https://github.com/angular/angular.js/pull/12286#discussion_r43656917
2015-11-02 19:54:20 +00:00
JonyD 4ff6c85792 docs(ngRepeat): fix link to MDN
Closes #13226
2015-11-02 20:48:12 +01:00
Martin Staffa e26bc2370b docs(ngInclude): fix incorrect link 2015-11-02 20:45:13 +01:00
Martin Staffa 33713deeb8 docs(tutorial/0 - Bootstrapping): mention that the setup must be completed
Closes #13106
2015-11-02 20:40:53 +01:00
luckylooke 17715fa366 fix(merge): clone elements instead of treating them like simple objects
Similar fix to #11720

Closes #12286
2015-11-02 17:20:59 +00:00
Jason Bedard 96288d02d3 refactor($compile): remove skipDestroyOnNextJQueryCleanData, remove jq data of all replaced nodes
Closes: #12094
2015-11-01 23:43:01 +01:00
Lucas Mirelmann 8be98e4fdf test($interpolate): fix test on $interpolate
Fix for a test in $interpolate after an incomplete merge
2015-11-01 23:35:05 +01:00
Jason Bedard cf83b4f445 perf($interpolate): provide a simplified result for constant expressions
Closes: #10414
2015-11-01 23:31:33 +01:00
Matthew Hill 15bfea8339 docs(angular-mocks): clarify angular.mock.module usage with objects
Closes #12354
2015-11-01 07:13:00 +00:00
Jason Bedard 9b90c32f31 perf($compile): use static jquery data method to avoid creating new instances 2015-11-01 06:51:22 +00:00
Chris J. Lee f4bf744516 chore(protractor-conf.js): remove dangling comma
Closes #13051
2015-11-01 06:45:42 +00:00
Peter Bacon Darwin 0d96995fcc chore(package.json): update dgeni-packages to 0.11.0 2015-10-31 20:16:24 +00:00
Nuri Hodges 2a85a634f8 fix(orderByFilter): throw error if input is not array-like
BREAKING CHANGE:
Previously, an non array-like input would pass through the orderBy filter
unchanged.
Now, an error is thrown. This can be worked around by converting an object
to an array, either manually or using a filter such as
https://github.com/petebacondarwin/angular-toArrayFilter.
(`null` and `undefined` still pass through without an error, in order to
support asynchronous loading of resources.)

Closes #11255
Closes #11719
2015-10-31 02:52:03 +02:00
Bert Verhelst 00d2b2c4cf docs($location): improve style
Closes #13072
2015-10-30 22:05:49 +01:00
Martin Staffa e688f07023 docs(error/ctreq): fix typo
Closes #13083
2015-10-30 22:02:26 +01:00
Michael George Attard 8e2f7d37e4 docs($rootScope): improve clarity and consistency
Closes #13110
2015-10-30 21:59:43 +01:00
Georgios Kalpakas f5bc3ed9b4 test($templateRequest): remove unused dependencies
Closes #13199
2015-10-30 21:58:16 +01:00
Georgios Kalpakas 11d60af3dc docs($templateRequestProvider): make the description more generic and fix link 2015-10-30 21:58:12 +01:00
Pablo Villoslada Puigcerber 865f6065e7 docs(select): document the multiple attribute
Add the `multiple` attribute to the documentation of the select directive.

Closes #13119
2015-10-30 20:33:39 +02:00
Lucas Galfaso 91c0b364af style(ngOptionsSpec): fix code style issue 2015-10-30 12:28:26 +01:00
Lucas Mirelmann fce07f55e5 style($http): do not use angular global object to invoke isObject
Closes: #13204
2015-10-30 12:26:32 +01:00
Jason Bedard c8768d12f2 perf(copy): avoid regex in isTypedArray
Closes: #12054
2015-10-30 12:12:14 +01:00
Stephen Sauceda af6342d6fb docs(CONTRIBUTING.md): add commitizen instructions 2015-10-30 08:20:09 +00:00
Stephen Sauceda a179757fad chore(package.json): add commitizen, adapter and npm script
Closes #13194
2015-10-30 08:20:09 +00:00
Lucas Mirelmann 35eada68c4 refactor($parse): simplify constantWatchDelegate
Closes: #13176
2015-10-29 21:30:37 +01:00
Sreenivasan K bfad2a4f4a fix($animate): ensure leave animation calls close callback
Closes #12278
Closes #12096
Closes #13054
2015-10-29 07:51:18 +00:00
Shahar Talmi 54e816552f feat(Module): add helper method, component(...) for creating component directives
Since we are promoting component directives as the building blocks of
Angular applications, this new helper provides a simpler method for
defining such directives. By using sensible, widely accepted, conventions
the number of parameters needed has been cut down dramatically.

Many component directives can now be defined by simply providing a `name`,
`template`/`templateUrl`, a `controller`, and `bindings`:

```js
myMod.component('myComp', {
  template: '<div>My name is {{myComp.name}}</div>',
  controller: function() {
  },
  bindings: { name: '=' }
});
```

Closes #10007
Closes #12933
2015-10-29 07:25:02 +00:00
Stanislav Komanec 4fc734665e fix($resource): allow XHR request to be cancelled via timeout promise
Closes #12657
Closes #12675
Closes #10890
Closes #9332
2015-10-28 22:21:37 +00:00
Peter Bacon Darwin b8736e65b0 test($rootScope): ensure that only child scopes are disconnected
Related to #11786 and 8fe781fbe7
2015-10-28 22:03:42 +00:00
Alicia Lauerman b9bed7d9da fix($cacheFactory): check key exists before decreasing cache size count
Previously, there was no check for the existence of an item in the
cache when calling `$cacheFactory.remove()` before modifying the cache size
count.

Closes #12321
Closes #12329
2015-10-28 21:50:00 +00:00
Peter Bacon Darwin b2fc39d2dd feat($templateRequest): support configuration of $http options
It is now possible to configure the options sent to $http for template requests.
If no value is configured then the request will use the default $http options.

Thanks to @luckycadow for help on this feature

Closes #13188
Closes #11868
Closes #6860
2015-10-28 21:43:34 +00:00
Georgios Kalpakas 7c0731edb2 fix($http): apply transformResponse even when data is empty
Note, that (as a by-product of the previous implementation) only non-empty
data was passed through the `transformResponse` pipeline. This is no
longer the case.

When using a custom `transformResponse` function, one should make sure it
can also handle an empty (i.e. falsy) `data` argument appropriately.

Fixes #12976
Closes #12979
2015-10-28 21:41:30 +00:00
Peter Bacon Darwin 8fe781fbe7 fix($rootScope): stop IE9 memory leak when destroying scopes
Ensure that all child scopes are completely disconnected when a parent is
destroyed.

Closes #10706
Closes #11786
2015-10-28 21:32:43 +00:00
Charlie-Hua 23932a26ff docs(ngModelOptions): add missing user.data result for updateOn: blur example
In the updateOn:blur example, there is an input for user.data but the
result is missing and nowhere to see how the value changes compared to user.name.

Closes #13129
2015-10-28 22:09:04 +01:00
Georgios Kalpakas 3ca4ca463c refactor(ngMessage): remove unused argument
Closes #13087
Closes #12508
2015-10-28 18:56:51 +01:00
Peter Bacon Darwin 395f3ec638 fix(ngOptions): skip comments and empty options when looking for options
Related #12952
Closes #12190
Closes #13029
Closes #13033
2015-10-28 18:36:21 +01:00
Stu Cox 964a901bd8 docs($q): add a note re. difference in exception handling vs ES6
Closes #11472
Closes #13101
2015-10-28 08:17:39 +00:00
Ryan Hart a995ee17ee docs(ngOptions): explain the caveats of using select as and track by together
Changes:

* Modify warning message to indicate that `track by` can be used with `select as`,
  but subject to certain limitations.
* Provide both a working and an non-working example.
* Explain why the latter does not work.

Closes #13007
2015-10-27 22:00:31 +02:00
Sam Rawlins 794d1c1ebe docs($anchorScroll): fix link to HTML5 spec
Closes #13180
2015-10-27 20:39:08 +02:00
Marcy Sutton 662fb282c1 fix(ngAria): don't add tabindex to radio and checkbox inputs
Closes #12492
Closes #13095
2015-10-27 17:46:14 +01:00
Andrew Austin ffb6b2fb56 fix(ngInput): change URL_REGEXP to better match RFC3987
The URL_REGEXP in use to perform validation in ngInput is too restrictive and fails to
follow RFC3987. In particular, it only accepts ftp, http, and https scheme components and
rejects perfectly valid schemes such as "file", "mailto", "chrome-extension",
etc. The regex also requires the scheme to be followed by two "/" but the RFC says
0 to n are acceptable. This change fixes both of these issues to better align to
the standard.

Closes #11341
Closes #11381
2015-10-26 21:45:10 +00:00
Kuzminov Aleksandr Sergeevich 941c1c35f1 fix(jqLite): ensure mouseenter works with svg elements on IE
Closes #10259
Closes #10276
2015-10-26 21:21:44 +00:00
Lucas Mirelmann 29a05984fe feat($injector): Allow specifying a decorator on $injector
Allows the definition of a decorator on `$injector`

Closes: #13103
2015-10-26 22:10:41 +01:00
sevdog 4038aabffa docs($animateCss): add missing documentation for the structural option
Add missing documentation for structural option in `$animateCss` service

Closes #13049
2015-10-26 13:02:13 -07:00
Lucas Galfaso 773efd0812 fix(isArrayLike): handle jQuery objects of length 0
Closes: #13169
Closes: #13171
2015-10-26 16:37:38 +00:00
Risan Bagja Pradana 80881949fc docs(tutorial): add a note about Chrome or Firefox not being available
Based on the current configuration, Karma will run the tests on both
Chrome and Firefox, which will result in an error if either browser is not
available on the user's machine. This commit adds a note and directions on
how to solve this.

Closes #13114
2015-10-26 15:01:14 +02:00
Jack Viers 2c8d87e064 fix(Angular.js): fix isArrayLike for unusual cases
Closes #10186
Closes #8000
Closes #4855
Closes #4751
Closes #10272
2015-10-25 14:54:11 +00:00
Jason Bedard 33c67ce785 perf(copy): only validate/clear user specified destination
Closes #12068
2015-10-24 11:08:14 +02:00
Lucas Mirelmann ad4296d966 refactor($rootScope): remove unused dependency
Removed unused dependency

Closes #13102
2015-10-16 18:28:37 +02:00
Georgios Kalpakas 469b14a525 refactor($compile): remove unused var
Closes #13086
2015-10-15 17:02:49 +03:00
Lucas Mirelmann 1caf0b6bee fix($parse): evaluate once simple expressions in interpolations
For simple expressions without filters that have a stateless interceptor
then handle the 2nd phase parse evaluation using `inputs`.

TL;DR
This fixes the issue that interpolated simple expressions were evaluated twice
within one digest loop.

Long version, things happen in the following order:

* There was an overhaul on $interpolate, this overhaul changed $parse and
  incorporated the concept of an interceptor.
* Optimization on $parse landed  so expressions that have filters without
  parameters (or the parameters are constants) would be evaluated in 2 phases,
  first to evaluate the expression sans the filter evaluation and then with
  the filter evaluation. This also used interceptors [the second evaluation
  issue was added here]
* More optimizations on $parse landed and now expressions could be evaluated
  in 2 phases. One to get all the possible values that could change (lets call
  this state), the state was checked by $watch to know if an expression changed.
  The second to continue the evaluation (as long as this state is provided).
  This, once again, used interceptors

The last change, was supposed to fix the issue, but there was an assumption in
the existing code that the code would always generate the 2 phases functions,
but that is not true. If the expression is simple enough (just like the one in
your case) then the 2-phase evaluations functions are not generated. In this
case, if a stateless interceptor was added (just like what $interpolate adds)
then the state was not used and you see the function being evaluated twice.
This explains why, if you change the expression from
`Hello {{log('A')}} {{log('B')}}!` to `Hello {{log('A') + ' ' + log('B')}}!`,
then the repetition is not there.

Closes #12983
Closes #13002
2015-10-14 23:45:07 +02:00
zurin 9f716dd590 docs(guide/Scopes): fix grammar
Added a comma to make reading more natural.

Closes #13084
2015-10-14 16:14:21 +03:00
Michael Salmon 914a934b6f docs(guide/Directives): improve description of linking function
The `controller` and `transclude` parameters of the linking function were not
mentioned in the description, but used in the examples.
This commit improves the description and links to the `$compile` API docs
for more details.

Closes #13028
2015-10-14 10:48:06 +03:00
Peter Bacon Darwin a4ada8ba9c feat($compile): multiple transclusion via named slots
Now you can efficiently split up and transclude content into specified
places in a component's template.

```html
<pane>
  <pane-title>Some content for slot A</pane-title>
  <pane-content>Some content for slot A</pane-content>
</component>
```

```js
mod.directive('pane', function() {
  return {
    restrict: 'E',
    transclude: { paneTitle: '?titleSlot', paneContent: 'contentSlot' },
    template:
    '<div class="pane">' +
      '<h1 ng-transclude="titleSlot"></h1>' +
      '<div ng-transclude="contentSlot"></div>' +
    '</div>' +
  };
});
```

Closes #4357
Closes #12742
Closes #11736
Closes #12934
2015-10-12 14:46:53 +01:00
Martin Staffa 40c974ab14 docs(linky): mention sanitization, improve formatting 2015-10-09 15:10:37 +02:00
Stian Jensen 06f002b161 feat(linky): add support for custom attributes
Optional extra attributes may be defined either as:
- a map of attributes and values
- a function that takes the url as a parameter and returns a map

Closes #12558
Closes #13061
2015-10-09 15:10:36 +02:00
Peter Bacon Darwin 8226ff8b8e style(ngMockSpec): fix excessive indentation
See https://github.com/angular/angular.js/pull/12406#discussion_r41503587. Thanks @gkalpak :-)
2015-10-08 15:13:18 +01:00
Peter Bacon Darwin d67e999dfb feat(ngMock): add expectRoute and whenRoute shortcuts with colon param matching
Add `params` argument to the `when()` and `expect()` functions to give map regex
groups to keys on a new `params` argument of the `respond()` callback.

Add `whenRoute` and `expectRoute` methods to `$httpBackend` to support matching
URLs by patterns similar to those defined for `ngRoute`.

Closes #12406
2015-10-08 15:07:18 +01:00
Martin Staffa a8e03b3a90 docs(ngOptions): add info about preselecting complex models
Closes #12966
2015-10-08 15:54:20 +02:00
Chris J. Lee 6f3e26c404 test(ngResource): fix typos in tests
Closes #13044
2015-10-08 11:29:16 +03:00
Flavio Corpa Ríos 1c75ea613d docs(ngInclude): add workaround for using onload function with SVG in IE11
Closes #12493
Closes #13042
2015-10-07 23:02:23 +02:00
Jason Hopper c8e1db2050 docs(tutorial): update angular module versions to reflect tutorial files
Closes #12991
Closes #12992
2015-10-07 17:51:50 +02:00
Sugan Krishnan 74ed28665d docs($sce): fix typo
Closes #13030
2015-10-07 13:22:40 +01:00
Peter Bacon Darwin 4ad0ca130d refactor($compile): check removeWatches before calling
Previously we assigned `noop` if there was no function but there is no
performance advantage in doing this since the check would have to happen
either at assignment time or at call time.

Removing this use of `noop` makes the code clearer, IMO :-)

Closes #12528
2015-10-07 12:30:30 +01:00
Peter Bacon Darwin b51dd3010d refactor($compile): initialize removeWatchCollection at the start
This check means that we don't have to keep checking whether the collection
has been created when adding a new watcher

Closes #12528
2015-10-07 12:30:21 +01:00
Peter Bacon Darwin 6e6f31943c refactor($compile): rename variables to clarify their purpose
Closes #12528
2015-10-07 12:30:15 +01:00
Jason Bedard 66fee7e22a refactor($compile): move $scope.$on('$destroy') handler out of initializeDirectiveBindings
Since only one of three invocations of `initializeDirectiveBindings` actually
adds a `$destroy` handler to the scope (the others just manually call unwatch
as needed), we can move that code out of this method.

This also has the benefit of simplifying what parameters need to be passed
through to the linking functions

Closes #12528
2015-10-07 12:30:06 +01:00
Martin Staffa beea571660 Revert "fix(ngOptions): skip comments when looking for option elements"
This reverts commit 7f3f3dd3eb.
The fix only fixed a specific case and exhibited a flawed logic
(namely skipping every option if the emptyOption is a comment).
See https://github.com/angular/angular.js/issues/12190#issuecomment-145877914

Conflicts:
	test/ng/directive/ngOptionsSpec.js
2015-10-07 11:28:13 +02:00
Richard Harrington 0c8a9a0e1a docs(constant): fix pluralization
Closes #13024
2015-10-06 23:21:15 +03:00
Peter Bacon Darwin fd83d3724a fix(ngMock): reset cache before every test
We don't need to have values in the cache from previous tests. This was
causing failures in all subsequent tests when a single test failed due
to a memory leak.

Now that we reset the cache each time we do not need to store the cache
size at the start of each test

Closes #13013
2015-10-06 13:55:16 +01:00
Martin Staffa 2fcfd75a14 fix(ngOptions): override select option registration
When ngOptions is present on a select, the option directive should not be able to
register options on the selectCtrl since this may cause errors during the
ngOptions lifecycle.

This can happen in the following cases:

- there is a blank option below the select element, an ngModel
directive, an ngOptions directive and some other directive on the select
element, which compiles the children of the select
(i.e. the option elements) before ngOptions is has finished linking.

- there is a blank option below the select element, an ngModel
directive, an ngOptions directive and another directive, which uses
templateUrl and replace:true.

What happens is:
- the option directive is compiled and adds an element `$destroy` listener
that will call `ngModel.$render` when the option element is removed.
- when `ngOptions` processes the option, it removes the element, and
triggers the `$destroy` listener on the option.
- the registered `$destroy` listener calls `$render` on `ngModel`.
- $render calls `selectCtrl.writeValue()`, which accesses the `options`
object in the `ngOptions` directive.
- Since `ngOptions` has not yet completed linking the `options` has not
yet been defined and we get an error.

This fix moves the registration code for the `option` directive into the
`SelectController.registerOption()` method, which is then overridden by
the `ngOptions` directive as a `noop`.

Fixes #11685
Closes #12972
Closes #12968
Closes #13012
2015-10-06 13:43:28 +01:00
Raghav 2d40507547 docs($animate): fixed typo ("an animations" -> "any animations")
Closes #13020
2015-10-06 13:15:13 +03:00
Georgios Kalpakas ecf9304811 fix(limitTo): start at 0 if begin is negative and exceeds input length
Previously, specifying a negative `begin` whose abs value exceeds the
input's length, would behave unexpectedly (depending on the value of
`limit` relative to `begin`). E.g.:

```
limitToFilter('12345', 3, -7) === '1'
// but
limitToFilter('12345', 10, -7) === '123'
```

This commit fixes the unexpected behaviour, by setting `begin` to 0 in the
aforementioned cases. Thus, the previous examples become:

```
limitToFilter('12345', 3, -7) === limitToFilter('12345', 3, 0) === '123'
// and
limitToFilter('12345', 10, -7) === limitToFilter('12345', 10, 0) === '12345'
```

Fixes #12775
Closes #12781
2015-10-06 09:38:54 +01:00
Matias Niemelä b9ab88776b docs(ngAnimate): simplify $animateCss example code 2015-10-05 10:55:56 -07:00
spoonraker 00bf218304 docs(tutorial): updates for the text for animations in step 12
The grammar for the animation description has now been improved.

Closes #12740
2015-10-05 10:20:58 -07:00
Jason Hopper 95fbf168d1 docs(tutorial): update tutorial copy to reflect updates to tutorial source @bower.json excerpt for animations
Code breaks if tutorial is followed without reset.
bower.js exceprt copy does not match source.
Changed to reflect in text body.

Closes #12993
2015-10-05 10:17:30 -07:00
Shahar Talmi 51a27c0f1a feat(ngMock): invoke nested calls to module() immediately
Before 1.3, it was possible to call `angular.mock.module()` from inside
another module. After this version additional calls were just ignored.

It is helpful to be able to do this since a test may have helper functions
that need to access "config"-time things such as constants or providers,
and without this change it is difficult to get hold of the `$provide`
object from within those helpers.

Closes #12887
2015-10-05 12:50:31 +01:00
Magnus Pedersen f02811f0bb docs(ngOptions): rephrased a sentence for clarity
Closes #13010
2015-10-05 11:48:30 +03:00
Alexandr Gureev bcf78ebb18 docs(ngAnimate): fix typos in examples
Closes #12995
2015-10-02 11:23:48 +03:00
John Zhang 3050dd1b47 docs($httpProvider): fix description of useLegacyPromiseExtensions
useLegacyPromiseExtensions's default value is true, and the  legacy
methods exist when it is set to true.

Closes #12974
2015-10-01 18:30:01 +02:00
Donghwan Kim 1efdb4745a docs(guide/Running in Production): fix an incorrect indefinite article
Closes #12986
2015-10-01 18:25:11 +02:00
koyner d73f7dff45 docs(guide/Forms): fix indentation.
Closes #12988
2015-10-01 18:24:29 +02:00
Martin Staffa f047ad2628 docs(guide/Using $location): note that the fakeBrowser is not for actual projects
Closes #12982
Closes #12987
2015-10-01 18:20:31 +02:00
Peter Bacon Darwin 049d3def80 docs(CHANGELOG): the $time service feature was reverted as it is not ready 2015-10-01 13:22:40 +01:00
Peter Bacon Darwin 00778aa239 test($compile): move lazy compile specs into a describe block 2015-10-01 12:27:52 +01:00
Matias Niemelä 6b123a0419 chore(CHANGELOG): update with changes for 1.5.0-beta.1 2015-09-29 13:59:34 -07:00
Matias Niemelä 4079eea6b3 chore(CHANGELOG): update with changes for 1.4.7 2015-09-29 13:59:21 -07:00
Matias Niemelä d277ac2eb8 chore(CHANGELOG): update with changes for 1.3.20 2015-09-29 13:59:10 -07:00
Matias Niemelä 9deb123d04 chore(CHANGELOG): update with changes for 1.2.29 2015-09-29 13:58:54 -07:00
Martin Probst 1c2d2e8ba0 docs($sceProvider): XSS when turning of SCE
Document that turning off SCE is very, very dangerous and should normally not be
used by applications.
2015-09-28 14:19:38 +02:00
Martin Probst 144bcc84ab docs($interpolateProvider): document XSS in $interpolate
`$interpolateProvider.startSymbol` & friends are often used dangerously, to embed Angular templating in other templating languages. This change documents that that is a very dangerous practice.
2015-09-28 14:07:31 +02:00
Shrulik 693021c449 docs($animateCss): remove superfluous asterisk
Closes #12959
2015-09-27 22:14:46 +03:00
Rishabh Jain dc818e1165 docs(input[time]): fixes a typo 2015-09-27 20:11:24 +02:00
Martin Staffa e51174bf13 docs($http): link to usage where config is mentioned; make drier
Linking to usage section makes it easier for beginners to find out what the config object looks like.
The General Usage section now features an example that actually uses $http(config), and the Shortcut Methods section has been moved so that it appears directly after.

Closes #12949
Closes #12950
2015-09-27 15:42:25 +02:00
Martin Staffa 7f3f3dd3eb fix(ngOptions): skip comments when looking for option elements
When the empty/blank option has a directive that transcludes, ngIf for example,
a comment will be added into the select. Previously, ngOptions used this
comment as the empty option, which would mess up the displayed options.

Closes #12190
2015-09-27 15:08:35 +02:00
Martin Staffa d077966ff1 test(ngOptions): clarify a test description 2015-09-26 17:59:35 +02:00
Stefan Krüger 0df4ff800a docs(guide/Directives): let myTabs directive ctrl use inline array notation
modified `docsTabsExample` myTabs directive ctrl at
[Creating Directives that Communicate Example](https://docs.angularjs.org/guide/directive#creating-directives-that-communicate) so that it uses
[Inline Array Annotation](https://docs.angularjs.org/guide/di#inline-array-annotation)
and is compatible with
[Using Strict Dependency Injection](https://docs.angularjs.org/guide/di#using-strict-dependency-injection)

Closes #12767
2015-09-25 11:26:35 +02:00
Martin Staffa 6208b76afa chore(docs): update to lunr-0.5.12
This improves the search results for certain terms.
For example, previously guide/scope was unfindable with the search
term 'scope', now it's the first hit.

Closes #12937
2015-09-25 11:09:33 +02:00
daviskoh 70dac5ae82 fix($parse): fix typo in error message ("assing" -> "assign")
Closes #12940
2015-09-25 10:56:29 +03:00
Martin Staffa aafbd94439 docs(ngModel): align $viewValue description with $setViewValue 2015-09-24 21:14:50 +02:00
Matias Niemelä e732f8e579 docs($animateCss): options.transition should be options.transitionStyle 2015-09-24 10:07:14 -07:00
Matias Niemelä 9f67da6252 feat($animateCss): add support for temporary styles via cleanupStyles
Some animations make use of the `from` and `to` styling only for the
lifetime of the animation. This patch allows for those styles to be
removed once the animation is closed automatically within `$animateCss`.

Closes #12930
2015-09-24 09:55:43 -07:00
Igor Minar b3a3c6a72e build(travis): make sauce connect process query a bit more specific 2015-09-23 14:00:24 -07:00
Igor Minar e8cdabe129 build(travis): fix typo in a comment 2015-09-23 14:00:23 -07:00
Martin Staffa 3af71bee75 Revert "refactor(ngModel): move model -> view update from watchFn to watchAction"
This reverts commit 862c9d8bb2. The
commit was accidentially pushed. Sorry for the noise.
2015-09-23 19:14:43 +02:00
Igor Minar f2724b2bbc build(travis): gracefully shut down the sauce connect tunnel after the tests are done running
This is to prevent sauce connect tunnel leaks.

Closes #12921
2015-09-23 09:40:03 -07:00
Martin Staffa 38500669f6 docs(ngList): whitespace -> newline 2015-09-23 17:35:06 +02:00
Martin Staffa 862c9d8bb2 refactor(ngModel): move model -> view update from watchFn to watchAction 2015-09-23 13:39:42 +02:00
Matias Niemelä 240d5896ec fix(ngAnimate): ensure anchoring uses body as a container when needed
Prior to this fix anchoring would allow for a container to be a document
node or something higher beyond the body tag. This patch makes it fall
back to body incase the rootElement node exists as a parent ancestor.

Closes #12872
2015-09-22 12:49:28 -07:00
Matias Niemelä 64ef084b91 revert: chore(core): introduce $$body service
Relying on the body node to be present right at injection has
caused issues with unit testing as well as some animations on
the body element. Reverting this patch fixes these issues.

Closes #12874
2015-09-22 12:47:21 -07:00
Matias Niemelä 8b27c3f064 fix(ngAnimate): callback detection should only use RAF when necessary
Callbacks are detected within the internals of ngAnimate whenever an
animation starts and ends. In order to allow the user to set callbacks
the callback detection needs to happen during the next tick. Prior to
this fix we used $$rAF to do the tick detection, however, with this
patch we intelligently use $$postDigest to do that for us and then
only issue a call to `$$rAF` if necessary.
2015-09-22 11:01:33 -07:00
Peter Bacon Darwin 8366622bed fix(ngMessages): prevent race condition with ngAnimate
If `ngMessage` tried to add a message back in that was about to be removed
after an animation, the NgMessageController got confused and tried to detach
the newly added message, when the pending node was destroyed.

This change applies a unique `attachId` to the message object and its DOM
node when it is attached. This is then checked when a DOM node is being
destroyed to prevent unwanted calls to `detach`.

Closes #12856
Closes #12903
2015-09-22 17:54:04 +01:00
Georgios Kalpakas d3de0066b0 chore(check-node-modules): make check/reinstall node_modules work across platforms
The previous implementations (based on shell scripts) threw errors on
Windows, because it was not able to `rm -rf` 'node_modules' (due to the
255 character limit in file-paths).

This implementation works consistently across platforms and is heavily based on
'https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js'.

Fixes #11143
Closes #11353

Closes #12792
2015-09-22 15:46:13 +03:00
Martin Staffa b7e5133b2e docs(guide/Directives): fix link formatting
Closes #12909;
2015-09-22 13:11:39 +02:00
Martin Staffa 42c97c5db5 fix(ngOptions): prevent frozen select ui in IE
In certain scenarios, IE10/11/Edge create unresponsive select elements.
The following contribute to the bug:
- There need to be at least 2 selects next to each other
- The option elements are added via javascript
- the option.value is accessed before it is set
- the option.label is added after the option.value has been set
- The first select is wrappend in an element with display: inline or
display: inline-block,

This cannot be tested in a unit-test or e2e test.

Closes #11314
Closes #11795
2015-09-22 12:58:41 +02:00
Lucas Galfaso e1f4f23f78 fix($parse): block assigning to fields of a constructor
Throw when assigning to a field of a constructor.

Closes #12860
2015-09-22 10:43:43 +01:00
Peter Bacon Darwin 630280c7fb feat(ngModel): provide ng-empty and ng-not-empty CSS classes
If the `$viewValue` is empty then the `ng-empty` CSS class is applied
to the input. Conversely, if it is not empty the `ng-not-empty` CSS class
is applied. Emptiness is ascertained by calling `NgModelController.$isEmpty()`

Closes #10050
Closes #12848
2015-09-22 10:16:49 +01:00
Peter Bacon Darwin 496a67c10e test(ngModel): remove jankiness test
It is no longer appropriate to test this here as $animate takes care of
queueing up CSS class changes into a single update.
2015-09-22 10:16:13 +01:00
Lucas Mirelmann 7dcfe5e03e revert: feat($time): create time service
This reverts commit fa4c7b7f1d.
2015-09-21 21:08:37 +02:00
Ivan Verevkin 9e83b8355e docs($cacheFactory): fix call to isUndefined() in example
Closes #12899
2015-09-21 15:20:50 +03:00
Shahar Talmi 03726f7fbd fix(injector): support arrow functions with no parenthesis
with arrow functions parenthesis are optional in case you have exactly
one argument to the function. the previous regexp assumed function
arguments are always inside parenthesis and so it didn't annotate
functions like `$http => $http.get(...)` correctly

Closes #12890
2015-09-21 08:49:19 +03:00
Shahar Talmi 75893ae9e7 chore(karma): upgrade chrome in saucelabs to latest 2015-09-21 08:49:06 +03:00
Shahar Talmi d9ca245917 refactor(injector): extract common code to function 2015-09-21 08:49:06 +03:00
Shahar Talmi 3259aabdf3 chore(npm): upgrade grunt-jscs 2015-09-21 08:49:06 +03:00
Lucas Mirelmann befeeb3689 style(*): small style fixes 2015-09-20 16:46:03 +02:00
Kent C. Dodds 79577c5d31 feat($injector): add strictDi property to $injector instance
Add a strictDi property which is true or false when creating an instance of an injector.

Closes #11728
Closes #11734
2015-09-20 16:41:27 +02:00
Peter Bacon Darwin b71d7c3f3c fix(ngOptions): allow falsy values as option group identifiers
Now one can use `''`, `0`, `false` and `null` as option groups. Previously
all of these falsy values were treated as the option not being a member of
a group.

Closes #7015
Closes #7024
Closes #12888

BREAKING CHANGES
If your data contains falsy values for option groups, then these options
will now be placed into option groups. Only option groups that are `undefined`
will result in the option being put in no group. If you have data that
contains falsy values that should not be used as groups then you must
filter the values before passing them to `ngOptions` converting falsy
values to `undefined`.
2015-09-20 16:40:36 +02:00
Marcin Wosinek fa4c7b7f1d feat($time): create time service
Add simple $time service to allow easier mocking dates in applications.

Closes #10402
Closes #10525
2015-09-20 16:37:50 +02:00
Daniel Herman 652b83eb22 perf($compile): Lazily compile the transclude function
For transcluded directives, the transclude function can be lazily compiled
most of the time since the contents will not be needed until the
`transclude` function was actually invoked.  For example, the `transclude`
function that is passed to `ng-if` or `ng-switch-when` does not need to be
invoked until the condition that it's bound to has been matched.  For
complex trees or switch statements, this can represent significant
performance gains since compilation of branches is deferred, and that
compilation may never actually happen if it isn't needed.

There are two instances where compilation will not be lazy; when we scan
ahead in the array of directives to be processed and find at least two of
the following:

* A directive that is transcluded and does not allow multiple transclusion
* A directive that has templateUrl and replace: true
* A directive that has a template and replace: true

In both of those cases, we will need to continue eager compilation in
order to generate the multiple transclusion exception at the correct time.
2015-09-19 22:32:59 +02:00
Lucas Mirelmann 20cf7d5e3a fix($parse): do not convert to string computed properties multiple times
Do not convert to string properties multiple times.
2015-09-19 22:19:14 +02:00
Martin Staffa ded2518756 fix(ngOptions): throw if ngModel is not present
Closes #7047
Closes #12840

BREAKING CHANGE:
`ngOptions` will now throw if `ngModel` is not present on the `select`
element. Previously, having no `ngModel` let `ngOptions` silently
fail, which could lead to hard to debug errors. The change should
therefore not affect any applications, as it simply makes the
requirement more strict and alerts the developer explicitly.
2015-09-19 15:26:42 +02:00
Martin Staffa b366f0352a fix(input): remove workaround for Firefox bug
The bug in question has been fixed in FF 38, which is also the
current ESR, so it's safe to remove the workaround: https://bugzilla.mozilla.org/show_bug.cgi?id=1064430
2015-09-19 12:24:17 +02:00
Jason Bedard 76c2491a31 fix($compile): use createMap() for $$observe listeners when initialized from attr interpolation
Closes #10446
2015-09-19 08:46:55 +01:00
Mark Knell ef3df93afe docs(guide/Unit Testing): tighter English
Leaner, more forceful style.

Fixes a grammatical problems with subject number doesn't agrees with the verb. (originally "We have built many features into
Angular which makes...", which reduces to "features...which makes").

More authoritative use of commas, such as the Oxford comma in lists of three or more.

Sentences that are sentences, not fragments. (Yes, that was a fragment just now. I'm not writing docs now that reflect the polish of the project.)

The English grammatical/stylistic notion of parallelism.

Etc. This is just polish. The ideas are fine.

Closes #10478
2015-09-18 17:11:15 -04:00
Sjur Bakka 106f90aafa feat($http): add $xhrFactory service to enable creation of custom xhr objects
Closes #2318
Closes #9319
Closes #12159
2015-09-18 19:43:26 +01:00
Igor Minar 86e8088b38 test($sanitize): add a test to prove that html comments are being stripped
Closes #12524
2015-09-18 19:38:43 +01:00
Igor Minar a654bdfed9 refactor(): rename local variables to improve code clarity
Closes #12524
2015-09-18 19:38:38 +01:00
Igor Minar 2c22c57e58 refactor($sanitize): remove <script> from valid block elements
the script and style tag are explicitly blacklisted, so this doesn't change any functionality.
the change is done to improve code clarity.

Closes #12524
2015-09-18 19:38:32 +01:00
Igor Minar a4dfa4d061 fix($sanitize): strip urls starting with 'unsafe:' as opposed to 'unsafe'
Closes #12524
2015-09-18 19:38:25 +01:00
Igor Minar bc0d8c4eea fix($sanitize): add mXSS protection
Closes #12524
2015-09-18 19:38:18 +01:00
Igor Minar 9b84fcad76 chore(travis): disable browserstack builds for now
we don't really pay attention to them anyway.

Closes #12524
2015-09-18 19:38:12 +01:00
Igor Minar f33ce173c9 fix($compile): properly sanitize xlink:href attribute interoplation
Closes #12524
2015-09-18 19:38:05 +01:00
Igor Minar 181fc567d8 feat($sanitize): make svg support an opt-in
Closes #12524

BREAKING CHANGE: The svg support in  is now an opt-in option

Applications that depend on this option can use  to turn the option back on,
but while doing so, please read the warning provided in the  documentation for
information on preventing click-hijacking attacks when this option is turned on.
2015-09-18 19:37:59 +01:00
Igor Minar 94207f8fb6 fix($sanitize): support void elements, fixups, remove dead code, typos
Closes #12524
2015-09-18 19:37:45 +01:00
Misko Hevery 35a21532b7 refactor($sanitize): new implementation of the html sanitized parser
This implementation is based on using inert document parsed by the browser

Closes #11442
Closes #11443
Closes #12524
2015-09-18 19:37:29 +01:00
Alessandro Diaferia 1c97a6057b fix(ngResource): encode & in URL query param values
Closes #12201
2015-09-18 16:32:04 +03:00
Peter Bacon Darwin 658a865c5b fix(filters): ensure formatNumber observes i18n decimal separators
Closes #10342
Closes #12850
2015-09-18 13:45:01 +01:00
847 changed files with 27528 additions and 2464 deletions
+12 -11
View File
@@ -19,10 +19,10 @@ env:
- JOB=docs-e2e BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
- JOB=unit BROWSER_PROVIDER=browserstack
- JOB=docs-e2e BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
# - JOB=unit BROWSER_PROVIDER=browserstack
# - JOB=docs-e2e BROWSER_PROVIDER=browserstack
# - JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
# - JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
global:
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
@@ -31,12 +31,12 @@ env:
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready
matrix:
allow_failures:
- env: "JOB=unit BROWSER_PROVIDER=browserstack"
- env: "JOB=docs-e2e BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack"
#matrix:
# allow_failures:
# - env: "JOB=unit BROWSER_PROVIDER=browserstack"
# - env: "JOB=docs-e2e BROWSER_PROVIDER=browserstack"
# - env: "JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack"
# - env: "JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack"
install:
# Check the size of caches
@@ -48,7 +48,7 @@ install:
- npm config set loglevel http
- npm install -g npm@2.5
# Instal npm dependecies and ensure that npm cache is not stale
- scripts/npm/install-dependencies.sh
- npm install
before_script:
- mkdir -p $LOGS_DIR
@@ -61,6 +61,7 @@ script:
- ./scripts/travis/build.sh
after_script:
- ./scripts/travis/tear_down_browser_provider.sh
- ./scripts/travis/print_logs.sh
notifications:
+772 -5
View File
@@ -1,3 +1,717 @@
<a name="1.5.0-rc.0"></a>
# 1.5.0-rc.0 oblong-panoptikum (2015-12-09)
This is the first Release Candidate for AngularJS 1.5.0. Please try upgrading your applications and
report any regressions or other issues you find as soon as possible.
## Features
- **$parse:** provide a mechanism to access the locals object, `$locals`
([0ea53503](https://github.com/angular/angular.js/commit/0ea535035a3a1a992948490c3533bffb83235052))
- **$resource:** add proper support for cancelling requests, `$cancelRequest()`
([98528be3](https://github.com/angular/angular.js/commit/98528be311b48269ba0e15ba4e3e2ad9b89693a9),
[#9332](https://github.com/angular/angular.js/issues/9332), [#13050](https://github.com/angular/angular.js/issues/13050), [#13058](https://github.com/angular/angular.js/issues/13058), [#13210](https://github.com/angular/angular.js/issues/13210))
- **ngAnimate:** provide ng-[event]-prepare class for structural animations
([6e18b50a](https://github.com/angular/angular.js/commit/6e18b50a5b168848cc526081b0a2a16075ee44bd))
- **ngLocale:** add support for standalone months
([96c73a06](https://github.com/angular/angular.js/commit/96c73a0672f0e46ae9285c482b057bd03ce135ba),
[#3744](https://github.com/angular/angular.js/issues/3744), [#10247](https://github.com/angular/angular.js/issues/10247), [#12642](https://github.com/angular/angular.js/issues/12642), [#12844](https://github.com/angular/angular.js/issues/12844))
- **ngMock:** destroy $rootScope after each test
([b75c0d8d](https://github.com/angular/angular.js/commit/b75c0d8d0549261ece551210a11d8be48c3ab3cc),
[#13433](https://github.com/angular/angular.js/issues/13433))
- **ngTransclude:** don't overwrite the contents with an unfilled optional slot
([0812af49](https://github.com/angular/angular.js/commit/0812af49bd4f4fad4067603ff64dbe720bd6e3e5),
[#13426](https://github.com/angular/angular.js/issues/13426))
- **ngView:** reference resolved locals in scope, `resolveAs: '$resolve'`
([983b0598](https://github.com/angular/angular.js/commit/983b0598121a8c5a3a51a30120e114d7e3085d4d),
[#13400](https://github.com/angular/angular.js/issues/13400))
## Bug Fixes
- **$compile:**
- swap keys and values for transclude definition object
([c3a26911](https://github.com/angular/angular.js/commit/c3a2691115b92536fb3d213d0ca16ac68cf32415),
[#13439](https://github.com/angular/angular.js/issues/13439))
- include non-elements in default transclusion slot
([df6fade6](https://github.com/angular/angular.js/commit/df6fade6e67cfbfb5295bab3703ba2054d48daa7))
- support merging special attribute names in `replace` directives
([a5ff651a](https://github.com/angular/angular.js/commit/a5ff651a59933c2c43b81642454ee458f98e1401),
[#13317](https://github.com/angular/angular.js/issues/13317), [#13318](https://github.com/angular/angular.js/issues/13318))
- **$http:** throw if url passed is not a string
([6628b4f1](https://github.com/angular/angular.js/commit/6628b4f1e5835d997290881c6ba394547883a516),
[#12925](https://github.com/angular/angular.js/issues/12925), [#13444](https://github.com/angular/angular.js/issues/13444))
- **$parse:**
- prevent assignment on constructor properties
([5a674f3b](https://github.com/angular/angular.js/commit/5a674f3bb9d1118d11b333e3b966c01a571c09e6),
[#13417](https://github.com/angular/angular.js/issues/13417))
- handle interceptors with `undefined` expressions
([4473b81c](https://github.com/angular/angular.js/commit/4473b81cdaf16c5509ac53d80b9bdfb0a7ac5f30))
- **$sanitize:** blacklist SVG `<use>` elements
([7a668cdd](https://github.com/angular/angular.js/commit/7a668cdd7d08a7016883eb3c671cbcd586223ae8),
[#13453](https://github.com/angular/angular.js/issues/13453))
- **formatNumber:** cope with large and small number corner cases
([6a0686d4](https://github.com/angular/angular.js/commit/6a0686d434c41445c50b2d9669073802ede77b3b),
[#13394](https://github.com/angular/angular.js/issues/13394), [#8674](https://github.com/angular/angular.js/issues/8674), [#12709](https://github.com/angular/angular.js/issues/12709), [#8705](https://github.com/angular/angular.js/issues/8705), [#12707](https://github.com/angular/angular.js/issues/12707), [#10246](https://github.com/angular/angular.js/issues/10246), [#10252](https://github.com/angular/angular.js/issues/10252))
- **input:** add missing chars to URL validation regex
([e4bb8387](https://github.com/angular/angular.js/commit/e4bb8387952069cca9da06bbc5c87ae576c2bf6f),
[#13379](https://github.com/angular/angular.js/issues/13379), [#13460](https://github.com/angular/angular.js/issues/13460))
- **ngAnimate:**
- consider options.delay value for closing timeout
([7ffb2d3c](https://github.com/angular/angular.js/commit/7ffb2d3c17643303a51eb4e324c365af70fe3824),
[#13355](https://github.com/angular/angular.js/issues/13355), [#13363](https://github.com/angular/angular.js/issues/13363))
- ensure animate runner is the same with and without animations
([546a277d](https://github.com/angular/angular.js/commit/546a277d65a3e075178d9b6d7ea6abcebc4bc04b),
[#13205](https://github.com/angular/angular.js/issues/13205), [#13347](https://github.com/angular/angular.js/issues/13347))
- ignore children without animation data when closing them
([77419cf1](https://github.com/angular/angular.js/commit/77419cf19fe625b262e971d5453151c63ff52b34),
[#11992](https://github.com/angular/angular.js/issues/11992), [#13424](https://github.com/angular/angular.js/issues/13424))
- do not alter the provided options data
([193153c3](https://github.com/angular/angular.js/commit/193153c3d391338a859cb7788ef32a8af05fb920),
[#13040](https://github.com/angular/angular.js/issues/13040), [#13175](https://github.com/angular/angular.js/issues/13175))
- **ngMock:** clear out `$providerInjector` after each test
([a72c12bd](https://github.com/angular/angular.js/commit/a72c12bd7052da9f60da74625409374342b50b73),
[#13397](https://github.com/angular/angular.js/issues/13397), [#13416](https://github.com/angular/angular.js/issues/13416))
- **ngOptions:** don't $dirty multiple select after compilation
([c7a2028a](https://github.com/angular/angular.js/commit/c7a2028ab38cdfc4d956c50b6f41cbccef302165),
[#13211](https://github.com/angular/angular.js/issues/13211), [#13326](https://github.com/angular/angular.js/issues/13326))
- **ngTransclude:**
- don't replace existing content if no transcluded content exists
([c3ae6ed7](https://github.com/angular/angular.js/commit/c3ae6ed78e145a9b0c13de7ef95852ba3c467551),
[#11839](https://github.com/angular/angular.js/issues/11839))
- fix case where ngTransclude attribute value equals its key
([7ddbc9aa](https://github.com/angular/angular.js/commit/7ddbc9aa35119154acb649e8c5096babc1d43476),
[#12934](https://github.com/angular/angular.js/issues/12934), [#13383](https://github.com/angular/angular.js/issues/13383))
## Breaking Changes
- **$compile:** due to [c3a26911](https://github.com/angular/angular.js/commit/c3a2691115b92536fb3d213d0ca16ac68cf32415),
**This is only a breaking change to a feature that was added in beta 2. If you have not started
using multi-slot transclusion then this will not affect you.**
The keys and values for the `transclude` map of the directive definition have been swapped around
to be more consistent with the other maps, such as `scope` and `bindToController`.
Now the `key` is the slot name and the `value` is a normalized element selector.
- **$resource:** due to [98528be3](https://github.com/angular/angular.js/commit/98528be311b48269ba0e15ba4e3e2ad9b89693a9),
Using a promise as `timeout` is no longer supported and will log a
warning. It never worked the way it was supposed to anyway.
Before:
```js
var deferred = $q.defer();
var User = $resource('/api/user/:id', {id: '@id'}, {
get: {method: 'GET', timeout: deferred.promise}
});
var user = User.get({id: 1}); // sends a request
deferred.resolve(); // aborts the request
// Now, we need to re-define `User` passing a new promise as `timeout`
// or else all subsequent requests from `someAction` will be aborted
User = $resource(...);
user = User.get({id: 2});
```
After:
```js
var User = $resource('/api/user/:id', {id: '@id'}, {
get: {method: 'GET', cancellable: true}
});
var user = User.get({id: 1}); // sends a request
instance.$cancelRequest(); // aborts the request
user = User.get({id: 2});
```
- **$sanitize:** due to [7a668cdd](https://github.com/angular/angular.js/commit/7a668cdd7d08a7016883eb3c671cbcd586223ae8),
The $sanitize service will now remove instances of the `<use>` tag from the content passed to it.
This element is used to import external SVG resources, which is a security risk as the `$sanitize`
service does not have access to the resource in order to sanitize it.
- **ngView:** due to [983b0598](https://github.com/angular/angular.js/commit/983b0598121a8c5a3a51a30120e114d7e3085d4d),
A new property to access route resolves is now available on the scope of the route. The default name
for this property is `$resolve`. If your scope already contains a property with this name then it
will be hidden or overwritten.
In this case, you should choose a custom name for this property, that does not collide with other
properties on the scope, by specifying the `resolveAs` property on the route.
- **$parse:** due to [0ea53503](https://github.com/angular/angular.js/commit/0ea535035a3a1a992948490c3533bffb83235052),
A new property to access all the locals for an expression is now available on the scope. This property
is `$locals`.
* If `scope.$locals` already exists, the way to reference this property is now `this.$locals`.
* If the locals themselves include a property `$locals` then the way to reference that is now `$locals.$locals`.
<a name="1.4.8"></a>
# 1.4.8 ice-manipulation (2015-11-19)
## Bug Fixes
- **$animate:** ensure leave animation calls `close` callback
([6bd6dbff](https://github.com/angular/angular.js/commit/6bd6dbff4961a601c03e9465442788781d329ba6),
[#12278](https://github.com/angular/angular.js/issues/12278), [#12096](https://github.com/angular/angular.js/issues/12096), [#13054](https://github.com/angular/angular.js/issues/13054))
- **$cacheFactory:** check key exists before decreasing cache size count
([2a5a52a7](https://github.com/angular/angular.js/commit/2a5a52a76ccf60c6e8c5d881e90e11a2666a6d3c),
[#12321](https://github.com/angular/angular.js/issues/12321), [#12329](https://github.com/angular/angular.js/issues/12329))
- **$compile:**
- bind all directive controllers correctly when using `bindToController`
([5d8861fb](https://github.com/angular/angular.js/commit/5d8861fb2f203e8a688b6044cbd1140cd79fd049),
[#11343](https://github.com/angular/angular.js/issues/11343), [#11345](https://github.com/angular/angular.js/issues/11345))
- evaluate against the correct scope with bindToController on new scope
([b9f7c453](https://github.com/angular/angular.js/commit/b9f7c453e00d6938106f414952f74d5e5fdcb993),
[#13021](https://github.com/angular/angular.js/issues/13021), [#13025](https://github.com/angular/angular.js/issues/13025))
- fix scoping of transclusion directives inside replace directive
([74da0340](https://github.com/angular/angular.js/commit/74da03407782d679951cd8f693860cea214f2580),
[#12975](https://github.com/angular/angular.js/issues/12975), [#12936](https://github.com/angular/angular.js/issues/12936), [#13244](https://github.com/angular/angular.js/issues/13244))
- **$http:** apply `transformResponse` even when `data` is empty
([c6909464](https://github.com/angular/angular.js/commit/c690946469e09cfe6b774e63dbe14ace92ce6cb7),
[#12976](https://github.com/angular/angular.js/issues/12976), [#12979](https://github.com/angular/angular.js/issues/12979))
- **$location:** ensure `$locationChangeSuccess` fires even if URL ends with `#`
([6f8ddb6d](https://github.com/angular/angular.js/commit/6f8ddb6d4329441e8d4a856978413aa9b9bd918f),
[#12175](https://github.com/angular/angular.js/issues/12175), [#13251](https://github.com/angular/angular.js/issues/13251))
- **$parse:** evaluate once simple expressions only once
([e4036824](https://github.com/angular/angular.js/commit/e403682444fa08af4f3491badf2f3a10d7595699),
[#12983](https://github.com/angular/angular.js/issues/12983), [#13002](https://github.com/angular/angular.js/issues/13002))
- **$resource:** allow XHR request to be cancelled via a timeout promise
([7170f9d9](https://github.com/angular/angular.js/commit/7170f9d9ca765c578f8d3eb4699860a9330a0a11),
[#12657](https://github.com/angular/angular.js/issues/12657), [#12675](https://github.com/angular/angular.js/issues/12675), [#10890](https://github.com/angular/angular.js/issues/10890), [#9332](https://github.com/angular/angular.js/issues/9332))
- **$rootScope:** prevent IE9 memory leak when destroying scopes
([87b0055c](https://github.com/angular/angular.js/commit/87b0055c80f40589c5bcf3765e59e872bcfae119),
[#10706](https://github.com/angular/angular.js/issues/10706), [#11786](https://github.com/angular/angular.js/issues/11786))
- **Angular.js:** fix `isArrayLike` for unusual cases
([70edec94](https://github.com/angular/angular.js/commit/70edec947c7b189694ae66b129568182e3369cab),
[#10186](https://github.com/angular/angular.js/issues/10186), [#8000](https://github.com/angular/angular.js/issues/8000), [#4855](https://github.com/angular/angular.js/issues/4855), [#4751](https://github.com/angular/angular.js/issues/4751), [#10272](https://github.com/angular/angular.js/issues/10272))
- **isArrayLike:** handle jQuery objects of length 0
([d3da55c4](https://github.com/angular/angular.js/commit/d3da55c40f1e1ddceced5da51e364888ff9d82ff))
- **jqLite:**
- deregister special `mouseenter` / `mouseleave` events correctly
([22f66025](https://github.com/angular/angular.js/commit/22f66025db262417ebb78c1ce1f4d7058dca3fd3),
[#12795](https://github.com/angular/angular.js/issues/12795), [#12799](https://github.com/angular/angular.js/issues/12799))
- ensure mouseenter works with svg elements on IE
([c1f34e8e](https://github.com/angular/angular.js/commit/c1f34e8eeb5105767f6cbf4727b8c5664be2a261),
[#10259](https://github.com/angular/angular.js/issues/10259), [#10276](https://github.com/angular/angular.js/issues/10276))
- **limitTo:** start at 0 if `begin` is negative and exceeds input length
([4fc40bc9](https://github.com/angular/angular.js/commit/4fc40bc9320a1d5902e648b70fa79c7cf7e794c7),
[#12775](https://github.com/angular/angular.js/issues/12775), [#12781](https://github.com/angular/angular.js/issues/12781))
- **merge:**
- ensure that jqlite->jqlite and DOM->DOM
([2f8db1bf](https://github.com/angular/angular.js/commit/2f8db1bf01173b546a2868fc7b8b188c2383fbff))
- clone elements instead of treating them like simple objects
([838cf4be](https://github.com/angular/angular.js/commit/838cf4be3c671903796dbb69d95c0e5ac1516a06),
[#12286](https://github.com/angular/angular.js/issues/12286))
- **ngAria:** don't add tabindex to radio and checkbox inputs
([59f1f4e1](https://github.com/angular/angular.js/commit/59f1f4e19a02e6e6f4c41c15b0e9f3372d85cecc),
[#12492](https://github.com/angular/angular.js/issues/12492), [#13095](https://github.com/angular/angular.js/issues/13095))
- **ngInput:** change URL_REGEXP to better match RFC3987
([cb51116d](https://github.com/angular/angular.js/commit/cb51116dbd225ccfdbc9a565a66a170e65d26331),
[#11341](https://github.com/angular/angular.js/issues/11341), [#11381](https://github.com/angular/angular.js/issues/11381))
- **ngMock:** reset cache before every test
([91b7cd9b](https://github.com/angular/angular.js/commit/91b7cd9b74d72a48d844c5c3e0e9dee03405e0ca),
[#13013](https://github.com/angular/angular.js/issues/13013))
- **ngOptions:**
- skip comments and empty options when looking for options
([0f58334b](https://github.com/angular/angular.js/commit/0f58334b7b9a9d3d6ff34e9754961b6f67731fae),
[#12190](https://github.com/angular/angular.js/issues/12190), [#13029](https://github.com/angular/angular.js/issues/13029), [#13033](https://github.com/angular/angular.js/issues/13033))
- override select option registration to allow compilation of empty option
([7b2ecf42](https://github.com/angular/angular.js/commit/7b2ecf42c697eb8d51a0f2d73b324bd900139e05),
[#11685](https://github.com/angular/angular.js/issues/11685), [#12972](https://github.com/angular/angular.js/issues/12972), [#12968](https://github.com/angular/angular.js/issues/12968), [#13012](https://github.com/angular/angular.js/issues/13012))
## Performance Improvements
- **$compile:** use static jquery data method to avoid creating new instances
([55ad192e](https://github.com/angular/angular.js/commit/55ad192e4ab79295ab15ecaaf8f6b9e7932a0336))
- **copy:**
- avoid regex in `isTypedArray`
([19fab4a1](https://github.com/angular/angular.js/commit/19fab4a1d79d2445795273f1622344353cf4d104))
- only validate/clear if the user specifies a destination
([d1293540](https://github.com/angular/angular.js/commit/d1293540e13573eb9ea5f90730bb9c9710c345db),
[#12068](https://github.com/angular/angular.js/issues/12068))
- **merge:** remove unnecessary wrapping of jqLite element
([ce6a96b0](https://github.com/angular/angular.js/commit/ce6a96b0d76dd2e5ab2247ca3059d284575bc6f0),
[#13236](https://github.com/angular/angular.js/issues/13236))
## Breaking Changes
<a name="1.5.0-beta.2"></a>
# 1.5.0-beta.2 effective-delegation (2015-11-17)
## Bug Fixes
- **$animate:** ensure leave animation calls `close` callback
([bfad2a4f](https://github.com/angular/angular.js/commit/bfad2a4f4ae71cfead61c112b0d2ab1fcadd39ee),
[#12278](https://github.com/angular/angular.js/issues/12278), [#12096](https://github.com/angular/angular.js/issues/12096), [#13054](https://github.com/angular/angular.js/issues/13054))
- **$cacheFactory:** check key exists before decreasing cache size count
([b9bed7d9](https://github.com/angular/angular.js/commit/b9bed7d9dadb4ba1a4186f2ae562f807b21bcf12),
[#12321](https://github.com/angular/angular.js/issues/12321), [#12329](https://github.com/angular/angular.js/issues/12329))
- **$compile:**
- bind all directive controllers correctly when using `bindToController`
([bd7b2177](https://github.com/angular/angular.js/commit/bd7b2177291697a665e4068501b3704200972467),
[1c13a4f4](https://github.com/angular/angular.js/commit/1c13a4f45ddc86805a96576b75c969ad577b6274)
[#11343](https://github.com/angular/angular.js/issues/11343), [#11345](https://github.com/angular/angular.js/issues/11345))
- evaluate against the correct scope with bindToController on new scope
([50557a6c](https://github.com/angular/angular.js/commit/50557a6cd329e8438fb5694d11e8a7d018142afe),
[#13021](https://github.com/angular/angular.js/issues/13021), [#13025](https://github.com/angular/angular.js/issues/13025))
- fix scoping of transclusion directives inside a replace directive
([1a98c0ee](https://github.com/angular/angular.js/commit/1a98c0ee346b718b9462da1abf4352a4605cbc7f),
[#12975](https://github.com/angular/angular.js/issues/12975), [#12936](https://github.com/angular/angular.js/issues/12936), [#13244](https://github.com/angular/angular.js/issues/13244))
- use createMap() for $$observe listeners when initialized from attr interpolation
([76c2491a](https://github.com/angular/angular.js/commit/76c2491a316d6b296c721227529fcb09087d369a),
[#10446](https://github.com/angular/angular.js/issues/10446))
- properly sanitize xlink:href attribute interoplation
([f33ce173](https://github.com/angular/angular.js/commit/f33ce173c90736e349cf594df717ae3ee41e0f7a),
[#12524](https://github.com/angular/angular.js/issues/12524))
- **$http:** apply `transformResponse` even when `data` is empty
([7c0731ed](https://github.com/angular/angular.js/commit/7c0731edb2f72bdf0efa186f641dab3b6aecc5d5),
[#12976](https://github.com/angular/angular.js/issues/12976), [#12979](https://github.com/angular/angular.js/issues/12979))
- **$location:** ensure `$locationChangeSuccess` fires even if URL ends with `#`
([4412fe23](https://github.com/angular/angular.js/commit/4412fe238f37f79a2017ee7b20ba089c0acd73e9),
[#12175](https://github.com/angular/angular.js/issues/12175), [#13251](https://github.com/angular/angular.js/issues/13251))
- **$parse:**
- evaluate simple expressions in interpolations only once
([1caf0b6b](https://github.com/angular/angular.js/commit/1caf0b6bee5781589e20f7a27a8c60e8b1b784f5),
[#12983](https://github.com/angular/angular.js/issues/12983), [#13002](https://github.com/angular/angular.js/issues/13002))
- fix typo in error message ("assing" -> "assign")
([70dac5ae](https://github.com/angular/angular.js/commit/70dac5ae82ffe9c6250681274905583747523b5d),
[#12940](https://github.com/angular/angular.js/issues/12940))
- block assigning to fields of a constructor
([e1f4f23f](https://github.com/angular/angular.js/commit/e1f4f23f781a79ae8a4046b21130283cec3f2917),
[#12860](https://github.com/angular/angular.js/issues/12860))
- safer conversion of computed properties to strings
([20cf7d5e](https://github.com/angular/angular.js/commit/20cf7d5e3a0af766b1929e24794859c79439351c))
- **$resource:** allow XHR request to be cancelled via a timeout promise
([4fc73466](https://github.com/angular/angular.js/commit/4fc734665e5dddef26ed30a9d4f75632cd269481),
[#12657](https://github.com/angular/angular.js/issues/12657), [#12675](https://github.com/angular/angular.js/issues/12675), [#10890](https://github.com/angular/angular.js/issues/10890), [#9332](https://github.com/angular/angular.js/issues/9332))
- **$rootScope:** prevent IE9 memory leak when destroying scopes
([8fe781fb](https://github.com/angular/angular.js/commit/8fe781fbe7c42c64eb895c28d9fd5479b037d020),
[#10706](https://github.com/angular/angular.js/issues/10706), [#11786](https://github.com/angular/angular.js/issues/11786))
- **$sanitize:**
- strip urls starting with 'unsafe:' as opposed to 'unsafe'
([a4dfa4d0](https://github.com/angular/angular.js/commit/a4dfa4d061fd2f6baf9821f0863dcce7888232ab),
[#12524](https://github.com/angular/angular.js/issues/12524))
- add mXSS protection
([bc0d8c4e](https://github.com/angular/angular.js/commit/bc0d8c4eea9a34bff5e29dd492dcdd668251be40),
[#12524](https://github.com/angular/angular.js/issues/12524))
- support void elements, fixups, remove dead code, typos
([94207f8f](https://github.com/angular/angular.js/commit/94207f8fb6ee8fe26fe18657f6b5aca6def99605),
[#12524](https://github.com/angular/angular.js/issues/12524))
- **Angular.js:** fix `isArrayLike` for unusual cases
([2c8d87e0](https://github.com/angular/angular.js/commit/2c8d87e064dca99a49ed35d1db885b1f2e40dcf4),
[#10186](https://github.com/angular/angular.js/issues/10186), [#8000](https://github.com/angular/angular.js/issues/8000), [#4855](https://github.com/angular/angular.js/issues/4855), [#4751](https://github.com/angular/angular.js/issues/4751), [#10272](https://github.com/angular/angular.js/issues/10272))
- **filters:** ensure `formatNumber` observes i18n decimal separators
([658a865c](https://github.com/angular/angular.js/commit/658a865c5b2580eed53b340e7394945cd76e2260),
[#10342](https://github.com/angular/angular.js/issues/10342), [#12850](https://github.com/angular/angular.js/issues/12850))
- **injector:** support arrow functions with no parentheses
([03726f7f](https://github.com/angular/angular.js/commit/03726f7fbd5d71c0604b8dd40e97cb2fb0fb777f),
[#12890](https://github.com/angular/angular.js/issues/12890))
- **input:** remove workaround for Firefox bug
([b366f035](https://github.com/angular/angular.js/commit/b366f0352abccfe4c4868b5a9e8c0b88659bd1ee))
- **isArrayLike:** handle jQuery objects of length 0
([773efd08](https://github.com/angular/angular.js/commit/773efd0812097a89944c889c595485a5744326f6))
- **jqLite:**
- deregister special `mouseenter` / `mouseleave` events correctly
([f5aa2079](https://github.com/angular/angular.js/commit/f5aa207960e0df577284a06a4353e2b53b159589),
[#12795](https://github.com/angular/angular.js/issues/12795), [#12799](https://github.com/angular/angular.js/issues/12799))
- ensure mouseenter works with svg elements on IE
([941c1c35](https://github.com/angular/angular.js/commit/941c1c35f175c36171a8855323f086341ea55711),
[#10259](https://github.com/angular/angular.js/issues/10259), [#10276](https://github.com/angular/angular.js/issues/10276))
- **limitTo:** start at 0 if `begin` is negative and exceeds input length
([ecf93048](https://github.com/angular/angular.js/commit/ecf9304811a0fd54289a35b9c3b715a1d4447806),
[#12775](https://github.com/angular/angular.js/issues/12775), [#12781](https://github.com/angular/angular.js/issues/12781))
- **merge:**
- ensure that jqlite->jqlite and DOM->DOM
([75292a6c](https://github.com/angular/angular.js/commit/75292a6cb5e17d618902f7996e80eb3118eff7b0))
- clone elements instead of treating them like simple objects
([17715fa3](https://github.com/angular/angular.js/commit/17715fa3668b1fcabaedcd82e2e57b2a80e0a0c2),
[#12286](https://github.com/angular/angular.js/issues/12286))
- **ngAnimate:**
- ensure anchoring uses body as a container when needed
([240d5896](https://github.com/angular/angular.js/commit/240d5896ecdfac2351f9bd6147b52de52c0b7608),
[#12872](https://github.com/angular/angular.js/issues/12872))
- callback detection should only use RAF when necessary
([8b27c3f0](https://github.com/angular/angular.js/commit/8b27c3f064b34532ba99d709cadf09fc4c0cbeab))
- **ngAria:** don't add tabindex to radio and checkbox inputs
([662fb282](https://github.com/angular/angular.js/commit/662fb282c176ca00a85b6dec7af90446ea90f662),
[#12492](https://github.com/angular/angular.js/issues/12492), [#13095](https://github.com/angular/angular.js/issues/13095))
- **ngInput:** change URL_REGEXP to better match RFC3987
([ffb6b2fb](https://github.com/angular/angular.js/commit/ffb6b2fb56d9ffcb051284965dd538629ea9687a),
[#11341](https://github.com/angular/angular.js/issues/11341), [#11381](https://github.com/angular/angular.js/issues/11381))
- **ngMessage:** make ngMessage compatible with ngBind
([4971ef12](https://github.com/angular/angular.js/commit/4971ef12d4c2c268cb8d26f90385dc96eba19db8),
[#8089](https://github.com/angular/angular.js/issues/8089), [#13074](https://github.com/angular/angular.js/issues/13074))
- **ngMessages:** prevent race condition with ngAnimate
([8366622b](https://github.com/angular/angular.js/commit/8366622bed009d2cad7d0cff28b9c1e48bfbd4e1),
[#12856](https://github.com/angular/angular.js/issues/12856), [#12903](https://github.com/angular/angular.js/issues/12903))
- **ngMock:** reset cache before every test
([fd83d372](https://github.com/angular/angular.js/commit/fd83d3724ad30a93254f08cb82f981eaddb5dbff),
[#13013](https://github.com/angular/angular.js/issues/13013))
- **ngOptions:**
- skip comments and empty options when looking for options
([395f3ec6](https://github.com/angular/angular.js/commit/395f3ec638f2ee77d22889823aa80898a6ce812d),
[#12190](https://github.com/angular/angular.js/issues/12190), [#13029](https://github.com/angular/angular.js/issues/13029), [#13033](https://github.com/angular/angular.js/issues/13033))
- override select option registration to allow compilation of empty option
([2fcfd75a](https://github.com/angular/angular.js/commit/2fcfd75a142200e1a4b1b7ed4fb588e3befcbd57),
[#11685](https://github.com/angular/angular.js/issues/11685), [#12972](https://github.com/angular/angular.js/issues/12972), [#12968](https://github.com/angular/angular.js/issues/12968), [#13012](https://github.com/angular/angular.js/issues/13012))
- prevent frozen select ui in IE
([42c97c5d](https://github.com/angular/angular.js/commit/42c97c5db5921e9e5447fb32bdae1f48da42844f),
[#11314](https://github.com/angular/angular.js/issues/11314), [#11795](https://github.com/angular/angular.js/issues/11795))
- allow falsy values as option group identifiers
([b71d7c3f](https://github.com/angular/angular.js/commit/b71d7c3f3c04e65b02d88b33c22dd90ae3cdfc27),
[#7015](https://github.com/angular/angular.js/issues/7015), [#7024](https://github.com/angular/angular.js/issues/7024), [#12888](https://github.com/angular/angular.js/issues/12888))
- throw if ngModel is not present
([ded25187](https://github.com/angular/angular.js/commit/ded2518756d4409fdfda0d4af243f2125bea01b5),
[#7047](https://github.com/angular/angular.js/issues/7047), [#12840](https://github.com/angular/angular.js/issues/12840))
- **ngResource:** encode `&` in URL query param values
([1c97a605](https://github.com/angular/angular.js/commit/1c97a6057bc013262be761bca5e5c22224c4bbf8),
[#12201](https://github.com/angular/angular.js/issues/12201))
- **orderByFilter:** throw error if input is not array-like
([2a85a634](https://github.com/angular/angular.js/commit/2a85a634f86c84f15b411ce009a3515fca7ba580),
[#11255](https://github.com/angular/angular.js/issues/11255), [#11719](https://github.com/angular/angular.js/issues/11719))
## Features
- **$animateCss:** add support for temporary styles via `cleanupStyles`
([9f67da62](https://github.com/angular/angular.js/commit/9f67da625293441e27559ebde7503cc63408a95c),
[#12930](https://github.com/angular/angular.js/issues/12930))
- **$compile:** multiple transclusion via named slots
([a4ada8ba](https://github.com/angular/angular.js/commit/a4ada8ba9c4358273575e16778e76446ad080054),
[#4357](https://github.com/angular/angular.js/issues/4357), [#12742](https://github.com/angular/angular.js/issues/12742), [#11736](https://github.com/angular/angular.js/issues/11736), [#12934](https://github.com/angular/angular.js/issues/12934))
- **$http:** add `$xhrFactory` service to enable creation of custom xhr objects
([106f90aa](https://github.com/angular/angular.js/commit/106f90aafa0fa5a81ad7af7ffc9d1e00ab97ffef),
[#2318](https://github.com/angular/angular.js/issues/2318), [#9319](https://github.com/angular/angular.js/issues/9319), [#12159](https://github.com/angular/angular.js/issues/12159))
- **$injector:**
- Allow specifying a decorator on $injector
([29a05984](https://github.com/angular/angular.js/commit/29a05984fe46c2c18ca51404f07c866dd92d1eec))
- add strictDi property to $injector instance
([79577c5d](https://github.com/angular/angular.js/commit/79577c5d316c7bf0204d7d1747ddc5b15bfe2955),
[#11728](https://github.com/angular/angular.js/issues/11728), [#11734](https://github.com/angular/angular.js/issues/11734))
- **$sanitize:** make svg support an opt-in
([181fc567](https://github.com/angular/angular.js/commit/181fc567d873df065f1e84af7225deb70a8d2eb9),
[#12524](https://github.com/angular/angular.js/issues/12524))
- **$templateRequest:** support configuration of $http options
([b2fc39d2](https://github.com/angular/angular.js/commit/b2fc39d2ddac64249b4f2961ee18b878a1e98251),
[#13188](https://github.com/angular/angular.js/issues/13188), [#11868](https://github.com/angular/angular.js/issues/11868), [#6860](https://github.com/angular/angular.js/issues/6860))
- **Module:** add helper method, `component(...)` for creating component directives
([54e81655](https://github.com/angular/angular.js/commit/54e816552f20e198e14f849cdb2379fed8570c1a),
[#10007](https://github.com/angular/angular.js/issues/10007), [#12933](https://github.com/angular/angular.js/issues/12933))
- **linky:** add support for custom attributes
([06f002b1](https://github.com/angular/angular.js/commit/06f002b161f61079933d482668440d8649fd84fc),
[#12558](https://github.com/angular/angular.js/issues/12558), [#13061](https://github.com/angular/angular.js/issues/13061))
- **ngAnimate:** introduce ngAnimateSwap directive
([78297d25](https://github.com/angular/angular.js/commit/78297d252de7c80f73ecf9e291ed71bd52578361))
- **ngMock:**
- add expectRoute and whenRoute shortcuts with colon param matching
([d67e999d](https://github.com/angular/angular.js/commit/d67e999dfbdf47b79fdb3830a04f4f4010a98b98),
[#12406](https://github.com/angular/angular.js/issues/12406))
- invoke nested calls to `module()` immediately
([51a27c0f](https://github.com/angular/angular.js/commit/51a27c0f1ad6cd8d3e33ab0d71de22c1627c7ec3),
[#12887](https://github.com/angular/angular.js/issues/12887))
- **ngModel:** provide ng-empty and ng-not-empty CSS classes
([630280c7](https://github.com/angular/angular.js/commit/630280c7fb04a83208d09c97c2efb81be3a3db74),
[#10050](https://github.com/angular/angular.js/issues/10050), [#12848](https://github.com/angular/angular.js/issues/12848))
## Performance Improvements
- **$compile:**
- use static jquery data method to avoid creating new instances
([9b90c32f](https://github.com/angular/angular.js/commit/9b90c32f31fd56e348539674128acec6536cd846))
- lazily compile the `transclude` function
([652b83eb](https://github.com/angular/angular.js/commit/652b83eb226131d131a44453520a569202aa4aac))
- **$interpolate:** provide a simplified result for constant expressions
([cf83b4f4](https://github.com/angular/angular.js/commit/cf83b4f445d3a1fc18fc140e65e670754401d50b))
- **copy:**
- avoid regex in `isTypedArray`
([c8768d12](https://github.com/angular/angular.js/commit/c8768d12f2f0b31f9ac971aeac6d2c17c9ff3db5))
- only validate/clear if the user specifies a destination
([33c67ce7](https://github.com/angular/angular.js/commit/33c67ce785cf8be7f0c294b3942ca4a337c5759d),
[#12068](https://github.com/angular/angular.js/issues/12068))
- **merge:** remove unnecessary wrapping of jqLite element
([4daafd3d](https://github.com/angular/angular.js/commit/4daafd3dbe6a80d578f5a31df1bb99c77559543e),
[#13236](https://github.com/angular/angular.js/issues/13236))
## Breaking Changes
- **$sanitize:** due to [181fc567](https://github.com/angular/angular.js/commit/181fc567d873df065f1e84af7225deb70a8d2eb9),
The svg support in is now an opt-in option
Applications that depend on this option can use to turn the option back on,
but while doing so, please read the warning provided in the documentation for
information on preventing click-hijacking attacks when this option is turned on.
- **ngMessage:** due to [4971ef12](https://github.com/angular/angular.js/commit/4971ef12d4c2c268cb8d26f90385dc96eba19db8),
ngMessage is now compiled with a priority of 1, which means directives
on the same element as ngMessage with a priority lower than 1 will
be applied when ngMessage calls the $transclude function.
Previously, they were applied during the initial compile phase and were
passed the comment element created by the transclusion of ngMessage.
To restore this behavior, custom directives need to have
their priority increased to at least "1".
- **ngOptions:** due to [ded25187](https://github.com/angular/angular.js/commit/ded2518756d4409fdfda0d4af243f2125bea01b5),
`ngOptions` will now throw if `ngModel` is not present on the `select`
element. Previously, having no `ngModel` let `ngOptions` silently
fail, which could lead to hard to debug errors. The change should
therefore not affect any applications, as it simply makes the
requirement more strict and alerts the developer explicitly.
- **orderByFilter:** due to [2a85a634](https://github.com/angular/angular.js/commit/2a85a634f86c84f15b411ce009a3515fca7ba580),
Previously, an non array-like input would pass through the orderBy filter unchanged.
Now, an error is thrown. This can be worked around by converting an object
to an array, either manually or using a filter such as
https://github.com/petebacondarwin/angular-toArrayFilter.
(`null` and `undefined` still pass through without an error, in order to
support asynchronous loading of resources.)
Closes #11255
Closes #11719
<a name="1.5.0-beta.1"></a>
# 1.5.0-beta.1 dense-dispersion (2015-09-29)
## Bug Fixes
- **$compile:**
- use createMap() for $$observe listeners when initialized from attr interpolation
([76c2491a](https://github.com/angular/angular.js/commit/76c2491a316d6b296c721227529fcb09087d369a),
[#10446](https://github.com/angular/angular.js/issues/10446))
- properly sanitize xlink:href attribute interoplation
([f33ce173](https://github.com/angular/angular.js/commit/f33ce173c90736e349cf594df717ae3ee41e0f7a),
[#12524](https://github.com/angular/angular.js/issues/12524))
- **$parse:**
- fix typo in error message ("assing" -> "assign")
([70dac5ae](https://github.com/angular/angular.js/commit/70dac5ae82ffe9c6250681274905583747523b5d),
[#12940](https://github.com/angular/angular.js/issues/12940))
- block assigning to fields of a constructor
([e1f4f23f](https://github.com/angular/angular.js/commit/e1f4f23f781a79ae8a4046b21130283cec3f2917),
[#12860](https://github.com/angular/angular.js/issues/12860))
- do not convert to string computed properties multiple times
([20cf7d5e](https://github.com/angular/angular.js/commit/20cf7d5e3a0af766b1929e24794859c79439351c))
- **$sanitize:**
- strip urls starting with 'unsafe:' as opposed to 'unsafe'
([a4dfa4d0](https://github.com/angular/angular.js/commit/a4dfa4d061fd2f6baf9821f0863dcce7888232ab),
[#12524](https://github.com/angular/angular.js/issues/12524))
- add mXSS protection
([bc0d8c4e](https://github.com/angular/angular.js/commit/bc0d8c4eea9a34bff5e29dd492dcdd668251be40),
[#12524](https://github.com/angular/angular.js/issues/12524))
- support void elements, fixups, remove dead code, typos
([94207f8f](https://github.com/angular/angular.js/commit/94207f8fb6ee8fe26fe18657f6b5aca6def99605),
[#12524](https://github.com/angular/angular.js/issues/12524))
- **filters:** ensure `formatNumber` observes i18n decimal separators
([658a865c](https://github.com/angular/angular.js/commit/658a865c5b2580eed53b340e7394945cd76e2260),
[#10342](https://github.com/angular/angular.js/issues/10342), [#12850](https://github.com/angular/angular.js/issues/12850))
- **injector:** support arrow functions with no parenthesis
([03726f7f](https://github.com/angular/angular.js/commit/03726f7fbd5d71c0604b8dd40e97cb2fb0fb777f),
[#12890](https://github.com/angular/angular.js/issues/12890))
- **input:** remove workaround for Firefox bug
([b366f035](https://github.com/angular/angular.js/commit/b366f0352abccfe4c4868b5a9e8c0b88659bd1ee))
- **ngAnimate:**
- ensure anchoring uses body as a container when needed
([240d5896](https://github.com/angular/angular.js/commit/240d5896ecdfac2351f9bd6147b52de52c0b7608),
[#12872](https://github.com/angular/angular.js/issues/12872))
- callback detection should only use RAF when necessary
([8b27c3f0](https://github.com/angular/angular.js/commit/8b27c3f064b34532ba99d709cadf09fc4c0cbeab))
- **ngMessages:** prevent race condition with ngAnimate
([8366622b](https://github.com/angular/angular.js/commit/8366622bed009d2cad7d0cff28b9c1e48bfbd4e1),
[#12856](https://github.com/angular/angular.js/issues/12856), [#12903](https://github.com/angular/angular.js/issues/12903))
- **ngOptions:**
- skip comments when looking for option elements
([7f3f3dd3](https://github.com/angular/angular.js/commit/7f3f3dd3ebcc44711600ac292af54c411c3c705f),
[#12190](https://github.com/angular/angular.js/issues/12190))
- prevent frozen select ui in IE
([42c97c5d](https://github.com/angular/angular.js/commit/42c97c5db5921e9e5447fb32bdae1f48da42844f),
[#11314](https://github.com/angular/angular.js/issues/11314), [#11795](https://github.com/angular/angular.js/issues/11795))
- allow falsy values as option group identifiers
([b71d7c3f](https://github.com/angular/angular.js/commit/b71d7c3f3c04e65b02d88b33c22dd90ae3cdfc27),
[#7015](https://github.com/angular/angular.js/issues/7015), [#7024](https://github.com/angular/angular.js/issues/7024), [#12888](https://github.com/angular/angular.js/issues/12888))
- throw if ngModel is not present
([ded25187](https://github.com/angular/angular.js/commit/ded2518756d4409fdfda0d4af243f2125bea01b5),
[#7047](https://github.com/angular/angular.js/issues/7047), [#12840](https://github.com/angular/angular.js/issues/12840))
- **ngResource:** encode `&` in URL query param values
([1c97a605](https://github.com/angular/angular.js/commit/1c97a6057bc013262be761bca5e5c22224c4bbf8),
[#12201](https://github.com/angular/angular.js/issues/12201))
## Features
- **$animateCss:** add support for temporary styles via `cleanupStyles`
([9f67da62](https://github.com/angular/angular.js/commit/9f67da625293441e27559ebde7503cc63408a95c),
[#12930](https://github.com/angular/angular.js/issues/12930))
- **$http:** add `$xhrFactory` service to enable creation of custom xhr objects
([106f90aa](https://github.com/angular/angular.js/commit/106f90aafa0fa5a81ad7af7ffc9d1e00ab97ffef),
[#2318](https://github.com/angular/angular.js/issues/2318), [#9319](https://github.com/angular/angular.js/issues/9319), [#12159](https://github.com/angular/angular.js/issues/12159))
- **$injector:** add strictDi property to $injector instance
([79577c5d](https://github.com/angular/angular.js/commit/79577c5d316c7bf0204d7d1747ddc5b15bfe2955),
[#11728](https://github.com/angular/angular.js/issues/11728), [#11734](https://github.com/angular/angular.js/issues/11734))
- **$sanitize:** make svg support an opt-in
([181fc567](https://github.com/angular/angular.js/commit/181fc567d873df065f1e84af7225deb70a8d2eb9),
[#12524](https://github.com/angular/angular.js/issues/12524))
- **ngModel:** provide ng-empty and ng-not-empty CSS classes
([630280c7](https://github.com/angular/angular.js/commit/630280c7fb04a83208d09c97c2efb81be3a3db74),
[#10050](https://github.com/angular/angular.js/issues/10050), [#12848](https://github.com/angular/angular.js/issues/12848))
## Performance Improvements
- **$compile:** Lazily compile the `transclude` function
([652b83eb](https://github.com/angular/angular.js/commit/652b83eb226131d131a44453520a569202aa4aac))
## Breaking Changes
- **$sanitize:** due to [181fc567](https://github.com/angular/angular.js/commit/181fc567d873df065f1e84af7225deb70a8d2eb9),
The svg support in is now an opt-in option
Applications that depend on this option can use to turn the option back on,
but while doing so, please read the warning provided in the documentation for
information on preventing click-hijacking attacks when this option is turned on.
- **ngOptions:** due to [ded25187](https://github.com/angular/angular.js/commit/ded2518756d4409fdfda0d4af243f2125bea01b5),
`ngOptions` will now throw if `ngModel` is not present on the `select`
element. Previously, having no `ngModel` let `ngOptions` silently
fail, which could lead to hard to debug errors. The change should
therefore not affect any applications, as it simply makes the
requirement more strict and alerts the developer explicitly.
<a name="1.4.7"></a>
# 1.4.7 dark-luminescence (2015-09-29)
## Bug Fixes
- **$compile:** use createMap() for $$observe listeners when initialized from attr interpolation
([5a98e806](https://github.com/angular/angular.js/commit/5a98e806ef3c59916bb4668268125610b11effe8),
[#10446](https://github.com/angular/angular.js/issues/10446))
- **$parse:**
- block assigning to fields of a constructor
([a7f3761e](https://github.com/angular/angular.js/commit/a7f3761eda5309f76b73c6fb1d3173a270112899),
[#12860](https://github.com/angular/angular.js/issues/12860))
- do not convert to string computed properties multiple times
([698af191](https://github.com/angular/angular.js/commit/698af191ded2465ca4e0f97959b75fede5a531ab))
- **filters:** ensure `formatNumber` observes i18n decimal separators
([4994acd2](https://github.com/angular/angular.js/commit/4994acd26e582eec8a92b139bfc09ca79a9b8835),
[#10342](https://github.com/angular/angular.js/issues/10342), [#12850](https://github.com/angular/angular.js/issues/12850))
- **jqLite:** properly handle dash-delimited node names in `jqLiteBuildFragment`
([cdd1227a](https://github.com/angular/angular.js/commit/cdd1227a308edd34d31b67f338083b6e0c4c0db9),
[#10617](https://github.com/angular/angular.js/issues/10617), [#12759](https://github.com/angular/angular.js/issues/12759))
- **ngAnimate:**
- ensure anchoring uses body as a container when needed
([9d3704ca](https://github.com/angular/angular.js/commit/9d3704ca467081f16b71b011eb50c53d5cdb2f34),
[#12872](https://github.com/angular/angular.js/issues/12872))
- callback detection should only use RAF when necessary
([fa8c399f](https://github.com/angular/angular.js/commit/fa8c399fadc30b78710868fe59d2930fdc17c7a5))
- **ngMessages:** prevent race condition with ngAnimate
([7295c60f](https://github.com/angular/angular.js/commit/7295c60ffb9f2e4f32043c538ace740b187f565a),
[#12856](https://github.com/angular/angular.js/issues/12856), [#12903](https://github.com/angular/angular.js/issues/12903))
- **ngOptions:**
- skip comments when looking for option elements
([68d4dc5b](https://github.com/angular/angular.js/commit/68d4dc5b71b23e4c7c2650e6da3d7200de99f1ae),
[#12190](https://github.com/angular/angular.js/issues/12190))
- prevent frozen select ui in IE
([dbc69851](https://github.com/angular/angular.js/commit/dbc698517ff620b3a6279f65d4a9b6e3c15087b9),
[#11314](https://github.com/angular/angular.js/issues/11314), [#11795](https://github.com/angular/angular.js/issues/11795))
## Features
- **$animateCss:** add support for temporary styles via `cleanupStyles`
([e52d731b](https://github.com/angular/angular.js/commit/e52d731bfd1fbb6c616125fbde2fb365722254b7),
[#12930](https://github.com/angular/angular.js/issues/12930))
- **$http:** add `$xhrFactory` service to enable creation of custom xhr objects
([7a413df5](https://github.com/angular/angular.js/commit/7a413df5e47e04e20a1c93d35922050bbcbfb492),
[#2318](https://github.com/angular/angular.js/issues/2318), [#9319](https://github.com/angular/angular.js/issues/9319), [#12159](https://github.com/angular/angular.js/issues/12159))
## Breaking Changes
<a name="1.3.20"></a>
# 1.3.20 shallow-translucence (2015-09-29)
## Bug Fixes
- **$parse:** do not convert to string computed properties multiple times
([d434f3db](https://github.com/angular/angular.js/commit/d434f3db53d6209eb140b904e83bbde401686c16))
## Breaking Changes
<a name="1.2.29"></a>
# 1.2.29 ultimate-deprecation (2015-09-29)
## Bug Fixes
- **$browser:** prevent infinite digests when clearing the hash of a url
([9845cee6](https://github.com/angular/angular.js/commit/9845cee63eda3ad5024622c792c5e745b59ec5cb),
[#9629](https://github.com/angular/angular.js/issues/9629), [#9635](https://github.com/angular/angular.js/issues/9635), [#10228](https://github.com/angular/angular.js/issues/10228), [#10308](https://github.com/angular/angular.js/issues/10308))
- **$compile:** workaround for IE11 MutationObserver
([fccce96d](https://github.com/angular/angular.js/commit/fccce96d444f442ba5ecfb67201505a445d0c209),
[#11781](https://github.com/angular/angular.js/issues/11781), [#12613](https://github.com/angular/angular.js/issues/12613))
- **$location:** strip off empty hash segments when comparing
([e81b2f72](https://github.com/angular/angular.js/commit/e81b2f726cacd08bcf91500183a7ea3f71961718),
[#9635](https://github.com/angular/angular.js/issues/9635), [#10748](https://github.com/angular/angular.js/issues/10748))
- **$parse:**
- do not convert to string computed properties multiple times
([afb65c11](https://github.com/angular/angular.js/commit/afb65c11e5e3425f8431ad5b82308ff6b8ecbc64))
- throw error when accessing a restricted property indirectly
([e6cbd4fa](https://github.com/angular/angular.js/commit/e6cbd4faa211b2c0c8879c255e3194fb0717dcec),
[#12833](https://github.com/angular/angular.js/issues/12833))
- **ngAnimate:** ensure that minified repaint code isn't removed
([b041b664](https://github.com/angular/angular.js/commit/b041b664752e34a42bbc65e02bf0009f0836c50c),
[#9936](https://github.com/angular/angular.js/issues/9936))
## Breaking Changes
<a name="1.4.6"></a>
# 1.4.6 multiplicative-elevation (2015-09-17)
@@ -170,8 +884,6 @@ the built-in pattern validator:
```
<a name="1.4.5"></a>
# 1.4.5 permanent-internship (2015-08-28)
@@ -1340,7 +2052,6 @@ mechanism.
- **ngMessages:** due to [c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0),
The `ngMessagesInclude` attribute is now its own directive and that must
be placed as a **child** element within the element with the ngMessages
directive. (Keep in mind that the former behaviour of the
@@ -1363,6 +2074,26 @@ end of the container containing the ngMessages directive).
</div>
```
- **ngMessages:** due to [c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0),
it is no longer possible to use interpolation inside the `ngMessages` attribute expression. This technique
is generally not recommended, and can easily break when a directive implementation changes. In cases
where a simple expression is not possible, you can delegate accessing the object to a function:
```html
<div ng-messages="ctrl.form['field_{{$index}}'].$error">...</div>
```
would become
```html
<div ng-messages="ctrl.getMessages($index)">...</div>
```
where `ctrl.getMessages()`
```javascript
ctrl.getMessages = function($index) {
return ctrl.form['field_' + $index].$error;
}
```
- **$http:** due to [5da1256](https://github.com/angular/angular.js/commit/5da1256fc2812d5b28fb0af0de81256054856369),
`transformRequest` functions can no longer modify request headers.
@@ -1818,6 +2549,12 @@ But in practice this is not what people want and so this change iterates over pr
in the order they are returned by Object.keys(obj), which is almost always the order
in which the properties were defined.
- **ngOptions:** due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
setting the ngOptions attribute expression after the element is compiled, will no longer trigger the ngOptions behavior.
This worked previously because the ngOptions logic was part of the select directive, while
it is now implemented in the ngOptions directive itself.
- **select:** due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
@@ -2420,7 +3157,36 @@ We also added a documentation page focused on security, which contains some of t
[#9578](https://github.com/angular/angular.js/issues/9578), [#9751](https://github.com/angular/angular.js/issues/9751))
## Breaking Changes
- **$observe:** Due to [531a8de7](https://github.com/angular/angular.js/commit/531a8de72c439d8ddd064874bf364c00cedabb11),
observers no longer register on undefined attributes. For example, if you were using `$observe` on
an absent optional attribute to set a default value, the following would not work anymore:
```html
<my-dir></my-dir>
```
```js
// link function for directive myDir
link: function(scope, element, attr) {
attr.$observe('myAttr', function(newVal) {
scope.myValue = newVal ? newVal : 'myDefaultValue';
})
}
```
Instead, check if the attribute is set before registering the observer:
```js
link: function(scope, element, attr) {
if (attr.myAttr) {
// register the observer
} else {
// set the default
}
}
```
<a name="1.3.0"></a>
# 1.3.0 superluminal-nudge (2014-10-13)
@@ -4917,8 +5683,9 @@ See https://github.com/angular/angular.js/issues/10236 for an example.
- **angular.toJson:** due to [c054288c](https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251),
If you expected `toJson` to strip these types of properties before,
you will have to manually do this yourself now.
`toJson()` will no longer strip properties starting with a single `$`. If you relied on
`toJson()`'s stripping these types of properties before, you will have to do it manually now.
It will still strip properties starting with `$$` though.
+3 -1
View File
@@ -71,7 +71,7 @@ chances of your issue being dealt with quickly:
* **Angular Version(s)** - is it a regression?
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
* **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
[JSFiddle][jsfiddle]) or an unambiguous set of steps.
* **Related Issues** - has a similar issue been reported before?
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
causing the problem (line of code or commit)
@@ -187,6 +187,8 @@ We have very precise rules over how our git commit messages can be formatted. T
readable messages** that are easy to follow when looking through the **project history**. But also,
we use the git commit messages to **generate the AngularJS change log**.
The commit message formatting can be added using a typical git workflow or through the use of a CLI wizard ([Commitizen](https://github.com/commitizen/cz-cli)). To use the wizard, run `npm run commit` in your terminal after staging your changes in git.
### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
+1 -1
View File
@@ -305,7 +305,7 @@ module.exports = function(grunt) {
shell: {
"npm-install": {
command: path.normalize('scripts/npm/install-dependencies.sh')
command: 'node scripts/npm/check-node-modules.js'
},
"promises-aplus-tests": {
+1 -1
View File
@@ -21,7 +21,7 @@ piece of cake. Best of all?? It makes development fun!
Building AngularJS
---------
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
[Once you have your environment set up](http://docs.angularjs.org/misc/contribute) just run:
grunt package
+2 -2
View File
@@ -14,6 +14,7 @@ var angularFiles = {
'src/ng/anchorScroll.js',
'src/ng/animate.js',
'src/ng/animateRunner.js',
'src/ng/animateCss.js',
'src/ng/browser.js',
'src/ng/cacheFactory.js',
@@ -92,7 +93,6 @@ var angularFiles = {
'angularModules': {
'ngAnimate': [
'src/ngAnimate/shared.js',
'src/ngAnimate/body.js',
'src/ngAnimate/rafScheduler.js',
'src/ngAnimate/animateChildrenDirective.js',
'src/ngAnimate/animateCss.js',
@@ -100,8 +100,8 @@ var angularFiles = {
'src/ngAnimate/animateJs.js',
'src/ngAnimate/animateJsDriver.js',
'src/ngAnimate/animateQueue.js',
'src/ngAnimate/animateRunner.js',
'src/ngAnimate/animation.js',
'src/ngAnimate/ngAnimateSwap.js',
'src/ngAnimate/module.js'
],
'ngCookies': [
+92 -1
View File
@@ -315,8 +315,13 @@ iframe.example {
color:white;
}
.search-results-group .search-results {
padding: 0 5px 0;
list-style-type: none;
}
.search-results-frame > .search-results-group:first-child > .search-results {
border-right:1px solid #050505;
border-right:1px solid #222;
}
.search-results-group.col-group-api { width:30%; }
@@ -325,10 +330,57 @@ iframe.example {
.search-results-group.col-group-misc,
.search-results-group.col-group-error { width:15%; float: right; }
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group.col-group-api .search-results {
-moz-column-count: 2;
-ms-column-count: 2;
-webkit-column-count: 2;
column-count: 2;
/* Prevent bullets in the second column from being hidden in Chrome and IE */
-webkit-column-gap: 2em;
-ms-column-gap: 2em;
column-gap: 2em;
}
}
.search-results-group .search-result {
word-wrap: break-word;
-webkit-hyphens: auto;
-moz-hyphens: auto;
-ms-hyphens: auto;
hyphens: auto;
-ms-column-break-inside: avoid;
-webkit-column-break-inside: avoid;
-moz-column-break-inside: avoid; /* Unsupported */
column-break-inside: avoid;
text-indent: -0.65em; /* Make sure line wrapped words are aligned vertically */
}
@supports (-moz-column-count: 2) {
.search-results-group .search-result {
/* Prevents column breaks inside words in FF, but has adverse effects in IE11 and Chrome */
overflow: hidden;
padding-left: 1em; /* In FF the list item bullet is otherwise hidden */
margin-left: -1em; /* offset the padding left */
}
}
.search-result:before {
content: "\002D\00A0"; /* Dash and non-breaking space as List item type */
position: relative;
}
.search-results-group.col-group-api .search-result {
width:48%;
display:inline-block;
padding-left: 12px;
}
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group.col-group-api .search-result {
width:auto;
display: list-item;
}
}
.search-close {
@@ -589,6 +641,12 @@ ul.events > li {
vertical-align: top;
}
.table > tbody > tr.head > td,
.table > tbody > tr.head > th {
border-bottom: 2px solid #ddd;
padding-top: 50px;
}
@media only screen and (min-width: 769px) and (max-width: 991px) {
.main-body-grid {
margin-top: 160px;
@@ -682,6 +740,11 @@ ul.events > li {
padding-bottom:60px;
text-align:left;
}
.search-results-frame > .search-results-group:first-child > .search-results {
border-right: none;
}
.search-results-group {
float:none!important;
display:block!important;
@@ -689,14 +752,42 @@ ul.events > li {
border:0!important;
padding:0!important;
}
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group .search-results {
-moz-column-count: 2;
-ms-column-count: 2;
-webkit-column-count: 2;
column-count: 2;
}
}
.search-results-group .search-result {
display:inline-block!important;
padding:0 5px;
width:auto!important;
text-indent: initial;
margin-left: 0;
}
.search-results-group .search-result:after {
content:", ";
}
.search-results-group .search-result:before {
content: "";
}
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
.search-results-group .search-result {
display: list-item !important;
}
.search-results-group .search-result:after {
content: "";
}
}
#wrapper {
padding-bottom:0px;
}
+1 -1
View File
@@ -3,7 +3,7 @@
/* global importScripts, onmessage: true, postMessage, lunr */
// Load up the lunr library
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
// Create the lunr index - the docs should be an array of object, each object containing
// the path and search terms for a page
+9 -1
View File
@@ -11,7 +11,15 @@ angular.module('search', [])
var MIN_SEARCH_LENGTH = 2;
if(q.length >= MIN_SEARCH_LENGTH) {
docsSearch(q).then(function(hits) {
var results = {};
// Make sure the areas are always in the same order
var results = {
api: [],
guide: [],
tutorial: [],
error: [],
misc: []
};
angular.forEach(hits, function(hit) {
var area = hit.area;
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "AngularJS-docs-app",
"dependencies": {
"jquery": "2.1.1",
"lunr.js": "0.4.3",
"lunr.js": "0.5.12",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"bootstrap": "3.1.1"
@@ -147,13 +147,13 @@
<div class="search-results-container" ng-show="hasResults">
<div class="container">
<div class="search-results-frame">
<div ng-repeat="(key, value) in results" class="search-results-group" ng-class="colClassName + ' col-group-' + key">
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
<h4 class="search-results-group-heading">{{ key }}</h4>
<div class="search-results">
<div ng-repeat="item in value" class="search-result">
- <a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a>
</div>
</div>
<ul class="search-results">
<!-- Do not insert a line break between li and a. Chrome will insert an actual line-break, which breaks the list item view.
TODO: use a html minifier instead -->
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
</ul>
</div>
</div>
<a href="" ng-click="hideResults()" class="search-close">
+1 -1
View File
@@ -8,7 +8,7 @@ but the required directive controller is not present on the current DOM element
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.
If the required controller is expected to be on a ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
If the required controller is expected to be on an ancestor element, make sure that you prefix the controller name in the `require` definition with `^`.
If the required controller is optionally requested, use `?` or `^?` to specify that.
+2 -2
View File
@@ -1,6 +1,6 @@
@ngdoc error
@name $location:nobase
@fullName $location in HTML5 mode requires a <base> tag to be present!
@fullName $location in HTML5 mode requires a `<base>` tag to be present!
@description
If you configure {@link ng.$location `$location`} to use
@@ -15,7 +15,7 @@ $locationProvider.html5Mode({
});
```
Note that removing the requirement for a <base> tag will have adverse side effects when resolving
Note that removing the requirement for a `<base>` tag will have adverse side effects when resolving
relative paths with `$location` in IE9.
The base URL is then used to resolve all relative URLs throughout the application regardless of the
@@ -1,11 +0,0 @@
@ngdoc error
@name $sanitize:badparse
@fullName Parsing Error while Sanitizing
@description
This error occurs when the HTML string passed to '$sanitize' can't be parsed by the sanitizer.
The error contains part of the html string that can't be parsed.
The parser is more strict than a typical browser parser, so it's possible that some obscure input would produce this error despite the string being recognized as valid HTML by a browser.
If a valid html code results in this error, please file a bug.
@@ -0,0 +1,10 @@
@ngdoc error
@name $sanitize:noinert
@fullName Can't create an inert html document
@description
This error occurs when `$sanitize` sanitizer determines that `document.implementation.createHTMLDocument ` api is not supported by the current browser.
This api is necessary for safe parsing of HTML strings into DOM trees and without it the sanitizer can't sanitize the input.
The api is present in all supported browsers including IE 9.0, so the presence of this error usually indicates that Angular's `$sanitize` is being used on an unsupported platform.
+13
View File
@@ -0,0 +1,13 @@
@ngdoc error
@name $sanitize:uinput
@fullName Failed to sanitize html because the input is unstable
@description
This error occurs when `$sanitize` sanitizer tries to check the input for possible mXSS payload and the verification
errors due to the input mutating indefinitely. This could be a sign that the payload contains code exploiting an mXSS
vulnerability in the browser.
mXSS attack exploit browser bugs that cause some browsers parse a certain html strings into DOM, which once serialized
doesn't match the original input. These browser bugs can be exploited by attackers to create payload which looks
harmless to sanitizers, but due to mutations caused by the browser are turned into dangerous code once processed after
sanitization.
+52
View File
@@ -0,0 +1,52 @@
@ngdoc error
@name orderBy:notarray
@fullName Value is not array-like
@description
This error occurs when {@link ng.orderBy orderBy} is not passed an array-like value:
```html
<div ng-repeat="(key, value) in myObj | orderBy:someProp">
{{ key }} : {{ value }}
</div>
```
`orderBy` must be used with an array-like value so a subset of items can be returned.
The array can be initialized asynchronously and therefore `null` or `undefined` won't throw this error.
To use `orderBy` to order the properties of an object, you can create your own array based on that object:
```js
angular.module('aModule', [])
.controller('aController', function($scope) {
var myObj = {
one: {id: 1, name: 'Some thing'},
two: {id: 2, name: 'Another thing'},
three: {id: 3, name: 'A third thing'}
};
$scope.arrFromMyObj = Object.keys(myObj).map(function(key) {
return myObj[key];
});
});
```
That can be used as:
```html
<label>
Order by:
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
</label>
<div ng-repeat="item in arrFromMyObj | orderBy:orderProp">
[{{ item.id }}] {{ item.name }}
</div>
```
You could as well convert the object to an array using a filter such as
[toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter):
```html
<label>
Order by:
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
</label>
<div ng-repeat="item in myObj | toArray:false | orderBy:orderProp">
[{{ item.id }}] {{ item.name }}
</div>
```
+11 -9
View File
@@ -356,15 +356,15 @@ legacy browsers and hashbang links in modern browser:
### Example
Here you can see two `$location` instances, both in **Html5 mode**, but on different browsers, so
that you can see the differences. These `$location` services are connected to a fake browsers. Each
input represents the address bar of the browser.
Here you can see two `$location` instances that show the difference between **Html5 mode** and **Html5 Fallback mode**.
Note that to simulate different levels of browser support, the `$location` instances are connected to
a fakeBrowser service, which you don't have to set up in actual projects.
Note that when you type hashbang url into first browser (or vice versa) it doesn't rewrite /
Note that when you type hashbang url into the first browser (or vice versa) it doesn't rewrite /
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
= on page reload.
In these examples we use `<base href="/base/index.html" />`
In these examples we use `<base href="/base/index.html" />`. The inputs represent the address bar of the browser.
#### Browser in HTML5 mode
<example module="html5-mode" name="location-html5-mode">
@@ -389,6 +389,7 @@ In these examples we use `<base href="/base/index.html" />`
<file name="app.js">
angular.module('html5-mode', ['fake-browser', 'address-bar'])
// Configure the fakeBrowser. Do not set these values in actual projects.
.constant('initUrl', 'http://www.example.com/base/path?a=b#h')
.constant('baseHref', '/base/index.html')
.value('$sniffer', { history: true })
@@ -538,6 +539,7 @@ In these examples we use `<base href="/base/index.html" />`
<file name="app.js">
angular.module('hashbang-mode', ['fake-browser', 'address-bar'])
// Configure the fakeBrowser. Do not set these values in actual projects.
.constant('initUrl', 'http://www.example.com/base/index.html#!/path?a=b#h')
.constant('baseHref', '/base/index.html')
.value('$sniffer', { history: false })
@@ -769,8 +771,8 @@ then uses the information it obtains to compose hashbang URLs (such as
</tr>
<tr class="head">
<td>Navigation outside the app</td>
<td>Use lower level API</td>
<th>Navigation outside the app</td>
<th>Use lower level API</td>
</tr>
<tr>
@@ -784,8 +786,8 @@ then uses the information it obtains to compose hashbang URLs (such as
</tr>
<tr class="head">
<td>Read access</td>
<td>Change to</td>
<th>Read access</td>
<th>Change to</td>
</tr>
<tr>
+31
View File
@@ -274,6 +274,37 @@ myModule.directive('my-directive', ['$animate', function($animate) {
}]);
```
## Preventing flicker before an animation starts
When nesting elements with structural animations such as `ngIf` into elements that have class-based
animations such as `ngClass`, it sometimes happens that before the actual animation starts, there is a brief flicker or flash of content
where the animated element is briefly visible.
To prevent this, you can apply styles to the `ng-[event]-prepare` class, which is added as soon as an animation is initialized,
but removed before the actual animation starts (after waiting for a $digest). This class is only added for *structural*
animations (`enter`, `move`, and `leave`).
Here's an example where you might see flickering:
```html
<div ng-class="{red: myProp}">
<div ng-class="{blue: myProp}">
<div class="message" ng-if="myProp"></div>
</div>
</div>
```
It is possible that during the `enter` event, the `.message` div will be briefly visible before it starts animating.
In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
```css
.message.ng-enter-prepare {
opacity: 0;
}
/* Other animation styles ... */
```
## More about animations
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
+4 -5
View File
@@ -76,7 +76,7 @@ stores/updates the value of the input field into/from a variable.
The second kind of new markup are the double curly braces `{{ expression | filter }}`:
When the compiler encounters this markup, it will replace it with the evaluated value of the markup.
An <a name="expression">{@link expression expression}</a> in a template is a JavaScript-like code snippet that allows
to read and write variables. Note that those variables are not global variables.
Angular to read and write variables. Note that those variables are not global variables.
Just like variables in a JavaScript function live in a scope,
Angular provides a <a name="scope">{@link scope scope}</a> for the variables accessible to expressions.
The values that are stored in variables on the scope are referred to as the <a name="model"></a>*model*
@@ -334,9 +334,9 @@ The following example shows how this is done with Angular:
var refresh = function() {
var url = YAHOO_FINANCE_URL_PATTERN.
replace('PAIRS', 'USD' + currencies.join('","USD'));
return $http.jsonp(url).success(function(data) {
return $http.jsonp(url).then(function(response) {
var newUsdToForeignRates = {};
angular.forEach(data.query.results.rate, function(rate) {
angular.forEach(response.data.query.results.rate, function(rate) {
var currency = rate.id.substring(3,6);
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
});
@@ -348,8 +348,7 @@ The following example shows how this is done with Angular:
return {
currencies: currencies,
convert: convert,
refresh: refresh
convert: convert
};
}]);
</file>
+16 -7
View File
@@ -43,8 +43,7 @@ mirrors the process of compiling source code in
Before we can write a directive, we need to know how Angular's {@link guide/compiler HTML compiler}
determines when to use a given directive.
Similar to the terminology used when an [element **matches** a selector]
(https://developer.mozilla.org/en-US/docs/Web/API/Element.matches), we say an element **matches** a
Similar to the terminology used when an [element **matches** a selector](https://developer.mozilla.org/en-US/docs/Web/API/Element.matches), we say an element **matches** a
directive when the directive is part of its declaration.
In the following example, we say that the `<input>` element **matches** the `ngModel` directive
@@ -588,14 +587,24 @@ want to reuse throughout your app.
In this example we will build a directive that displays the current time.
Once a second, it updates the DOM to reflect the current time.
Directives that want to modify the DOM typically use the `link` option.
`link` takes a function with the following signature, `function link(scope, element, attrs) { ... }`
where:
Directives that want to modify the DOM typically use the `link` option to register DOM listeners
as well as update the DOM. It is executed after the template has been cloned and is where
directive logic will be put.
`link` takes a function with the following signature,
`function link(scope, element, attrs, controller, transcludeFn) { ... }`, where:
* `scope` is an Angular scope object.
* `element` is the jqLite-wrapped element that this directive matches.
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
corresponding attribute values.
* `controller` is the directive's required controller instance(s) or its own controller (if any).
The exact value depends on the directive's require property.
* `transcludeFn` is a transclude linking function pre-bound to the correct transclusion scope.
<div class="alert alert-info">
For more details on the `link` option refer to the {@link ng.$compile#-link- `$compile` API} page.
</div>
In our `link` function, we want to update the displayed time once a second, or whenever a user
changes the time formatting string that our directive binds to. We will use the `$interval` service
@@ -903,7 +912,7 @@ to which tab is active.
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope) {
controller: ['$scope', function($scope) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
@@ -919,7 +928,7 @@ to which tab is active.
}
panes.push(pane);
};
},
}],
templateUrl: 'my-tabs.html'
};
})
+6 -1
View File
@@ -35,7 +35,9 @@ Angular expressions are like JavaScript expressions with the following differenc
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
in an Angular expression.
* **No Comma And Void Operators:** You cannot use `,` or `void` in an Angular expression.
* **No Object Creation With New Operator:** You cannot use `new` operator in an Angular expression.
* **No Comma And Void Operators:** You cannot use `,` or `void` operators in an Angular expression.
* **Filters:** You can use {@link guide/filter filters} within expressions to format data before
displaying it.
@@ -113,6 +115,9 @@ This restriction is intentional. It prevents accidental access to the global sta
Instead use services like `$window` and `$location` in functions called from expressions. Such services
provide mockable access to globals.
It is possible to access the context object using the identifier `this` and the locals object using the
identifier `$locals`.
<example module="expressionExample">
<file name="index.html">
<div class="example2" ng-controller="ExampleController">
+1 -1
View File
@@ -383,7 +383,7 @@ In the following example we create two directives:
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
var usernames = ['Jim', 'John', 'Jill', 'Jackie'];
ctrl.$asyncValidators.username = function(modelValue, viewValue) {
+63 -4
View File
@@ -170,6 +170,25 @@ other inline messages situated as children within the `ngMessages` container dir
Depending on where the `ngMessagesInclude` directive is placed it will be prioritized inline with the other messages
before and after it.
Also due to [c9a4421f](https://github.com/angular/angular.js/commit/c9a4421fc3c97448527eadef1f42eb2f487ec2e0),
it is no longer possible to use interpolation inside the `ngMessages` attribute expression. This technique
is generally not recommended, and can easily break when a directive implementation changes. In cases
where a simple expression is not possible, you can delegate accessing the object to a function:
```html
<div ng-messages="ctrl.form['field_{{$index}}'].$error">...</div>
```
would become
```html
<div ng-messages="ctrl.getMessages($index)">...</div>
```
where `ctrl.getMessages()`
```javascript
ctrl.getMessages = function($index) {
return ctrl.form['field_' + $index].$error;
}
```
### ngOptions
The `ngOptions` directive has also been refactored and as a result some long-standing bugs
@@ -189,6 +208,10 @@ But in practice this is not what people want and so this change iterates over pr
in the order they are returned by Object.keys(obj), which is almost always the order
in which the properties were defined.
Also due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
setting the ngOptions attribute expression after the element is compiled, will no longer trigger the ngOptions behavior.
This worked previously because the ngOptions logic was part of the select directive, while
it is now implemented in the ngOptions directive itself.
### select
@@ -473,10 +496,9 @@ This change also makes our forEach behave more like Array#forEach.
- **angular.toJson:** due to [c054288c](https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251),
If you expected `toJson` to strip these types of properties before, you will have to
manually do this yourself now.
`toJson()` will no longer strip properties starting with a single `$`. If you relied on
`toJson()`'s stripping these types of properties before, you will have to do it manually now.
It will still strip properties starting with `$$` though.
@@ -561,6 +583,36 @@ After:
};
});
- due to [531a8de7](https://github.com/angular/angular.js/commit/531a8de72c439d8ddd064874bf364c00cedabb11),
`$observe` no longer registers on undefined attributes. For example, if you were using `$observe` on
an absent optional attribute to set a default value, the following would not work anymore:
```html
<my-dir></my-dir>
```
```js
// Link function for directive myDir
link: function(scope, element, attr) {
attr.$observe('myAttr', function(newVal) {
scope.myValue = newVal ? newVal : 'myDefaultValue';
})
}
```
Instead, check if the attribute is set before registering the observer:
```js
link: function(scope, element, attr) {
if (attr.myAttr) {
// register the observer
} else {
// set the default
}
}
```
@@ -633,8 +685,15 @@ $scope.resetWithCancel = function (e) {
[#5864](https://github.com/angular/angular.js/issues/5864))
- {@link input[checkbox] `input[checkbox]`} now supports constant expressions in `ngTrueValue` and
`ngFalseValue`, making it now possible to e.g. use boolean and integer values. Previously, these attributes would
always be treated as strings, whereas they are now parsed as expressions, and will throw if an expression
is non-constant. To convert non-constant strings into constant expressions, simply wrap them in an
extra pair of quotes, like so:
`<input type="checkbox" ng-model="..." ng-true-value="'truthyValue'">`
See [c90cefe1614](https://github.com/angular/angular.js/commit/c90cefe16142d973a123e945fc9058e8a874c357)
## Scopes and Digests (`$scope`)
+1 -1
View File
@@ -44,7 +44,7 @@ and {@link angular.reloadWithDebugInfo `angular.reloadWithDebugInfo`}.
## Strict DI Mode
Using strict di mode in your production application will throw errors when a injectable
Using strict di mode in your production application will throw errors when an injectable
function is not
{@link di#dependency-annotation annotated explicitly}. Strict di mode is intended to help
you make sure that your code will work when minified. However, it also will force you to
+2 -2
View File
@@ -391,7 +391,7 @@ implementing custom event callbacks, or when working with third-party library ca
5. The {@link ng.$rootScope.Scope#$watch $watch} list is a set of expressions
which may have changed since last iteration. If a change is detected then the `$watch`
function is called which typically updates the DOM with the new value.
6. Once the Angular {@link ng.$rootScope.Scope#$digest $digest} loop finishes
6. Once the Angular {@link ng.$rootScope.Scope#$digest $digest} loop finishes,
the execution leaves the Angular and JavaScript context. This is followed by the browser
re-rendering the DOM to reflect any changes.
@@ -419,4 +419,4 @@ user enters text into the text field.
which in turn updates the DOM.
6. Angular exits the execution context, which in turn exits the `keydown` event and with it
the JavaScript execution context.
7. The browser re-renders the view with update text.
7. The browser re-renders the view with the updated text.
+7 -7
View File
@@ -6,7 +6,7 @@
JavaScript is a dynamically typed language which comes with great power of expression, but it also
comes with almost no help from the compiler. For this reason we feel very strongly that any code
written in JavaScript needs to come with a strong set of tests. We have built many features into
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
Angular which make testing your Angular applications easy. With Angular, there is no excuse for not testing.
## Separation of Concerns
@@ -20,13 +20,13 @@ related pieces such as the DOM elements, or making any XHR calls to fetch the da
While this may seem obvious it can be very difficult to call an individual function on a
typical project. The reason is that the developers often mix concerns resulting in a
piece of code which does everything. It makes an XHR request, it sorts the response data and then it
piece of code which does everything. It makes an XHR request, it sorts the response data, and then it
manipulates the DOM.
With Angular we try to make it easy for you to do the right thing, and so we
provide dependency injection for your XHR requests, which can be mocked, and we provide abstractions which
allow you to test your model without having to resort to manipulating the DOM. The test can then
assert that the data has been sorted without having to create or look at the state of the DOM or
With Angular, we try to make it easy for you to do the right thing. For your XHR requests, we
provide dependency injection, so your requests can be simulated. For the DOM, we abstract it, so you can
test your model without having to manipulate the DOM directly. Your tests can then
assert that the data has been sorted without having to create or look at the state of the DOM or to
wait for any XHR requests to return data. The individual sort function can be tested in isolation.
## With great power comes great responsibility
@@ -359,7 +359,7 @@ element, to which it can then insert the transcluded content into its template.
Before compilation:
```html
<div translude-directive>
<div transclude-directive>
Some transcluded content
</div>
```
+5 -3
View File
@@ -6,12 +6,14 @@
<ul doc-tutorial-nav="0"></ul>
You are now ready to build the AngularJS phonecat app. In this step, you will become familiar
with the most important source code files, learn how to start the development servers bundled with
In this step of the tutorial, you will become familiar with the most important source code files of
the AngularJS phonecat app. You will also learn how to start the development servers bundled with
angular-seed, and run the application in the browser.
Before you continue, make sure you have set up your development environment and installed all necessary
dependencies, as described in {@link tutorial/index#get-started Get Started}.
In `angular-phonecat` directory, run this command:
In the `angular-phonecat` directory, run this command:
```
git checkout -f step-0
+15 -4
View File
@@ -61,7 +61,7 @@ by the value of the expressions.
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
__controller__ to the &lt;body&gt; tag. At this point:
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}`) denote
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
controller.
@@ -195,8 +195,19 @@ to ensure that Karma and its necessary plugins are installed. You can do this by
To run the tests, and then watch the files for changes: `npm test`.
* Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
the background. Karma will use this browser for test execution.
* Karma will start new instances of Chrome and Firefox browsers automatically. Just ignore them and
let them run in the background. Karma will use these browsers for test execution.
* If you only have one of the browsers installed on your machine (either Chrome or Firefox), make
sure to update the karma configuration file before running the test. Locate the configuration file
in `test/karma.conf.js`, then update the `browsers` property.
E.g. if you only have Chrome installed:
<pre>
...
browsers: ['Chrome'],
...
</pre>
* You should see the following or similar output in the terminal:
<pre>
@@ -250,7 +261,7 @@ browser is limited, which results in your karma tests running extremely slow.
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
Extra points: try and make an 8x8 table using an additional `ng-repeat`.
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
+2 -2
View File
@@ -37,12 +37,12 @@ We are using [Bower][bower] to install client-side dependencies. This step upda
"angular-mocks": "1.4.x",
"jquery": "~2.1.1",
"bootstrap": "~3.1.1",
"angular-route": "~1.4.0"
"angular-route": "1.4.x"
}
}
```
The new dependency `"angular-route": "~1.4.0"` tells bower to install a version of the
The new dependency `"angular-route": "1.4.x"` tells bower to install a version of the
angular-route component that is compatible with version 1.4.x. We must tell bower to download
and install this dependency.
+2 -2
View File
@@ -12,8 +12,8 @@ phone in the phone list.
* When you click on a phone on the list, the phone details page with phone-specific information
is displayed.
To implement the phone details view we used {@link ng.$http $http} to fetch our data, and we
fleshed out the `phone-detail.html` view template.
To implement the phone details view we are going to use {@link ng.$http $http} to fetch our data,
and then flesh out the `phone-detail.html` view template.
<div doc-tutorial-reset="8"></div>
+7 -6
View File
@@ -32,17 +32,18 @@ We are using [Bower][bower] to install client side dependencies. This step upda
"license": "MIT",
"private": true,
"dependencies": {
"angular": "~1.3.0",
"angular-mocks": "~1.3.0",
"angular": "1.4.x",
"angular-mocks": "1.4.x",
"jquery": "~2.1.1",
"bootstrap": "~3.1.1",
"angular-route": "~1.3.0",
"angular-resource": "~1.3.0"
"angular-route": "1.4.x",
"angular-resource": "1.4.x"
}
}
```
The new dependency `"angular-resource": "~1.3.0"` tells bower to install a version of the
angular-resource component that is compatible with version 1.3.x. We must ask bower to download
The new dependency `"angular-resource": "1.4.x"` tells bower to install a version of the
angular-resource component that is compatible with version 1.4.x. We must ask bower to download
and install this dependency. We can do this by running:
```
+13 -13
View File
@@ -36,20 +36,20 @@ We are using [Bower][bower] to install client side dependencies. This step upda
"license": "MIT",
"private": true,
"dependencies": {
"angular": "~1.3.0",
"angular-mocks": "~1.3.0",
"bootstrap": "~3.1.1",
"angular-route": "~1.3.0",
"angular-resource": "~1.3.0",
"angular": "1.4.x",
"angular-mocks": "1.4.x",
"jquery": "~2.1.1",
"angular-animate": "~1.3.0"
"bootstrap": "~3.1.1",
"angular-route": "1.4.x",
"angular-resource": "1.4.x",
"angular-animate": "1.4.x"
}
}
```
* `"angular-animate": "~1.3.0"` tells bower to install a version of the
angular-animate component that is compatible with version 1.3.x.
* `"jquery": "2.1.1"` tells bower to install the 2.1.1 version of jQuery. Note that this is not an
* `"angular-animate": "1.4.x"` tells bower to install a version of the
angular-animate component that is compatible with version 1.4.x.
* `"jquery": "~2.1.1"` tells bower to install the 2.1.1 version of jQuery. Note that this is not an
Angular library, it is the standard jQuery library. We can use bower to install a wide range of 3rd
party libraries.
@@ -111,7 +111,7 @@ __`app/index.html`.__
```
<div class="alert alert-error">
**Important:** Be sure to use jQuery version 2.1 or newer when using Angular 1.3; jQuery 1.x is
**Important:** Be sure to use jQuery version 2.1 or newer when using Angular 1.4; jQuery 1.x is
not officially supported.
Be sure to load jQuery before all AngularJS scripts, otherwise AngularJS won't detect jQuery and
animations will not work as expected.
@@ -239,9 +239,9 @@ The name of the starting class is the name of the event that is fired (like `ent
The active class name is the same as the starting class's but with an `-active` suffix.
This two-class CSS naming convention allows the developer to craft an animation, beginning to end.
In our example above, elements expand from a height of **0** to **120 pixels** when items are added or moved,
around and collapsing the items before removing them from the list.
There's also a nice fade-in and fade-out effect that also occurs at the same time. All of this is handled
In our example above, elements are expanded from a height of **0** to **120 pixels** when they're added to the
list and are collapsed back down to **0 pixels** before being removed from the list.
There's also a nice fade-in and fade-out effect that occurs at the same time. All of this is handled
by the CSS transition declarations at the top of the example code above.
Although most modern browsers have good support for [CSS transitions](http://caniuse.com/#feat=css-transitions)
+4
View File
@@ -11,6 +11,8 @@ function newTestLocaleInfo() {
DATETIME_FORMATS: {
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
'octobre', 'novembre', 'décembre'],
STANDALONEMONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
'octobre', 'novembre', 'décembre'],
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
'nov.', 'déc.'],
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
@@ -180,6 +182,8 @@ describe("extractDateTimeSymbols", function() {
DATETIME_FORMATS: {
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
'octobre', 'novembre', 'décembre'],
STANDALONEMONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',
'août', 'septembre', 'octobre', 'novembre', 'décembre'],
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
'nov.', 'déc.'],
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
+2
View File
@@ -27,6 +27,7 @@ describe("convertNumberData", function() {
describe("convertDatetimeData", function() {
var convert = converter.convertDatetimeData,
dataObj = { MONTHS: ['Enero', 'Pebrero'],
STANDALONEMONTHS: ['Enero', 'Pebrero'],
SHORTMONTHS: ['Ene', 'Peb'],
WEEKDAYS: ['Linggo', 'Lunes'],
SHORTWEEKDAYS: ['Lin', 'Lun'],
@@ -37,6 +38,7 @@ describe("convertDatetimeData", function() {
it('should convert empty datetime obj', function() {
var processedData = convert(dataObj);
expect(processedData.MONTH).toEqual(['Enero', 'Pebrero']);
expect(processedData.STANDALONEMONTH).toEqual(['Enero', 'Pebrero']);
expect(processedData.SHORTMONTH).toEqual(['Ene', 'Peb']);
expect(processedData.DAY).toEqual(['Linggo', 'Lunes']);
expect(processedData.SHORTDAY).toEqual(['Lin', 'Lun']);
+1
View File
@@ -39,6 +39,7 @@ function convertDatetimeData(dataObj) {
datetimeFormats.MONTH = dataObj.MONTHS;
datetimeFormats.SHORTMONTH = dataObj.SHORTMONTHS;
datetimeFormats.STANDALONEMONTH = dataObj.STANDALONEMONTHS;
datetimeFormats.DAY = dataObj.WEEKDAYS;
datetimeFormats.SHORTDAY = dataObj.SHORTWEEKDAYS;
datetimeFormats.AMPMS = dataObj.AMPMS;
+1 -1
View File
@@ -37,7 +37,7 @@ module.exports = function(config, specificOptions) {
'SL_Chrome': {
base: 'SauceLabs',
browserName: 'chrome',
version: '43'
version: '45'
},
'SL_Firefox': {
base: 'SauceLabs',
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -e -o pipefail
echo "Shutting down Browserstack tunnel"
echo "TODO: implement me"
exit 1
-309
View File
@@ -1,309 +0,0 @@
/*
* HTML Parser By John Resig (ejohn.org)
* Original code by Erik Arvidsson, Mozilla Public License
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*
* // Use like so:
* htmlParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* // or to get an XML string:
* HTMLtoXML(htmlString);
*
* // or to get an XML DOM Document
* HTMLtoDOM(htmlString);
*
* // or to inject into an existing document/DOM node
* HTMLtoDOM(htmlString, document);
* HTMLtoDOM(htmlString, document.body);
*
*/
(function(){
// Regular Expressions for parsing tags and attributes
var startTag = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
endTag = /^<\/(\w+)[^>]*>/,
attr = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
// Empty Elements - HTML 4.01
var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");
// Block Elements - HTML 4.01
var block = makeMap("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul");
// Inline Elements - HTML 4.01
var inline = makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
// Elements that you can, intentionally, leave open
// (and which close themselves)
var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
// Attributes that have their values filled in disabled="disabled"
var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
// Special Elements (can contain anything)
var special = makeMap("script,style");
var htmlParser = this.htmlParser = function( html, handler ) {
var index, chars, match, stack = [], last = html;
stack.last = function(){
return this[ this.length - 1 ];
};
while ( html ) {
chars = true;
// Make sure we're not in a script or style element
if ( !stack.last() || !special[ stack.last() ] ) {
// Comment
if ( html.indexOf("<!--") == 0 ) {
index = html.indexOf("-->");
if ( index >= 0 ) {
if ( handler.comment )
handler.comment( html.substring( 4, index ) );
html = html.substring( index + 3 );
chars = false;
}
// end tag
} else if ( html.indexOf("</") == 0 ) {
match = html.match( endTag );
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( endTag, parseEndTag );
chars = false;
}
// start tag
} else if ( html.indexOf("<") == 0 ) {
match = html.match( startTag );
if ( match ) {
html = html.substring( match[0].length );
match[0].replace( startTag, parseStartTag );
chars = false;
}
}
if ( chars ) {
index = html.indexOf("<");
var text = index < 0 ? html : html.substring( 0, index );
html = index < 0 ? "" : html.substring( index );
if ( handler.chars )
handler.chars( text );
}
} else {
html = html.replace(new RegExp("(.*)<\/" + stack.last() + "[^>]*>"), function(all, text){
text = text.replace(/<!--(.*?)-->/g, "$1")
.replace(/<!\[CDATA\[(.*?)]]>/g, "$1");
if ( handler.chars )
handler.chars( text );
return "";
});
parseEndTag( "", stack.last() );
}
if ( html == last )
throw "Parse Error: " + html;
last = html;
}
// Clean up any remaining tags
parseEndTag();
function parseStartTag( tag, tagName, rest, unary ) {
if ( block[ tagName ] ) {
while ( stack.last() && inline[ stack.last() ] ) {
parseEndTag( "", stack.last() );
}
}
if ( closeSelf[ tagName ] && stack.last() == tagName ) {
parseEndTag( "", tagName );
}
unary = empty[ tagName ] || !!unary;
if ( !unary )
stack.push( tagName );
if ( handler.start ) {
var attrs = [];
rest.replace(attr, function(match, name) {
var value = arguments[2] ? arguments[2] :
arguments[3] ? arguments[3] :
arguments[4] ? arguments[4] :
fillAttrs[name] ? name : "";
attrs.push({
name: name,
value: value,
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
});
});
if ( handler.start )
handler.start( tagName, attrs, unary );
}
}
function parseEndTag( tag, tagName ) {
// If no tag name is provided, clean shop
if ( !tagName )
var pos = 0;
// Find the closest opened tag of the same type
else
for ( var pos = stack.length - 1; pos >= 0; pos-- )
if ( stack[ pos ] == tagName )
break;
if ( pos >= 0 ) {
// Close all the open elements, up the stack
for ( var i = stack.length - 1; i >= pos; i-- )
if ( handler.end )
handler.end( stack[ i ] );
// Remove the open elements from the stack
stack.length = pos;
}
}
};
this.HTMLtoXML = function( html ) {
var results = "";
htmlParser(html, {
start: function( tag, attrs, unary ) {
results += "<" + tag;
for ( var i = 0; i < attrs.length; i++ )
results += " " + attrs[i].name + '="' + attrs[i].escaped + '"';
results += (unary ? "/" : "") + ">";
},
end: function( tag ) {
results += "</" + tag + ">";
},
chars: function( text ) {
results += text;
},
comment: function( text ) {
results += "<!--" + text + "-->";
}
});
return results;
};
this.HTMLtoDOM = function( html, doc ) {
// There can be only one of these elements
var one = makeMap("html,head,body,title");
// Enforce a structure for the document
var structure = {
link: "head",
base: "head"
};
if ( !doc ) {
if ( typeof DOMDocument != "undefined" )
doc = new DOMDocument();
else if ( typeof document != "undefined" && document.implementation && document.implementation.createDocument )
doc = document.implementation.createDocument("", "", null);
else if ( typeof ActiveX != "undefined" )
doc = new ActiveXObject("Msxml.DOMDocument");
} else
doc = doc.ownerDocument ||
doc.getOwnerDocument && doc.getOwnerDocument() ||
doc;
var elems = [],
documentElement = doc.documentElement ||
doc.getDocumentElement && doc.getDocumentElement();
// If we're dealing with an empty document then we
// need to pre-populate it with the HTML document structure
if ( !documentElement && doc.createElement ) (function(){
var html = doc.createElement("html");
var head = doc.createElement("head");
head.appendChild( doc.createElement("title") );
html.appendChild( head );
html.appendChild( doc.createElement("body") );
doc.appendChild( html );
})();
// Find all the unique elements
if ( doc.getElementsByTagName )
for ( var i in one )
one[ i ] = doc.getElementsByTagName( i )[0];
// If we're working with a document, inject contents into
// the body element
var curParentNode = one.body;
htmlParser( html, {
start: function( tagName, attrs, unary ) {
// If it's a pre-built element, then we can ignore
// its construction
if ( one[ tagName ] ) {
curParentNode = one[ tagName ];
return;
}
var elem = doc.createElement( tagName );
for ( var attr in attrs )
elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value );
if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] != "boolean" )
one[ structure[ tagName ] ].appendChild( elem );
else if ( curParentNode && curParentNode.appendChild )
curParentNode.appendChild( elem );
if ( !unary ) {
elems.push( elem );
curParentNode = elem;
}
},
end: function( tag ) {
elems.length -= 1;
// Init the new parentNode
curParentNode = elems[ elems.length - 1 ];
},
chars: function( text ) {
curParentNode.appendChild( doc.createTextNode( text ) );
},
comment: function( text ) {
// create comment node
}
});
return doc;
};
function makeMap(str){
var obj = {}, items = str.split(",");
for ( var i = 0; i < items.length; i++ )
obj[ items[i] ] = true;
return obj;
}
})();
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
set -e -o pipefail
echo "Shutting down Sauce Connect tunnel"
killall sc
while [[ -n `ps -ef | grep "sauce-connect-" | grep -v "grep"` ]]; do
printf "."
sleep .5
done
echo ""
echo "Sauce Connect tunnel has been shut down"
+3797 -48
View File
File diff suppressed because it is too large Load Diff
+5920 -140
View File
File diff suppressed because it is too large Load Diff
+17 -5
View File
@@ -1,7 +1,7 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.5.0-beta.0",
"branchVersion": "^1.5.0-rc.0",
"branchPattern": "1.5.*",
"distTag": "beta",
"repository": {
@@ -9,10 +9,15 @@
"url": "https://github.com/angular/angular.js.git"
},
"engines": {
"node": "~0.10",
"node": "<5",
"npm": "~2.5"
},
"engineStrict": true,
"scripts": {
"preinstall": "node scripts/npm/check-node-modules.js --purge",
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js",
"commit": "git-cz"
},
"devDependencies": {
"angular-benchpress": "0.x.x",
"benchmark": "1.x.x",
@@ -20,8 +25,10 @@
"browserstacktunnel-wrapper": "~1.3.1",
"canonical-path": "0.0.2",
"cheerio": "^0.17.0",
"commitizen": "^2.3.0",
"cz-conventional-changelog": "1.1.4",
"dgeni": "^0.4.0",
"dgeni-packages": "^0.10.0",
"dgeni-packages": "^0.11.0",
"event-stream": "~3.1.0",
"grunt": "~0.4.2",
"grunt-bump": "~0.0.13",
@@ -32,7 +39,7 @@
"grunt-contrib-jshint": "~0.10.0",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
"grunt-jscs": "~1.2.0",
"grunt-jscs": "^2.1.0",
"grunt-merge-conflict": "~0.0.1",
"grunt-shell": "~1.1.1",
"gulp": "~3.8.0",
@@ -76,5 +83,10 @@
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
}
],
"dependencies": {}
"dependencies": {},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
}
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ config.specs = [
];
config.capabilities = {
browserName: 'chrome',
browserName: 'chrome'
};
exports.config = config;
+2 -1
View File
@@ -14,8 +14,9 @@ function init {
TMP_DIR=$(resolveDir ../../tmp)
BUILD_DIR=$(resolveDir ../../build)
NEW_VERSION=$(cat $BUILD_DIR/version.txt)
PROJECT_DIR=$(resolveDir ../..)
# get the npm dist-tag from a custom property (distTag) in package.json
DIST_TAG=$(readJsonProp "package.json" "distTag")
DIST_TAG=$(readJsonProp "$PROJECT_DIR/package.json" "distTag")
}
+74
View File
@@ -0,0 +1,74 @@
// Implementation based on:
// https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js
'use strict';
// Imports
var fs = require('fs');
var path = require('path');
// Constants
var PROJECT_ROOT = path.join(__dirname, '../../');
var NODE_MODULES_DIR = 'node_modules';
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
// Run
_main();
// Functions - Definitions
function _main() {
var purgeIfStale = process.argv.indexOf('--purge') !== -1;
process.chdir(PROJECT_ROOT);
checkNodeModules(purgeIfStale);
}
function checkNodeModules(purgeIfStale) {
var nodeModulesOk = compareMarkerFiles(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE);
if (nodeModulesOk) {
console.log(':-) npm dependencies are looking good!');
} else if (purgeIfStale) {
console.log(':-( npm dependencies are stale or in an unknown state!');
console.log(' Purging \'' + NODE_MODULES_DIR + '\'...');
deleteDirSync(NODE_MODULES_DIR);
} else {
var separator = new Array(81).join('!');
console.warn(separator);
console.warn(':-( npm dependencies are stale or in an unknown state!');
console.warn('You can rebuild the dependencies by running `npm install`.');
console.warn(separator);
}
return nodeModulesOk;
}
function compareMarkerFiles(markerFilePath, cachedMarkerFilePath) {
if (!fs.existsSync(cachedMarkerFilePath)) return false;
var opts = {encoding: 'utf-8'};
var markerContent = fs.readFileSync(markerFilePath, opts);
var cachedMarkerContent = fs.readFileSync(cachedMarkerFilePath, opts);
return markerContent === cachedMarkerContent;
}
// Custom implementation of `rm -rf` that works consistently across OSes
function deleteDirSync(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(deleteDirOrFileSync);
fs.rmdirSync(path);
}
// Helpers
function deleteDirOrFileSync(subpath) {
var curPath = path + '/' + subpath;
if (fs.lstatSync(curPath).isDirectory()) {
deleteDirSync(curPath);
} else {
fs.unlinkSync(curPath);
}
}
}
+60
View File
@@ -0,0 +1,60 @@
'use strict';
// Imports
var fs = require('fs');
var path = require('path');
// Constants
var PROJECT_ROOT = path.join(__dirname, '../../');
var NODE_MODULES_DIR = 'node_modules';
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
// Run
_main();
// Functions - Definitions
function _main() {
process.chdir(PROJECT_ROOT);
copyFile(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE, onCopied);
}
// Implementation based on:
// https://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js#answer-21995878
function copyFile(srcPath, dstPath, callback) {
var callbackCalled = false;
if (!fs.existsSync(srcPath)) {
done(new Error('Missing source file: ' + srcPath));
return;
}
var rs = fs.createReadStream(srcPath);
rs.on('error', done);
var ws = fs.createWriteStream(dstPath);
ws.on('error', done);
ws.on('finish', done);
rs.pipe(ws);
// Helpers
function done(err) {
if (callback && !callbackCalled) {
callbackCalled = true;
callback(err);
}
}
}
function onCopied(err) {
if (err) {
var separator = new Array(81).join('!');
console.error(separator);
console.error(
'Failed to copy `' + NPM_SHRINKWRAP_FILE + '` to `' + NPM_SHRINKWRAP_CACHED_FILE + '`:');
console.error(err);
console.error(separator);
}
}
-16
View File
@@ -1,16 +0,0 @@
#!/bin/bash
set -e
SHRINKWRAP_FILE=npm-shrinkwrap.json
SHRINKWRAP_CACHED_FILE=node_modules/npm-shrinkwrap.cached.json
if diff -q $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE; then
echo 'No shrinkwrap changes detected. npm install will be skipped...';
else
echo 'Blowing away node_modules and reinstalling npm dependencies...'
rm -rf node_modules
npm install
cp $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE
echo 'npm install successful!'
fi
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Has to be run from project root directory.
./lib/${BROWSER_PROVIDER}/teardown_tunnel.sh
-2
View File
@@ -94,8 +94,6 @@
"VALIDITY_STATE_PROPERTY": false,
"reloadWithDebugInfo": false,
"skipDestroyOnNextJQueryCleanData": true,
"NODE_TYPE_ELEMENT": false,
"NODE_TYPE_ATTRIBUTE": false,
"NODE_TYPE_TEXT": false,
+117 -103
View File
@@ -198,20 +198,24 @@ msie = document.documentMode;
* String ...)
*/
function isArrayLike(obj) {
if (obj == null || isWindow(obj)) {
return false;
}
// `null`, `undefined` and `window` are not array-like
if (obj == null || isWindow(obj)) return false;
// arrays, strings and jQuery/jqLite objects are array like
// * jqLite is either the jQuery or jqLite constructor function
// * we have to check the existance of jqLite first as this method is called
// via the forEach method when constructing the jqLite object in the first place
if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
// Support: iOS 8.2 (not reproducible in simulator)
// "length" in obj used to prevent JIT error (gh-11508)
var length = "length" in Object(obj) && obj.length;
if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
return true;
}
return isString(obj) || isArray(obj) || length === 0 ||
typeof length === 'number' && length > 0 && (length - 1) in obj;
// NodeList objects (with `item` method) and
// other objects with suitable length characteristics are array-like
return isNumber(length) &&
(length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
}
/**
@@ -356,6 +360,10 @@ function baseExtend(dst, objs, deep) {
dst[key] = new Date(src.valueOf());
} else if (isRegExp(src)) {
dst[key] = new RegExp(src);
} else if (src.nodeName) {
dst[key] = src.cloneNode(true);
} else if (isElement(src)) {
dst[key] = src.clone();
} else {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
@@ -471,7 +479,7 @@ identity.$inject = [];
function valueFn(value) {return function() {return value;};}
function hasCustomToString(obj) {
return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
return isFunction(obj.toString) && obj.toString !== toString;
}
@@ -670,9 +678,9 @@ function isPromiseLike(obj) {
}
var TYPED_ARRAY_REGEXP = /^\[object (Uint8(Clamped)?)|(Uint16)|(Uint32)|(Int8)|(Int16)|(Int32)|(Float(32)|(64))Array\]$/;
var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
function isTypedArray(value) {
return TYPED_ARRAY_REGEXP.test(toString.call(value));
return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
}
@@ -794,100 +802,111 @@ function arrayRemove(array, value) {
</file>
</example>
*/
function copy(source, destination, stackSource, stackDest) {
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
}
if (isTypedArray(destination)) {
throw ngMinErr('cpta',
"Can't copy! TypedArray destination cannot be mutated.");
}
function copy(source, destination) {
var stackSource = [];
var stackDest = [];
if (!destination) {
destination = source;
if (isObject(source)) {
var index;
if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
return stackDest[index];
}
// TypedArray, Date and RegExp have specific copy functionality and must be
// pushed onto the stack before returning.
// Array and other objects create the base object and recurse to copy child
// objects. The array/object will be pushed onto the stack when recursed.
if (isArray(source)) {
return copy(source, [], stackSource, stackDest);
} else if (isTypedArray(source)) {
destination = new source.constructor(source);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isFunction(source.cloneNode)) {
destination = source.cloneNode(true);
} else {
var emptyObject = Object.create(getPrototypeOf(source));
return copy(source, emptyObject, stackSource, stackDest);
}
if (stackDest) {
stackSource.push(source);
stackDest.push(destination);
}
if (destination) {
if (isTypedArray(destination)) {
throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
}
} else {
if (source === destination) throw ngMinErr('cpi',
"Can't copy! Source and destination are identical.");
stackSource = stackSource || [];
stackDest = stackDest || [];
if (isObject(source)) {
stackSource.push(source);
stackDest.push(destination);
if (source === destination) {
throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
}
// Empty the destination object
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
if (key !== '$$hashKey') {
delete destination[key];
}
});
}
stackSource.push(source);
stackDest.push(destination);
return copyRecurse(source, destination);
}
return copyElement(source);
function copyRecurse(source, destination) {
var h = destination.$$hashKey;
var result, key;
if (isArray(source)) {
destination.length = 0;
for (var i = 0; i < source.length; i++) {
destination.push(copy(source[i], null, stackSource, stackDest));
for (var i = 0, ii = source.length; i < ii; i++) {
destination.push(copyElement(source[i]));
}
} else if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
destination[key] = copyElement(source[key]);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = copyElement(source[key]);
}
}
} else {
var h = destination.$$hashKey;
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
delete destination[key];
});
}
if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
destination[key] = copy(source[key], null, stackSource, stackDest);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = copy(source[key], null, stackSource, stackDest);
}
}
} else {
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
destination[key] = copy(source[key], null, stackSource, stackDest);
}
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
destination[key] = copyElement(source[key]);
}
}
setHashKey(destination,h);
}
setHashKey(destination, h);
return destination;
}
function copyElement(source) {
// Simple values
if (!isObject(source)) {
return source;
}
// Already copied values
var index = stackSource.indexOf(source);
if (index !== -1) {
return stackDest[index];
}
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
}
var needsRecurse = false;
var destination;
if (isArray(source)) {
destination = [];
needsRecurse = true;
} else if (isTypedArray(source)) {
destination = new source.constructor(source);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isFunction(source.cloneNode)) {
destination = source.cloneNode(true);
} else {
destination = Object.create(getPrototypeOf(source));
needsRecurse = true;
}
stackSource.push(source);
stackDest.push(destination);
return needsRecurse
? copyRecurse(source, destination)
: destination;
}
return destination;
}
/**
@@ -1663,7 +1682,6 @@ function snake_case(name, separator) {
}
var bindJQueryFired = false;
var skipDestroyOnNextJQueryCleanData;
function bindJQuery() {
var originalCleanData;
@@ -1697,15 +1715,11 @@ function bindJQuery() {
originalCleanData = jQuery.cleanData;
jQuery.cleanData = function(elems) {
var events;
if (!skipDestroyOnNextJQueryCleanData) {
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
events = jQuery._data(elem, "events");
if (events && events.$destroy) {
jQuery(elem).triggerHandler('$destroy');
}
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
events = jQuery._data(elem, "events");
if (events && events.$destroy) {
jQuery(elem).triggerHandler('$destroy');
}
} else {
skipDestroyOnNextJQueryCleanData = false;
}
originalCleanData(elems);
};
+7 -2
View File
@@ -57,10 +57,12 @@
$AnimateProvider,
$CoreAnimateCssProvider,
$$CoreAnimateQueueProvider,
$$CoreAnimateRunnerProvider,
$$AnimateRunnerFactoryProvider,
$$AnimateAsyncRunFactoryProvider,
$BrowserProvider,
$CacheFactoryProvider,
$ControllerProvider,
$DateProvider,
$DocumentProvider,
$ExceptionHandlerProvider,
$FilterProvider,
@@ -72,6 +74,7 @@
$HttpParamSerializerProvider,
$HttpParamSerializerJQLikeProvider,
$HttpBackendProvider,
$xhrFactoryProvider,
$LocationProvider,
$LogProvider,
$ParseProvider,
@@ -216,7 +219,8 @@ function publishExternalAPI(angular) {
$animate: $AnimateProvider,
$animateCss: $CoreAnimateCssProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
$$AnimateRunner: $$CoreAnimateRunnerProvider,
$$AnimateRunner: $$AnimateRunnerFactoryProvider,
$$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
@@ -230,6 +234,7 @@ function publishExternalAPI(angular) {
$httpParamSerializer: $HttpParamSerializerProvider,
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
$httpBackend: $HttpBackendProvider,
$xhrFactory: $xhrFactoryProvider,
$location: $LocationProvider,
$log: $LogProvider,
$parse: $ParseProvider,
+19 -10
View File
@@ -62,17 +62,23 @@
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
*/
var ARROW_ARG = /^([^\(]+?)=>/;
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function extractArgs(fn) {
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
return args;
}
function anonFn(fn) {
// For anonymous functions, showing at the very least the function signature can help in
// debugging.
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
args = fnText.match(FN_ARGS);
var args = extractArgs(fn);
if (args) {
return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
}
@@ -81,7 +87,6 @@ function anonFn(fn) {
function annotate(fn, strictDi, name) {
var $inject,
fnText,
argDecl,
last;
@@ -96,8 +101,7 @@ function annotate(fn, strictDi, name) {
throw $injectorMinErr('strictdi',
'{0} is not using explicit annotation and cannot be invoked in strict mode', name);
}
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
argDecl = extractArgs(fn);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
arg.replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
@@ -638,14 +642,19 @@ function createInjector(modulesToLoad, strictDi) {
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
protoInstanceInjector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
}));
return instanceInjector.invoke(
provider.$get, provider, undefined, serviceName);
}),
instanceInjector = protoInstanceInjector;
forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
var runBlocks = loadModules(modulesToLoad);
instanceInjector = protoInstanceInjector.get('$injector');
instanceInjector.strictDi = strictDi;
forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
return instanceInjector;
+83 -43
View File
@@ -33,16 +33,22 @@
*
* If jQuery is available, `angular.element` is an alias for the
* [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
* delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
* delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
*
* <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
* Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
* commonly needed functionality with the goal of having a very small footprint.</div>
* jqLite is a tiny, API-compatible subset of jQuery that allows
* Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
* commonly needed functionality with the goal of having a very small footprint.
*
* To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
* To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
* {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
* specific version of jQuery if multiple versions exist on the page.
*
* <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
* jqLite; they are never raw DOM references.</div>
* <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
* jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
*
* <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
* by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
* or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
*
* ## Angular's jqLite
* jqLite provides only the following jQuery methods:
@@ -55,7 +61,8 @@
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
* - [`clone()`](http://api.jquery.com/clone/)
* - [`contents()`](http://api.jquery.com/contents/)
* - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
* - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
* As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
* - [`data()`](http://api.jquery.com/data/)
* - [`detach()`](http://api.jquery.com/detach/)
* - [`empty()`](http://api.jquery.com/empty/)
@@ -189,6 +196,12 @@ function jqLiteHasData(node) {
return false;
}
function jqLiteCleanData(nodes) {
for (var i = 0, ii = nodes.length; i < ii; i++) {
jqLiteRemoveData(nodes[i]);
}
}
function jqLiteBuildFragment(html, context) {
var tmp, tag, wrap,
fragment = context.createDocumentFragment(),
@@ -241,6 +254,14 @@ function jqLiteParseHTML(html, context) {
return [];
}
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
var jqLiteContains = Node.prototype.contains || function(arg) {
// jshint bitwise: false
return !!(this.compareDocumentPosition(arg) & 16);
// jshint bitwise: true
};
/////////////////////////////////////////////
function JQLite(element) {
if (element instanceof JQLite) {
@@ -299,17 +320,23 @@ function jqLiteOff(element, type, fn, unsupported) {
delete events[type];
}
} else {
forEach(type.split(' '), function(type) {
if (isDefined(fn)) {
var listenerFns = events[type];
arrayRemove(listenerFns || [], fn);
if (listenerFns && listenerFns.length > 0) {
return;
}
}
removeEventListenerFn(element, type, handle);
delete events[type];
var removeHandler = function(type) {
var listenerFns = events[type];
if (isDefined(fn)) {
arrayRemove(listenerFns || [], fn);
}
if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
removeEventListenerFn(element, type, handle);
delete events[type];
}
};
forEach(type.split(' '), function(type) {
removeHandler(type);
if (MOUSE_EVENT_MAP[type]) {
removeHandler(MOUSE_EVENT_MAP[type]);
}
});
}
}
@@ -563,7 +590,8 @@ function getAliasedAttrName(name) {
forEach({
data: jqLiteData,
removeData: jqLiteRemoveData,
hasData: jqLiteHasData
hasData: jqLiteHasData,
cleanData: jqLiteCleanData
}, function(fn, name) {
JQLite[name] = fn;
});
@@ -764,6 +792,9 @@ function createEventHandler(element, events) {
return event.immediatePropagationStopped === true;
};
// Some events have special handlers that wrap the real handler
var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
// Copy event handlers in case event handlers array is modified during execution.
if ((eventFnsLength > 1)) {
eventFns = shallowCopy(eventFns);
@@ -771,7 +802,7 @@ function createEventHandler(element, events) {
for (var i = 0; i < eventFnsLength; i++) {
if (!event.isImmediatePropagationStopped()) {
eventFns[i].call(element, event);
handlerWrapper(element, event, eventFns[i]);
}
}
};
@@ -782,6 +813,22 @@ function createEventHandler(element, events) {
return eventHandler;
}
function defaultHandlerWrapper(element, event, handler) {
handler.call(element, event);
}
function specialMouseHandlerWrapper(target, event, handler) {
// Refer to jQuery's implementation of mouseenter & mouseleave
// Read about mouseenter and mouseleave:
// http://www.quirksmode.org/js/events_mouse.html#link8
var related = event.relatedTarget;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if (!related || (related !== target && !jqLiteContains.call(target, related))) {
handler.call(target, event);
}
}
//////////////////////////////////////////
// Functions iterating traversal.
// These functions chain results into a single
@@ -810,35 +857,28 @@ forEach({
var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
var i = types.length;
while (i--) {
type = types[i];
var addHandler = function(type, specialHandlerWrapper, noEventListener) {
var eventFns = events[type];
if (!eventFns) {
events[type] = [];
if (type === 'mouseenter' || type === 'mouseleave') {
// Refer to jQuery's implementation of mouseenter & mouseleave
// Read about mouseenter and mouseleave:
// http://www.quirksmode.org/js/events_mouse.html#link8
jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
var target = this, related = event.relatedTarget;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if (!related || (related !== target && !target.contains(related))) {
handle(event, type);
}
});
} else {
if (type !== '$destroy') {
addEventListenerFn(element, type, handle);
}
eventFns = events[type] = [];
eventFns.specialHandlerWrapper = specialHandlerWrapper;
if (type !== '$destroy' && !noEventListener) {
addEventListenerFn(element, type, handle);
}
eventFns = events[type];
}
eventFns.push(fn);
};
while (i--) {
type = types[i];
if (MOUSE_EVENT_MAP[type]) {
addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
addHandler(type, undefined, true);
} else {
addHandler(type);
}
}
},
+114 -1
View File
@@ -188,7 +188,7 @@ function setupModuleLoader(window) {
* @param {string} name constant name
* @param {*} object Constant value.
* @description
* Because the constant are fixed, they get applied before other provide methods.
* Because the constants are fixed, they get applied before other provide methods.
* See {@link auto.$provide#constant $provide.constant()}.
*/
constant: invokeLater('$provide', 'constant', 'unshift'),
@@ -282,6 +282,119 @@ function setupModuleLoader(window) {
*/
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
/**
* @ngdoc method
* @name angular.Module#component
* @module ng
* @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
* @param {Object} options Component definition object (a simplified
* {@link ng.$compile#directive-definition-object directive definition object}),
* has the following properties (all optional):
*
* - `controller` `{(string|function()=}` Controller constructor function that should be
* associated with newly created scope or the name of a {@link ng.$compile#-controller-
* registered controller} if passed as a string. Empty function by default.
* - `controllerAs` `{string=}` An identifier name for a reference to the controller.
* If present, the controller will be published to scope under the `controllerAs` name.
* If not present, this will default to be the same as the component name.
* - `template` `{string=|function()=}` html template as a string or a function that
* returns an html template as a string which should be used as the contents of this component.
* Empty string by default.
*
* If `template` is a function, then it is {@link auto.$injector#invoke injected} with
* the following locals:
*
* - `$element` - Current element
* - `$attrs` - Current attributes object for the element
*
* - `templateUrl` `{string=|function()=}` path or function that returns a path to an html
* template that should be used as the contents of this component.
*
* If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
* the following locals:
*
* - `$element` - Current element
* - `$attrs` - Current attributes object for the element
* - `bindings` `{object=}` Define DOM attribute binding to component properties.
* Component properties are always bound to the component controller and not to the scope.
* - `transclude` `{boolean=}` Whether {@link $compile#transclusion transclusion} is enabled.
* Enabled by default.
* - `isolate` `{boolean=}` Whether the new scope is isolated. Isolated by default.
* - `restrict` - `{string=}` - String of subset of {@link ng.$compile#-restrict- EACM} which
* restricts the component to specific directive declaration style. If omitted, this defaults to 'E'.
* - `$canActivate` `{function()=}` TBD.
* - `$routeConfig` `{object=}` TBD.
*
* @description
* Register a component definition with the compiler. This is short for registering a specific
* subset of directives which represents actual UI components in your application. Component
* definitions are very simple and do not require the complexity behind defining directives.
* Component definitions usually consist only of the template and the controller backing it.
* In order to make the definition easier, components enforce best practices like controllerAs
* and default behaviors like scope isolation, restrict to elements and allow transclusion.
*
* Here are a few examples of how you would usually define components:
*
* ```js
* var myMod = angular.module(...);
* myMod.component('myComp', {
* template: '<div>My name is {{myComp.name}}</div>',
* controller: function() {
* this.name = 'shahar';
* }
* });
*
* myMod.component('myComp', {
* template: '<div>My name is {{myComp.name}}</div>',
* bindings: {name: '@'}
* });
*
* myMod.component('myComp', {
* templateUrl: 'views/my-comp.html',
* controller: 'MyCtrl as ctrl',
* bindings: {name: '@'}
* });
*
* ```
*
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
component: function(name, options) {
function factory($injector) {
function makeInjectable(fn) {
if (angular.isFunction(fn)) {
return function(tElement, tAttrs) {
return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
};
} else {
return fn;
}
}
var template = (!options.template && !options.templateUrl ? '' : options.template);
return {
controller: options.controller || function() {},
controllerAs: identifierForController(options.controller) || options.controllerAs || name,
template: makeInjectable(template),
templateUrl: makeInjectable(options.templateUrl),
transclude: options.transclude === undefined ? true : options.transclude,
scope: options.isolate === false ? true : {},
bindToController: options.bindings || {},
restrict: options.restrict || 'E'
};
}
if (options.$canActivate) {
factory.$canActivate = options.$canActivate;
}
if (options.$routeConfig) {
factory.$routeConfig = options.$routeConfig;
}
factory.$inject = ['$injector'];
return moduleInstance.directive(name, factory);
},
/**
* @ngdoc method
* @name angular.Module#config
+1 -1
View File
@@ -41,7 +41,7 @@ function $AnchorScrollProvider() {
* When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
* current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
* in the
* [HTML5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
* [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
*
* It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
* match any anchor whenever it changes. This can be disabled by calling
+7 -25
View File
@@ -53,29 +53,6 @@ function prepareAnimateOptions(options) {
: {};
}
var $$CoreAnimateRunnerProvider = function() {
this.$get = ['$q', '$$rAF', function($q, $$rAF) {
function AnimateRunner() {}
AnimateRunner.all = noop;
AnimateRunner.chain = noop;
AnimateRunner.prototype = {
end: noop,
cancel: noop,
resume: noop,
pause: noop,
complete: noop,
then: function(pass, fail) {
return $q(function(resolve) {
$$rAF(function() {
resolve();
});
}).then(pass, fail);
}
};
return AnimateRunner;
}];
};
// this is prefixed with Core since it conflicts with
// the animateQueueProvider defined in ngAnimate/animateQueue.js
var $$CoreAnimateQueueProvider = function() {
@@ -101,7 +78,12 @@ var $$CoreAnimateQueueProvider = function() {
addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
}
return new $$AnimateRunner(); // jshint ignore:line
var runner = new $$AnimateRunner(); // jshint ignore:line
// since there are no animations to run the runner needs to be
// notified that the animation call is complete.
runner.complete();
return runner;
}
};
@@ -285,7 +267,7 @@ var $AnimateProvider = ['$provide', function($provide) {
* when an animation is detected (and animations are enabled), $animate will do the heavy lifting
* to ensure that animation runs with the triggered DOM operation.
*
* By default $animate doesn't trigger an animations. This is because the `ngAnimate` module isn't
* By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
* included and only when it is active then the animation hooks that `$animate` triggers will be
* functional. Once active then all structural `ng-` directives will trigger animations as they perform
* their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
+17 -32
View File
@@ -12,43 +12,28 @@
* Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
*/
var $CoreAnimateCssProvider = function() {
this.$get = ['$$rAF', '$q', function($$rAF, $q) {
this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
var RAFPromise = function() {};
RAFPromise.prototype = {
done: function(cancel) {
this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
},
end: function() {
this.done();
},
cancel: function() {
this.done(true);
},
getPromise: function() {
if (!this.defer) {
this.defer = $q.defer();
}
return this.defer.promise;
},
then: function(f1,f2) {
return this.getPromise().then(f1,f2);
},
'catch': function(f1) {
return this.getPromise()['catch'](f1);
},
'finally': function(f1) {
return this.getPromise()['finally'](f1);
return function(element, initialOptions) {
// we always make a copy of the options since
// there should never be any side effects on
// the input data when running `$animateCss`.
var options = copy(initialOptions);
// there is no point in applying the styles since
// there is no animation that goes on at all in
// this version of $animateCss.
if (options.cleanupStyles) {
options.from = options.to = null;
}
};
return function(element, options) {
if (options.from) {
element.css(options.from);
options.from = null;
}
var closed, runner = new RAFPromise();
/* jshint newcap: false */
var closed, runner = new $$AnimateRunner();
return {
start: run,
end: run
@@ -56,16 +41,16 @@ var $CoreAnimateCssProvider = function() {
function run() {
$$rAF(function() {
close();
applyAnimationContents();
if (!closed) {
runner.done();
runner.complete();
}
closed = true;
});
return runner;
}
function close() {
function applyAnimationContents() {
if (options.addClass) {
element.addClass(options.addClass);
options.addClass = null;
+170
View File
@@ -0,0 +1,170 @@
'use strict';
var $$AnimateAsyncRunFactoryProvider = function() {
this.$get = ['$$rAF', function($$rAF) {
var waitQueue = [];
function waitForTick(fn) {
waitQueue.push(fn);
if (waitQueue.length > 1) return;
$$rAF(function() {
for (var i = 0; i < waitQueue.length; i++) {
waitQueue[i]();
}
waitQueue = [];
});
}
return function() {
var passed = false;
waitForTick(function() {
passed = true;
});
return function(callback) {
passed ? callback() : waitForTick(callback);
};
};
}];
};
var $$AnimateRunnerFactoryProvider = function() {
this.$get = ['$q', '$sniffer', '$$animateAsyncRun',
function($q, $sniffer, $$animateAsyncRun) {
var INITIAL_STATE = 0;
var DONE_PENDING_STATE = 1;
var DONE_COMPLETE_STATE = 2;
AnimateRunner.chain = function(chain, callback) {
var index = 0;
next();
function next() {
if (index === chain.length) {
callback(true);
return;
}
chain[index](function(response) {
if (response === false) {
callback(false);
return;
}
index++;
next();
});
}
};
AnimateRunner.all = function(runners, callback) {
var count = 0;
var status = true;
forEach(runners, function(runner) {
runner.done(onProgress);
});
function onProgress(response) {
status = status && response;
if (++count === runners.length) {
callback(status);
}
}
};
function AnimateRunner(host) {
this.setHost(host);
this._doneCallbacks = [];
this._runInAnimationFrame = $$animateAsyncRun();
this._state = 0;
}
AnimateRunner.prototype = {
setHost: function(host) {
this.host = host || {};
},
done: function(fn) {
if (this._state === DONE_COMPLETE_STATE) {
fn();
} else {
this._doneCallbacks.push(fn);
}
},
progress: noop,
getPromise: function() {
if (!this.promise) {
var self = this;
this.promise = $q(function(resolve, reject) {
self.done(function(status) {
status === false ? reject() : resolve();
});
});
}
return this.promise;
},
then: function(resolveHandler, rejectHandler) {
return this.getPromise().then(resolveHandler, rejectHandler);
},
'catch': function(handler) {
return this.getPromise()['catch'](handler);
},
'finally': function(handler) {
return this.getPromise()['finally'](handler);
},
pause: function() {
if (this.host.pause) {
this.host.pause();
}
},
resume: function() {
if (this.host.resume) {
this.host.resume();
}
},
end: function() {
if (this.host.end) {
this.host.end();
}
this._resolve(true);
},
cancel: function() {
if (this.host.cancel) {
this.host.cancel();
}
this._resolve(false);
},
complete: function(response) {
var self = this;
if (self._state === INITIAL_STATE) {
self._state = DONE_PENDING_STATE;
self._runInAnimationFrame(function() {
self._resolve(response);
});
}
},
_resolve: function(response) {
if (this._state !== DONE_COMPLETE_STATE) {
forEach(this._doneCallbacks, function(fn) {
fn(response);
});
this._doneCallbacks.length = 0;
this._state = DONE_COMPLETE_STATE;
}
}
};
return AnimateRunner;
}];
};
+8 -7
View File
@@ -67,10 +67,10 @@
$scope.keys = [];
$scope.cache = $cacheFactory('cacheId');
$scope.put = function(key, value) {
if (isUndefined($scope.cache.get(key))) {
if (angular.isUndefined($scope.cache.get(key))) {
$scope.keys.push(key);
}
$scope.cache.put(key, isUndefined(value) ? null : value);
$scope.cache.put(key, angular.isUndefined(value) ? null : value);
};
}]);
</file>
@@ -93,9 +93,9 @@ function $CacheFactoryProvider() {
var size = 0,
stats = extend({}, options, {id: cacheId}),
data = {},
data = createMap(),
capacity = (options && options.capacity) || Number.MAX_VALUE,
lruHash = {},
lruHash = createMap(),
freshEnd = null,
staleEnd = null;
@@ -223,6 +223,8 @@ function $CacheFactoryProvider() {
delete lruHash[key];
}
if (!(key in data)) return;
delete data[key];
size--;
},
@@ -237,9 +239,9 @@ function $CacheFactoryProvider() {
* Clears the cache object of any entries.
*/
removeAll: function() {
data = {};
data = createMap();
size = 0;
lruHash = {};
lruHash = createMap();
freshEnd = staleEnd = null;
},
@@ -399,4 +401,3 @@ function $TemplateCacheProvider() {
return $cacheFactory('templates');
}];
}
+301 -125
View File
@@ -226,10 +226,10 @@
* * `$element` - Current element
* * `$attrs` - Current attributes object for the element
* * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
* `function([scope], cloneLinkingFn, futureParentElement)`.
* * `scope`: optional argument to override the scope.
* * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
* * `futureParentElement`:
* `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
* * `scope`: (optional) override the scope.
* * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
* * `futureParentElement` (optional):
* * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
* * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
* * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
@@ -237,7 +237,10 @@
* as those elements need to created and cloned in a special way when they are defined outside their
* usual containers (e.g. like `<svg>`).
* * See also the `directive.templateNamespace` property.
*
* * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
* then the default translusion is provided.
* The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
@@ -337,14 +340,6 @@
* The contents are compiled and provided to the directive as a **transclusion function**. See the
* {@link $compile#transclusion Transclusion} section below.
*
* There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
* directive's element or the entire element:
*
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
* * `'element'` - transclude the whole of the directive's element including any directives on this
* element that defined at a lower priority than this directive. When used, the `template`
* property is ignored.
*
*
* #### `compile`
*
@@ -474,6 +469,34 @@
* Testing Transclusion Directives}.
* </div>
*
* There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
* directive's element, the entire element or multiple parts of the element contents:
*
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
* * `'element'` - transclude the whole of the directive's element including any directives on this
* element that defined at a lower priority than this directive. When used, the `template`
* property is ignored.
* * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
*
* **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
*
* This object is a map where the keys are the name of the slot to fill and the value is an element selector
* used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
* and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
*
* For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
*
* If the element selector is prefixed with a `?` then that slot is optional.
*
* For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
* the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
*
* Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
* in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
* `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
* injectable into the directive's controller.
*
*
* #### Transclusion Functions
*
* When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
@@ -989,6 +1012,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
$controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
var SIMPLE_ATTR_NAME = /^\w/;
var specialAttrHolder = document.createElement('div');
var Attributes = function(element, attributesToCopy) {
if (attributesToCopy) {
var keys = Object.keys(attributesToCopy);
@@ -1124,7 +1149,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nodeName = nodeName_(this.$$element);
if ((nodeName === 'a' && key === 'href') ||
if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
(nodeName === 'img' && key === 'src')) {
// sanitize a[href] and img[src] values
this[key] = value = $$sanitizeUri(value, key === 'src');
@@ -1168,7 +1193,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (value === null || isUndefined(value)) {
this.$$element.removeAttr(attrName);
} else {
this.$$element.attr(attrName, value);
if (SIMPLE_ATTR_NAME.test(attrName)) {
this.$$element.attr(attrName, value);
} else {
setSpecialAttr(this.$$element[0], attrName, value);
}
}
}
@@ -1221,6 +1250,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
};
function setSpecialAttr(element, attrName, value) {
// Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
// so we have to jump through some hoops to get such an attribute
// https://github.com/angular/angular.js/pull/13318
specialAttrHolder.innerHTML = "<span " + attrName + ">";
var attributes = specialAttrHolder.firstChild.attributes;
var attribute = attributes[0];
// We have to remove the attribute from its container element before we can add it to the destination element
attributes.removeNamedItem(attribute.name);
attribute.value = value;
element.attributes.setNamedItem(attribute);
}
function safeAddClass($element, className) {
try {
@@ -1240,6 +1281,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
},
NG_ATTR_BINDING = /^ngAttr[A-Z]/;
var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
var bindings = $element.data('$binding') || [];
@@ -1280,7 +1322,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// We can not compile top level text elements since text nodes can be merged and we will
// not be able to attach scope data to them, so we will wrap them in <span>
forEach($compileNodes, function(node, index) {
if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */) {
$compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
}
});
@@ -1292,6 +1334,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return function publicLinkFn(scope, cloneConnectFn, options) {
assertArg(scope, 'scope');
if (previousCompileContext && previousCompileContext.needsNewScope) {
// A parent directive did a replace and a directive on this element asked
// for transclusion, which caused us to lose a layer of element on which
// we could hold the new transclusion scope, so we will create it manually
// here.
scope = scope.$parent.$new();
}
options = options || {};
var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
transcludeControllers = options.transcludeControllers,
@@ -1437,11 +1487,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (nodeLinkFn.scope) {
childScope = scope.$new();
compile.$$addScopeInfo(jqLite(node), childScope);
var destroyBindings = nodeLinkFn.$$destroyBindings;
if (destroyBindings) {
nodeLinkFn.$$destroyBindings = null;
childScope.$on('$destroyed', destroyBindings);
}
} else {
childScope = scope;
}
@@ -1460,8 +1505,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
childBoundTranscludeFn = null;
}
nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn,
nodeLinkFn);
nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
@@ -1486,6 +1530,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
});
};
// We need to attach the transclusion slots onto the `boundTranscludeFn`
// so that they are available inside the `controllersBoundTransclude` function
var boundSlots = boundTranscludeFn.$$slots = createMap();
for (var slotName in transcludeFn.$$slots) {
if (transcludeFn.$$slots[slotName]) {
boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
} else {
boundSlots[slotName] = null;
}
}
return boundTranscludeFn;
}
@@ -1530,13 +1585,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
});
}
var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
if (directiveIsMultiElement(directiveNName)) {
if (ngAttrName === directiveNName + 'Start') {
attrStartName = name;
attrEndName = name.substr(0, name.length - 5) + 'end';
name = name.substr(0, name.length - 6);
}
var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
attrStartName = name;
attrEndName = name.substr(0, name.length - 5) + 'end';
name = name.substr(0, name.length - 6);
}
nName = directiveNormalize(name.toLowerCase());
@@ -1646,6 +1699,37 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
};
}
/**
* A function generator that is used to support both eager and lazy compilation
* linking function.
* @param eager
* @param $compileNodes
* @param transcludeFn
* @param maxPriority
* @param ignoreDirective
* @param previousCompileContext
* @returns {Function}
*/
function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
if (eager) {
return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
}
var compiled;
return function() {
if (!compiled) {
compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
// Null out all of these references in order to make them eligible for garbage collection
// since this is a potentially long lived closure
$compileNodes = transcludeFn = previousCompileContext = null;
}
return compiled.apply(this, arguments);
};
}
/**
* Once the directives have been collected, their compile functions are executed. This method
* is responsible for inlining directive templates as well as terminating the application
@@ -1690,6 +1774,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
replaceDirective = originalReplaceDirective,
childTranscludeFn = transcludeFn,
linkFn,
didScanForMultipleTransclusion = false,
mightHaveMultipleTransclusionError = false,
directiveValue;
// executes all directives on the current element
@@ -1732,6 +1818,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
directiveName = directive.name;
// If we encounter a condition that can result in transclusion on the directive,
// then scan ahead in the remaining directives for others that may cause a multiple
// transclusion error to be thrown during the compilation process. If a matching directive
// is found, then we know that when we encounter a transcluded directive, we need to eagerly
// compile the `transclude` function rather than doing it lazily in order to throw
// exceptions at the correct time
if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
|| (directive.transclude && !directive.$$tlb))) {
var candidateDirective;
for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
if ((candidateDirective.transclude && !candidateDirective.$$tlb)
|| (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
mightHaveMultipleTransclusionError = true;
break;
}
}
didScanForMultipleTransclusion = true;
}
if (!directive.templateUrl && directive.controller) {
directiveValue = directive.controller;
controllerDirectives = controllerDirectives || createMap();
@@ -1761,7 +1868,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compileNode = $compileNode[0];
replaceWith(jqCollection, sliceArgs($template), compileNode);
childTranscludeFn = compile($template, transcludeFn, terminalPriority,
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
replaceDirective && replaceDirective.name, {
// Don't pass in:
// - controllerDirectives - otherwise we'll create duplicates controllers
@@ -1773,9 +1880,69 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nonTlbTranscludeDirective: nonTlbTranscludeDirective
});
} else {
var slots = createMap();
$template = jqLite(jqLiteClone(compileNode)).contents();
if (isObject(directiveValue)) {
// We have transclusion slots,
// collect them up, compile them and store their transclusion functions
$template = [];
var slotMap = createMap();
var filledSlots = createMap();
// Parse the element selectors
forEach(directiveValue, function(elementSelector, slotName) {
// If an element selector starts with a ? then it is optional
var optional = (elementSelector.charAt(0) === '?');
elementSelector = optional ? elementSelector.substring(1) : elementSelector;
slotMap[elementSelector] = slotName;
// We explicitly assign `null` since this implies that a slot was defined but not filled.
// Later when calling boundTransclusion functions with a slot name we only error if the
// slot is `undefined`
slots[slotName] = null;
// filledSlots contains `true` for all slots that are either optional or have been
// filled. This is used to check that we have not missed any required slots
filledSlots[slotName] = optional;
});
// Add the matching elements into their slot
forEach($compileNode.contents(), function(node) {
var slotName = slotMap[directiveNormalize(nodeName_(node))];
if (slotName) {
filledSlots[slotName] = true;
slots[slotName] = slots[slotName] || [];
slots[slotName].push(node);
} else {
$template.push(node);
}
});
// Check for required slots that were not filled
forEach(filledSlots, function(filled, slotName) {
if (!filled) {
throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
}
});
for (var slotName in slots) {
if (slots[slotName]) {
// Only define a transclusion function if the slot was filled
slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
}
}
}
$compileNode.empty(); // clear contents
childTranscludeFn = compile($template, transcludeFn);
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
childTranscludeFn.$$slots = slots;
}
}
@@ -1817,8 +1984,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
if (newIsolateScopeDirective) {
markDirectivesAsIsolate(templateDirectives);
if (newIsolateScopeDirective || newScopeDirective) {
// The original directive caused the current element to be replaced but this element
// also needs to have a new scope, so we need to tell the template directives
// that they would need to get their scope from further up, if they require transclusion
markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
}
directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
@@ -1971,10 +2141,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return elementControllers;
}
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn,
thisLinkFn) {
var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
attrs;
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
attrs, removeScopeBindingWatches, removeControllerBindingWatches;
if (compileNode === linkNode) {
attrs = templateAttrs;
@@ -1984,8 +2153,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
attrs = new Attributes($element, templateAttrs);
}
controllerScope = scope;
if (newIsolateScopeDirective) {
isolateScope = scope.$new(true);
} else if (newScopeDirective) {
controllerScope = scope.$parent;
}
if (boundTranscludeFn) {
@@ -1993,6 +2165,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
transcludeFn = controllersBoundTransclude;
transcludeFn.$$boundTransclude = boundTranscludeFn;
// expose the slots on the `$transclude` function
transcludeFn.isSlotFilled = function(slotName) {
return !!boundTranscludeFn.$$slots[slotName];
};
}
if (controllerDirectives) {
@@ -2006,42 +2182,34 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compile.$$addScopeClass($element, true);
isolateScope.$$isolateBindings =
newIsolateScopeDirective.$$isolateBindings;
initializeDirectiveBindings(scope, attrs, isolateScope,
isolateScope.$$isolateBindings,
newIsolateScopeDirective, isolateScope);
}
if (elementControllers) {
// Initialize bindToController bindings for new/isolate scopes
var scopeDirective = newIsolateScopeDirective || newScopeDirective;
var bindings;
var controllerForBindings;
if (scopeDirective && elementControllers[scopeDirective.name]) {
bindings = scopeDirective.$$bindings.bindToController;
controller = elementControllers[scopeDirective.name];
if (controller && controller.identifier && bindings) {
controllerForBindings = controller;
thisLinkFn.$$destroyBindings =
initializeDirectiveBindings(scope, attrs, controller.instance,
bindings, scopeDirective);
}
removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
isolateScope.$$isolateBindings,
newIsolateScopeDirective);
if (removeScopeBindingWatches) {
isolateScope.$on('$destroy', removeScopeBindingWatches);
}
for (i in elementControllers) {
controller = elementControllers[i];
var controllerResult = controller();
}
if (controllerResult !== controller.instance) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers and update the element data
controller.instance = controllerResult;
$element.data('$' + i + 'Controller', controllerResult);
if (controller === controllerForBindings) {
// Remove and re-install bindToController bindings
thisLinkFn.$$destroyBindings();
thisLinkFn.$$destroyBindings =
initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
}
}
// Initialize bindToController bindings
for (var name in elementControllers) {
var controllerDirective = controllerDirectives[name];
var controller = elementControllers[name];
var bindings = controllerDirective.$$bindings.bindToController;
if (controller.identifier && bindings) {
removeControllerBindingWatches =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
var controllerResult = controller();
if (controllerResult !== controller.instance) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers
controller.instance = controllerResult;
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
removeControllerBindingWatches && removeControllerBindingWatches();
removeControllerBindingWatches =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
}
@@ -2080,11 +2248,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// This is the function that is injected as `$transclude`.
// Note: all arguments are optional!
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
var transcludeControllers;
// No scope passed in:
if (!isScope(scope)) {
slotName = futureParentElement;
futureParentElement = cloneAttachFn;
cloneAttachFn = scope;
scope = undefined;
@@ -2096,15 +2264,36 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (!futureParentElement) {
futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
}
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
if (slotName) {
// slotTranscludeFn can be one of three things:
// * a transclude function - a filled slot
// * `null` - an optional slot that was not filled
// * `undefined` - a slot that was not declared (i.e. invalid)
var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
if (slotTranscludeFn) {
return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
} else if (isUndefined(slotTranscludeFn)) {
throw $compileMinErr('noslot',
'No parent directive that requires a transclusion with slot name "{0}". ' +
'Element: {1}',
slotName, startingTag($element));
}
} else {
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
}
}
}
}
function markDirectivesAsIsolate(directives) {
// mark all directives as needing isolate scope.
// Depending upon the context in which a directive finds itself it might need to have a new isolated
// or child scope created. For instance:
// * if the directive has been pulled into a template because another directive with a higher priority
// asked for element transclusion
// * if the directive itself asks for transclusion but it is at the root of a template and the original
// element was replaced. See https://github.com/angular/angular.js/issues/12936
function markDirectiveScope(directives, isolateScope, newScope) {
for (var j = 0, jj = directives.length; j < jj; j++) {
directives[j] = inherit(directives[j], {$$isolateScope: true});
directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
}
}
@@ -2251,7 +2440,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
if (isObject(origAsyncDirective.scope)) {
markDirectivesAsIsolate(templateDirectives);
// the original directive that caused the template to be loaded async required
// an isolate scope
markDirectiveScope(templateDirectives, true);
}
directives = templateDirectives.concat(directives);
mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
@@ -2300,7 +2491,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
childBoundTranscludeFn = boundTranscludeFn;
}
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
childBoundTranscludeFn, afterTemplateNodeLinkFn);
childBoundTranscludeFn);
}
linkQueue = null;
});
@@ -2317,8 +2508,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
}
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn,
afterTemplateNodeLinkFn);
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
}
};
}
@@ -2427,7 +2617,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compile: function() {
return {
pre: function attrInterpolatePreLinkFn(scope, element, attr) {
var $$observers = (attr.$$observers || (attr.$$observers = {}));
var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
throw $compileMinErr('nodomevents',
@@ -2522,41 +2712,33 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
parent.replaceChild(newNode, firstElementToRemove);
}
// TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
// Append all the `elementsToRemove` to a fragment. This will...
// - remove them from the DOM
// - allow them to still be traversed with .nextSibling
// - allow a single fragment.qSA to fetch all elements being removed
var fragment = document.createDocumentFragment();
fragment.appendChild(firstElementToRemove);
for (i = 0; i < removeCount; i++) {
fragment.appendChild(elementsToRemove[i]);
}
if (jqLite.hasData(firstElementToRemove)) {
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
// data here because there's no public interface in jQuery to do that and copying over
// event listeners (which is the main use of private data) wouldn't work anyway.
jqLite(newNode).data(jqLite(firstElementToRemove).data());
jqLite.data(newNode, jqLite.data(firstElementToRemove));
// Remove data of the replaced element. We cannot just call .remove()
// on the element it since that would deallocate scope that is needed
// for the new node. Instead, remove the data "manually".
if (!jQuery) {
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
} else {
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
// the replaced element. The cleanData version monkey-patched by Angular would cause
// the scope to be trashed and we do need the very same scope to work with the new
// element. However, we cannot just cache the non-patched version and use it here as
// that would break if another library patches the method after Angular does (one
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
// skipped this one time.
skipDestroyOnNextJQueryCleanData = true;
jQuery.cleanData([firstElementToRemove]);
}
// Remove $destroy event listeners from `firstElementToRemove`
jqLite(firstElementToRemove).off('$destroy');
}
for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
var element = elementsToRemove[k];
jqLite(element).remove(); // must do this way to clean up expando
fragment.appendChild(element);
delete elementsToRemove[k];
}
// Cleanup any data/listeners on the elements and children.
// This includes invoking the $destroy event on any elements with listeners.
jqLite.cleanData(fragment.querySelectorAll('*'));
// Update the jqLite collection to only contain the `newNode`
for (i = 1; i < removeCount; i++) {
delete elementsToRemove[i];
}
elementsToRemove[0] = newNode;
elementsToRemove.length = 1;
}
@@ -2578,9 +2760,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// Set up $watches for isolate scope and controller bindings. This process
// only occurs for isolate scopes and new scopes with controllerAs.
function initializeDirectiveBindings(scope, attrs, destination, bindings,
directive, newScope) {
var onNewScopeDestroyed;
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
var removeWatchCollection = [];
forEach(bindings, function(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
@@ -2642,14 +2823,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return lastValue = parentValue;
};
parentValueWatch.$stateful = true;
var unwatch;
var removeWatch;
if (definition.collection) {
unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
} else {
unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
}
onNewScopeDestroyed = (onNewScopeDestroyed || []);
onNewScopeDestroyed.push(unwatch);
removeWatchCollection.push(removeWatch);
break;
case '&':
@@ -2665,16 +2845,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
break;
}
});
var destroyBindings = onNewScopeDestroyed ? function destroyBindings() {
for (var i = 0, ii = onNewScopeDestroyed.length; i < ii; ++i) {
onNewScopeDestroyed[i]();
return removeWatchCollection.length && function removeWatches() {
for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
removeWatchCollection[i]();
}
} : noop;
if (newScope && destroyBindings !== noop) {
newScope.$on('$destroy', destroyBindings);
return noop;
}
return destroyBindings;
};
}
}];
}
+16 -19
View File
@@ -11,7 +11,8 @@
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/[\]$'()*,;~]*)?$/;
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;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
@@ -44,8 +45,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -332,7 +333,7 @@ var inputType = {
*
* @description
* Input with time validation and transformation. In browsers that do not yet support
* the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
* the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
* local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
* Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
*
@@ -679,8 +680,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -777,8 +778,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -876,8 +877,8 @@ var inputType = {
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -1337,11 +1338,7 @@ function badInputChecker(scope, element, attr, ctrl) {
if (nativeValidation) {
ctrl.$parsers.push(function(value) {
var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
// Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
// - also sets validity.badInput (should only be validity.typeMismatch).
// - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
// - can ignore this case as we can still read out the erroneous email...
return validity.badInput && !validity.typeMismatch ? undefined : value;
return validity.badInput || validity.typeMismatch ? undefined : value;
});
}
}
@@ -1513,8 +1510,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
@@ -1552,8 +1549,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
* value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object, then this is used directly.
* If the expression evaluates to a string, then it will be converted to a RegExp
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
+7 -1
View File
@@ -34,7 +34,13 @@
* @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
* make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
* @param {string=} onload Expression to evaluate when a new partial is loaded.
*
* <div class="alert alert-warning">
* **Note:** When using onload on SVG elements in IE11, the browser will try to call
* a function with the name on the window element, which will usually throw a
* "function is undefined" error. To fix this, you can instead use `data-onload` or a
* different form that {@link guide/directive#normalization matches} `onload`.
* </div>
*
* @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
* $anchorScroll} to scroll the viewport after the content is loaded.
*
+1 -1
View File
@@ -66,7 +66,7 @@
* </file>
* </example>
*
* ### Example - splitting on whitespace
* ### Example - splitting on newline
* <example name="ngList-directive-newlines">
* <file name="index.html">
* <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
+86 -37
View File
@@ -14,7 +14,9 @@ var VALID_CLASS = 'ng-valid',
DIRTY_CLASS = 'ng-dirty',
UNTOUCHED_CLASS = 'ng-untouched',
TOUCHED_CLASS = 'ng-touched',
PENDING_CLASS = 'ng-pending';
PENDING_CLASS = 'ng-pending',
EMPTY_CLASS = 'ng-empty',
NOT_EMPTY_CLASS = 'ng-not-empty';
var ngModelMinErr = minErr('ngModel');
@@ -22,7 +24,9 @@ var ngModelMinErr = minErr('ngModel');
* @ngdoc type
* @name ngModel.NgModelController
*
* @property {string} $viewValue Actual string value in the view.
* @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
* String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
* is set.
* @property {*} $modelValue The value in the model that the control is bound to.
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
the control reads value from the DOM. The functions are called in array order, each passing
@@ -316,6 +320,17 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
return isUndefined(value) || value === '' || value === null || value !== value;
};
this.$$updateEmptyClasses = function(value) {
if (ctrl.$isEmpty(value)) {
$animate.removeClass($element, NOT_EMPTY_CLASS);
$animate.addClass($element, EMPTY_CLASS);
} else {
$animate.removeClass($element, EMPTY_CLASS);
$animate.addClass($element, NOT_EMPTY_CLASS);
}
};
var currentValidationRunId = 0;
/**
@@ -433,11 +448,14 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* which may be caused by a pending debounced event or because the input is waiting for a some
* future event.
*
* If you have an input that uses `ng-model-options` to set up debounced events or events such
* as blur you can have a situation where there is a period when the `$viewValue`
* is out of synch with the ngModel's `$modelValue`.
* If you have an input that uses `ng-model-options` to set up debounced updates or updates that
* depend on special events such as blur, you can have a situation where there is a period when
* the `$viewValue` is out of sync with the ngModel's `$modelValue`.
*
* In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
* In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
* and reset the input to the last committed view value.
*
* It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
* programmatically before these debounced/future events have resolved/occurred, because Angular's
* dirty checking mechanism is not able to tell whether the model has actually changed or not.
*
@@ -450,39 +468,63 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* angular.module('cancel-update-example', [])
*
* .controller('CancelUpdateController', ['$scope', function($scope) {
* $scope.resetWithCancel = function(e) {
* $scope.model = {};
*
* $scope.setEmpty = function(e, value, rollback) {
* if (e.keyCode == 27) {
* $scope.myForm.myInput1.$rollbackViewValue();
* $scope.myValue = '';
* }
* };
* $scope.resetWithoutCancel = function(e) {
* if (e.keyCode == 27) {
* $scope.myValue = '';
* e.preventDefault();
* if (rollback) {
* $scope.myForm[value].$rollbackViewValue();
* }
* $scope.model[value] = '';
* }
* };
* }]);
* </file>
* <file name="index.html">
* <div ng-controller="CancelUpdateController">
* <p>Try typing something in each input. See that the model only updates when you
* blur off the input.
* </p>
* <p>Now see what happens if you start typing then press the Escape key</p>
* <p>Both of these inputs are only updated if they are blurred. Hitting escape should
* empty them. Follow these steps and observe the difference:</p>
* <ol>
* <li>Type something in the input. You will see that the model is not yet updated</li>
* <li>Press the Escape key.
* <ol>
* <li> In the first example, nothing happens, because the model is already '', and no
* update is detected. If you blur the input, the model will be set to the current view.
* </li>
* <li> In the second example, the pending update is cancelled, and the input is set back
* to the last committed view value (''). Blurring the input does nothing.
* </li>
* </ol>
* </li>
* </ol>
*
* <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
* <p id="inputDescription1">With $rollbackViewValue()</p>
* <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
* ng-keydown="resetWithCancel($event)"><br/>
* myValue: "{{ myValue }}"
* <div>
* <p id="inputDescription1">Without $rollbackViewValue():</p>
* <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
* ng-keydown="setEmpty($event, 'value1')">
* value1: "{{ model.value1 }}"
* </div>
*
* <p id="inputDescription2">Without $rollbackViewValue()</p>
* <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
* ng-keydown="resetWithoutCancel($event)"><br/>
* myValue: "{{ myValue }}"
* <div>
* <p id="inputDescription2">With $rollbackViewValue():</p>
* <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
* ng-keydown="setEmpty($event, 'value2', true)">
* value2: "{{ model.value2 }}"
* </div>
* </form>
* </div>
* </file>
<file name="style.css">
div {
display: table-cell;
}
div:nth-child(1) {
padding-right: 30px;
}
</file>
* </example>
*/
this.$rollbackViewValue = function() {
@@ -652,6 +694,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
return;
}
ctrl.$$updateEmptyClasses(viewValue);
ctrl.$$lastCommittedViewValue = viewValue;
// change to dirty
@@ -834,6 +877,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
viewValue = formatters[idx](viewValue);
}
if (ctrl.$viewValue !== viewValue) {
ctrl.$$updateEmptyClasses(viewValue);
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
ctrl.$render();
@@ -864,7 +908,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* require.
* - Providing validation behavior (i.e. required, number, email, url).
* - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
* - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
* - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
* `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
* - Registering the control with its parent {@link ng.directive:form form}.
*
* Note: `ngModel` will try to bind to the property given by evaluating the expression on the
@@ -905,13 +950,16 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* - `ng-touched`: the control has been blurred
* - `ng-untouched`: the control hasn't been blurred
* - `ng-pending`: any `$asyncValidators` are unfulfilled
* - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
* by the {@link ngModel.NgModelController#$isEmpty} method
* - `ng-not-empty`: the view contains a non-empty value
*
* Keep in mind that ngAnimate can detect each of these classes when added and removed.
*
* ## Animation Hooks
*
* Animations within models are triggered when any of the associated CSS classes are added and removed
* on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
* on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
* `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
* The animations that are triggered within ngModel are similar to how they work in ngClass and
* animations can be hooked into using CSS transitions, keyframes as well as JS animations.
@@ -1139,12 +1187,13 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
</label><br />
</form>
<pre>user.name = <span ng-bind="user.name"></span></pre>
<pre>user.data = <span ng-bind="user.data"></span></pre>
</div>
</file>
<file name="app.js">
angular.module('optionsExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.user = { name: 'say', data: '' };
$scope.user = { name: 'John', data: '' };
$scope.cancel = function(e) {
if (e.keyCode == 27) {
@@ -1159,20 +1208,20 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
var other = element(by.model('user.data'));
it('should allow custom events', function() {
input.sendKeys(' hello');
input.sendKeys(' Doe');
input.click();
expect(model.getText()).toEqual('say');
expect(model.getText()).toEqual('John');
other.click();
expect(model.getText()).toEqual('say hello');
expect(model.getText()).toEqual('John Doe');
});
it('should $rollbackViewValue when model changes', function() {
input.sendKeys(' hello');
expect(input.getAttribute('value')).toEqual('say hello');
input.sendKeys(' Doe');
expect(input.getAttribute('value')).toEqual('John Doe');
input.sendKeys(protractor.Key.ESCAPE);
expect(input.getAttribute('value')).toEqual('say');
expect(input.getAttribute('value')).toEqual('John');
other.click();
expect(model.getText()).toEqual('say');
expect(model.getText()).toEqual('John');
});
</file>
</example>
@@ -1198,7 +1247,7 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
<file name="app.js">
angular.module('optionsExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.user = { name: 'say' };
$scope.user = { name: 'Igor' };
}]);
</file>
</example>
+75 -42
View File
@@ -33,19 +33,27 @@ var ngOptionsMinErr = minErr('ngOptions');
*
* ## Complex Models (objects or collections)
*
* **Note:** By default, `ngModel` watches the model by reference, not value. This is important when
* binding any input directive to a model that is an object or a collection.
* By default, `ngModel` watches the model by reference, not value. This is important to know when
* binding the select to a model that is an object or a collection.
*
* Since this is a common situation for `ngOptions` the directive additionally watches the model using
* `$watchCollection` when the select has the `multiple` attribute or when there is a `track by` clause in
* the options expression. This allows ngOptions to trigger a re-rendering of the options even if the actual
* object/collection has not changed identity but only a property on the object or an item in the collection
* changes.
* One issue occurs if you want to preselect an option. For example, if you set
* the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
* because the objects are not identical. So by default, you should always reference the item in your collection
* for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
*
* Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
* of the item not by reference, but by the result of the `track by` expression. For example, if your
* collection items have an id property, you would `track by item.id`.
*
* A different issue with objects or collections is that ngModel won't detect if an object property or
* a collection item changes. For that reason, `ngOptions` additionally watches the model using
* `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
* This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
* has not changed identity, but only a property on the object or an item in the collection changes.
*
* Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
* if the model is an array). This means that changing a property deeper inside the object/collection that the
* first level will not trigger a re-rendering.
*
* if the model is an array). This means that changing a property deeper than the first level inside the
* object/collection will not trigger a re-rendering.
*
* ## `select` **`as`**
*
@@ -58,17 +66,13 @@ var ngOptionsMinErr = minErr('ngOptions');
* ### `select` **`as`** and **`track by`**
*
* <div class="alert alert-warning">
* Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
* Be careful when using `select` **`as`** and **`track by`** in the same expression.
* </div>
*
* Consider the following example:
*
* ```html
* <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"></select>
* ```
* Given this array of items on the $scope:
*
* ```js
* $scope.values = [{
* $scope.items = [{
* id: 1,
* label: 'aLabel',
* subItem: { name: 'aSubItem' }
@@ -77,20 +81,33 @@ var ngOptionsMinErr = minErr('ngOptions');
* label: 'bLabel',
* subItem: { name: 'bSubItem' }
* }];
*
* $scope.selected = { name: 'aSubItem' };
* ```
*
* With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
* of the data source (to `item` in this example). To calculate whether an element is selected, we do the
* following:
* This will work:
*
* 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
* 2. Apply **`track by`** to the already selected value in `ngModel`.
* In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
* value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
* a wrong object, the selected element can't be found, `<select>` is always reset to the "not
* selected" option.
* ```html
* <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
* ```
* ```js
* $scope.selected = $scope.items[0];
* ```
*
* but this will not work:
*
* ```html
* <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
* ```
* ```js
* $scope.selected = $scope.items[0].subItem;
* ```
*
* In both examples, the **`track by`** expression is applied successfully to each `item` in the
* `items` array. Because the selected option has been set programmatically in the controller, the
* **`track by`** expression is also applied to the `ngModel` value. In the first example, the
* `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
* no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
* expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
* is not matched against any `<option>` and the `<select>` appears as having no selected value.
*
*
* @param {string} ngModel Assignable angular expression to data-bind to.
@@ -392,17 +409,10 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var optionTemplate = document.createElement('option'),
optGroupTemplate = document.createElement('optgroup');
return {
restrict: 'A',
terminal: true,
require: ['select', '?ngModel'],
link: function(scope, selectElement, attr, ctrls) {
// if ngModel is not defined, we don't need to do anything
var ngModelCtrl = ctrls[1];
if (!ngModelCtrl) return;
function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
var selectCtrl = ctrls[0];
var ngModelCtrl = ctrls[1];
var multiple = attr.multiple;
// The emptyOption allows the application developer to provide their own custom "empty"
@@ -451,7 +461,6 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
unknownOption.remove();
};
// Update the controller methods for multiple selectable options
if (!multiple) {
@@ -579,11 +588,16 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
function updateOptionElement(option, element) {
option.element = element;
element.disabled = option.disabled;
if (option.value !== element.value) element.value = option.selectValue;
// NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
// selects in certain circumstances when multiple selects are next to each other and display
// the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
// See https://github.com/angular/angular.js/issues/11314 for more info.
// This is unfortunately untestable with unit / e2e tests
if (option.label !== element.label) {
element.label = option.label;
element.textContent = option.label;
}
if (option.value !== element.value) element.value = option.selectValue;
}
function addOrReuseElement(parent, current, type, templateElement) {
@@ -621,10 +635,15 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var emptyOption_ = emptyOption && emptyOption[0];
var unknownOption_ = unknownOption && unknownOption[0];
// We cannot rely on the extracted empty option being the same as the compiled empty option,
// because the compiled empty option might have been replaced by a comment because
// it had an "element" transclusion directive on it (such as ngIf)
if (emptyOption_ || unknownOption_) {
while (current &&
(current === emptyOption_ ||
current === unknownOption_)) {
current === unknownOption_ ||
current.nodeType === NODE_TYPE_COMMENT ||
current.value === '')) {
current = current.nextSibling;
}
}
@@ -653,7 +672,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var groupElement;
var optionElement;
if (option.group) {
if (isDefined(option.group)) {
// This option is to live in a group
// See if we have already created this group
@@ -714,14 +733,28 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
// Check to see if the value has changed due to the update to the options
if (!ngModelCtrl.$isEmpty(previousValue)) {
var nextValue = selectCtrl.readValue();
if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
var isNotPrimitive = ngOptions.trackBy || multiple;
if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
ngModelCtrl.$setViewValue(nextValue);
ngModelCtrl.$render();
}
}
}
}
return {
restrict: 'A',
terminal: true,
require: ['select', 'ngModel'],
link: {
pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
// Deactivate the SelectController.register method to prevent
// option directives from accidentally registering themselves
// (and unwanted $destroy handlers etc.)
ctrls[0].registerOption = noop;
},
post: ngOptionsPostLink
}
};
}];
+18 -8
View File
@@ -43,7 +43,7 @@
* Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
* when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
* keys in the order in which they were defined, although there are exceptions when keys are deleted
* and reinstated. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_issues
* and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
*
* If this is not desired, 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
@@ -53,15 +53,21 @@
*
* # Tracking and Duplicates
*
* When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM:
* `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
* the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
*
* * When an item is added, a new instance of the template is added to the DOM.
* * When an item is removed, its template instance is removed from the DOM.
* * When items are reordered, their respective templates are reordered in the DOM.
*
* By default, `ngRepeat` does not allow duplicate items in arrays. This is because when
* there are duplicates, it is not possible to maintain a one-to-one mapping between collection
* items and DOM elements.
* To minimize creation of DOM elements, `ngRepeat` uses a function
* to "keep track" of all items in the collection and their corresponding DOM elements.
* For example, if an item is added to the collection, ngRepeat will know that all other items
* already have DOM elements, and will not re-render them.
*
* The default tracking function (which tracks items by their identity) does not allow
* duplicate items in arrays. This is because when there are duplicates, it is not possible
* to maintain a one-to-one mapping between collection items and DOM elements.
*
* If you do need to repeat duplicate items, you can substitute the default tracking behavior
* with your own using the `track by` expression.
@@ -74,7 +80,7 @@
* </div>
* ```
*
* You may use arbitrary expressions in `track by`, including references to custom functions
* You may also use arbitrary expressions in `track by`, including references to custom functions
* on the scope:
* ```html
* <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
@@ -82,10 +88,14 @@
* </div>
* ```
*
* If you are working with objects that have an identifier property, you can track
* <div class="alert alert-success">
* If you are working with objects that have an identifier property, you should track
* by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
* will not have to rebuild the DOM elements for items it has already rendered, even if the
* JavaScript objects in the collection have been substituted for new ones:
* JavaScript objects in the collection have been substituted for new ones. For large collections,
* this signifincantly improves rendering performance. If you don't have a unique identifier,
* `track by $index` can also provide a performance boost.
* </div>
* ```html
* <div ng-repeat="model in collection track by model.id">
* {{model.name}}
+166 -46
View File
@@ -8,66 +8,186 @@
* @description
* Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
*
* Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
* You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
* as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
*
* If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
* content of this element will be removed before the transcluded content is inserted.
* If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
* that no transcluded content is provided.
*
* @element ANY
*
* @example
<example module="transcludeExample">
<file name="index.html">
<script>
angular.module('transcludeExample', [])
.directive('pane', function(){
return {
restrict: 'E',
transclude: true,
scope: { title:'@' },
template: '<div style="border: 1px solid black;">' +
'<div style="background-color: gray">{{title}}</div>' +
'<ng-transclude></ng-transclude>' +
'</div>'
};
})
.controller('ExampleController', ['$scope', function($scope) {
$scope.title = 'Lorem Ipsum';
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
}]);
</script>
<div ng-controller="ExampleController">
<input ng-model="title" aria-label="title"> <br/>
<textarea ng-model="text" aria-label="text"></textarea> <br/>
<pane title="{{title}}">{{text}}</pane>
</div>
</file>
<file name="protractor.js" type="protractor">
it('should have transcluded', function() {
var titleElement = element(by.model('title'));
titleElement.clear();
titleElement.sendKeys('TITLE');
var textElement = element(by.model('text'));
textElement.clear();
textElement.sendKeys('TEXT');
expect(element(by.binding('title')).getText()).toEqual('TITLE');
expect(element(by.binding('text')).getText()).toEqual('TEXT');
});
</file>
</example>
* @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
* or its value is the same as the name of the attribute then the default slot is used.
*
* @example
* ### Basic transclusion
* This example demonstrates basic transclusion of content into a component directive.
* <example name="simpleTranscludeExample" module="transcludeExample">
* <file name="index.html">
* <script>
* angular.module('transcludeExample', [])
* .directive('pane', function(){
* return {
* restrict: 'E',
* transclude: true,
* scope: { title:'@' },
* template: '<div style="border: 1px solid black;">' +
* '<div style="background-color: gray">{{title}}</div>' +
* '<ng-transclude></ng-transclude>' +
* '</div>'
* };
* })
* .controller('ExampleController', ['$scope', function($scope) {
* $scope.title = 'Lorem Ipsum';
* $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
* }]);
* </script>
* <div ng-controller="ExampleController">
* <input ng-model="title" aria-label="title"> <br/>
* <textarea ng-model="text" aria-label="text"></textarea> <br/>
* <pane title="{{title}}">{{text}}</pane>
* </div>
* </file>
* <file name="protractor.js" type="protractor">
* it('should have transcluded', function() {
* var titleElement = element(by.model('title'));
* titleElement.clear();
* titleElement.sendKeys('TITLE');
* var textElement = element(by.model('text'));
* textElement.clear();
* textElement.sendKeys('TEXT');
* expect(element(by.binding('title')).getText()).toEqual('TITLE');
* expect(element(by.binding('text')).getText()).toEqual('TEXT');
* });
* </file>
* </example>
*
* @example
* ### Transclude fallback content
* This example shows how to use `NgTransclude` with fallback content, that
* is displayed if no transcluded content is provided.
*
* <example module="transcludeFallbackContentExample">
* <file name="index.html">
* <script>
* angular.module('transcludeFallbackContentExample', [])
* .directive('myButton', function(){
* return {
* restrict: 'E',
* transclude: true,
* scope: true,
* template: '<button style="cursor: pointer;">' +
* '<ng-transclude>' +
* '<b style="color: red;">Button1</b>' +
* '</ng-transclude>' +
* '</button>'
* };
* });
* </script>
* <!-- fallback button content -->
* <my-button id="fallback"></my-button>
* <!-- modified button content -->
* <my-button id="modified">
* <i style="color: green;">Button2</i>
* </my-button>
* </file>
* <file name="protractor.js" type="protractor">
* it('should have different transclude element content', function() {
* expect(element(by.id('fallback')).getText()).toBe('Button1');
* expect(element(by.id('modified')).getText()).toBe('Button2');
* });
* </file>
* </example>
*
* @example
* ### Multi-slot transclusion
* This example demonstrates using multi-slot transclusion in a component directive.
* <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
* <file name="index.html">
* <style>
* .title, .footer {
* background-color: gray
* }
* </style>
* <div ng-controller="ExampleController">
* <input ng-model="title" aria-label="title"> <br/>
* <textarea ng-model="text" aria-label="text"></textarea> <br/>
* <pane>
* <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
* <pane-body><p>{{text}}</p></pane-body>
* </pane>
* </div>
* </file>
* <file name="app.js">
* angular.module('multiSlotTranscludeExample', [])
* .directive('pane', function(){
* return {
* restrict: 'E',
* transclude: {
* 'title': '?paneTitle',
* 'body': 'paneBody',
* 'footer': '?paneFooter'
* },
* template: '<div style="border: 1px solid black;">' +
* '<div class="title" ng-transclude="title">Fallback Title</div>' +
* '<div ng-transclude="body"></div>' +
* '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
* '</div>'
* };
* })
* .controller('ExampleController', ['$scope', function($scope) {
* $scope.title = 'Lorem Ipsum';
* $scope.link = "https://google.com";
* $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
* }]);
* </file>
* <file name="protractor.js" type="protractor">
* it('should have transcluded the title and the body', function() {
* var titleElement = element(by.model('title'));
* titleElement.clear();
* titleElement.sendKeys('TITLE');
* var textElement = element(by.model('text'));
* textElement.clear();
* textElement.sendKeys('TEXT');
* expect(element(by.css('.title')).getText()).toEqual('TITLE');
* expect(element(by.binding('text')).getText()).toEqual('TEXT');
* expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
* });
* </file>
* </example>
*/
var ngTranscludeMinErr = minErr('ngTransclude');
var ngTranscludeDirective = ngDirective({
restrict: 'EAC',
link: function($scope, $element, $attrs, controller, $transclude) {
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
// If the attribute is of the form: `ng-transclude="ng-transclude"`
// then treat it like the default
$attrs.ngTransclude = '';
}
function ngTranscludeCloneAttachFn(clone) {
if (clone.length) {
$element.empty();
$element.append(clone);
}
}
if (!$transclude) {
throw minErr('ngTransclude')('orphan',
throw ngTranscludeMinErr('orphan',
'Illegal use of ngTransclude directive in the template! ' +
'No parent directive that requires a transclusion found. ' +
'Element: {0}',
startingTag($element));
}
$transclude(function(clone) {
$element.empty();
$element.append(clone);
});
// If there is no slot name defined or the slot name is not optional
// then transclude the slot
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
$transclude(ngTranscludeCloneAttachFn, null, slotName);
}
});
+58 -53
View File
@@ -2,6 +2,15 @@
var noopNgModelController = { $setViewValue: noop, $render: noop };
function chromeHack(optionElement) {
// Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
// Adding an <option selected="selected"> element to a <select required="required"> should
// automatically select the new element
if (optionElement[0].hasAttribute('selected')) {
optionElement[0].selected = true;
}
}
/**
* @ngdoc type
* @name select.SelectController
@@ -77,6 +86,8 @@ var SelectController =
}
var count = optionsMap.get(value) || 0;
optionsMap.put(value, count + 1);
self.ngModelCtrl.$render();
chromeHack(element);
};
// Tell the select control that an option, with the given value, has been removed
@@ -98,6 +109,39 @@ var SelectController =
self.hasOption = function(value) {
return !!optionsMap.get(value);
};
self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
if (interpolateValueFn) {
// The value attribute is interpolated
var oldVal;
optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
if (isDefined(oldVal)) {
self.removeOption(oldVal);
}
oldVal = newVal;
self.addOption(newVal, optionElement);
});
} else if (interpolateTextFn) {
// The text content is interpolated
optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
optionAttrs.$set('value', newVal);
if (oldVal !== newVal) {
self.removeOption(oldVal);
}
self.addOption(newVal, optionElement);
});
} else {
// The value attribute is static
self.addOption(optionAttrs.value, optionElement);
}
optionElement.on('$destroy', function() {
self.removeOption(optionAttrs.value);
self.ngModelCtrl.$render();
});
};
}];
/**
@@ -143,6 +187,8 @@ var SelectController =
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} multiple Allows multiple options to be selected. The selected values will be
* bound to the model as an array.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds required attribute and required validation constraint to
* the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
@@ -308,7 +354,13 @@ var selectDirective = function() {
restrict: 'E',
require: ['select', '?ngModel'],
controller: SelectController,
link: function(scope, element, attr, ctrls) {
priority: 1,
link: {
pre: selectPreLink
}
};
function selectPreLink(scope, element, attr, ctrls) {
// if ngModel is not defined, we don't need to do anything
var ngModelCtrl = ctrls[1];
@@ -378,7 +430,6 @@ var selectDirective = function() {
}
}
};
};
@@ -386,16 +437,6 @@ var selectDirective = function() {
// of dynamically created (and destroyed) option elements to their containing select
// directive via its controller.
var optionDirective = ['$interpolate', function($interpolate) {
function chromeHack(optionElement) {
// Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
// Adding an <option selected="selected"> element to a <select required="required"> should
// automatically select the new element
if (optionElement[0].hasAttribute('selected')) {
optionElement[0].selected = true;
}
}
return {
restrict: 'E',
priority: 100,
@@ -403,12 +444,12 @@ var optionDirective = ['$interpolate', function($interpolate) {
if (isDefined(attr.value)) {
// If the value attribute is defined, check if it contains an interpolation
var valueInterpolated = $interpolate(attr.value, true);
var interpolateValueFn = $interpolate(attr.value, true);
} else {
// If the value attribute is not defined then we fall back to the
// text content of the option element, which may be interpolated
var interpolateFn = $interpolate(element.text(), true);
if (!interpolateFn) {
var interpolateTextFn = $interpolate(element.text(), true);
if (!interpolateTextFn) {
attr.$set('value', element.text());
}
}
@@ -422,44 +463,8 @@ var optionDirective = ['$interpolate', function($interpolate) {
selectCtrl = parent.data(selectCtrlName) ||
parent.parent().data(selectCtrlName); // in case we are in optgroup
function addOption(optionValue) {
selectCtrl.addOption(optionValue, element);
selectCtrl.ngModelCtrl.$render();
chromeHack(element);
}
// Only update trigger option updates if this is an option within a `select`
// that also has `ngModel` attached
if (selectCtrl && selectCtrl.ngModelCtrl) {
if (valueInterpolated) {
// The value attribute is interpolated
var oldVal;
attr.$observe('value', function valueAttributeObserveAction(newVal) {
if (isDefined(oldVal)) {
selectCtrl.removeOption(oldVal);
}
oldVal = newVal;
addOption(newVal);
});
} else if (interpolateFn) {
// The text content is interpolated
scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
attr.$set('value', newVal);
if (oldVal !== newVal) {
selectCtrl.removeOption(oldVal);
}
addOption(newVal);
});
} else {
// The value attribute is static
addOption(attr.value);
}
element.on('$destroy', function() {
selectCtrl.removeOption(attr.value);
selectCtrl.ngModelCtrl.$render();
});
if (selectCtrl) {
selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
}
};
}
+183 -79
View File
@@ -1,5 +1,9 @@
'use strict';
var MAX_DIGITS = 22;
var DECIMAL_SEP = '.';
var ZERO_CHAR = '0';
/**
* @ngdoc filter
* @name currency
@@ -89,7 +93,7 @@ function currencyFilter($locale) {
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
* If this is not provided then the fraction size is computed from the current locale's number
* formatting pattern. In the case of the default locale, it will be 3.
* @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
* @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
*
* @example
<example module="numberFilterExample">
@@ -124,8 +128,6 @@ function currencyFilter($locale) {
</file>
</example>
*/
numberFilter.$inject = ['$locale'];
function numberFilter($locale) {
var formats = $locale.NUMBER_FORMATS;
@@ -139,92 +141,194 @@ function numberFilter($locale) {
};
}
var DECIMAL_SEP = '.';
function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
if (isObject(number)) return '';
/**
* Parse a number (as a string) into three components that can be used
* for formatting the number.
*
* (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
*
* @param {string} numStr The number to parse
* @return {object} An object describing this number, containing the following keys:
* - d : an array of digits containing leading zeros as necessary
* - i : the number of the digits in `d` that are to the left of the decimal point
* - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
*
*/
function parse(numStr) {
var exponent = 0, digits, numberOfIntegerDigits;
var i, j, zeros;
var isNegative = number < 0;
number = Math.abs(number);
var isInfinity = number === Infinity;
if (!isInfinity && !isFinite(number)) return '';
var numStr = number + '',
formatedText = '',
hasExponent = false,
parts = [];
if (isInfinity) formatedText = '\u221e';
if (!isInfinity && numStr.indexOf('e') !== -1) {
var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
if (match && match[2] == '-' && match[3] > fractionSize + 1) {
number = 0;
} else {
formatedText = numStr;
hasExponent = true;
}
// Decimal point?
if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
numStr = numStr.replace(DECIMAL_SEP, '');
}
if (!isInfinity && !hasExponent) {
var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
// Exponential form?
if ((i = numStr.search(/e/i)) > 0) {
// Work out the exponent.
if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
numberOfIntegerDigits += +numStr.slice(i + 1);
numStr = numStr.substring(0, i);
} else if (numberOfIntegerDigits < 0) {
// There was no decimal point or exponent so it is an integer.
numberOfIntegerDigits = numStr.length;
}
// determine fractionSize if it is not specified
if (isUndefined(fractionSize)) {
fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
}
// Count the number of leading zeros.
for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++);
// safely round numbers in JS without hitting imprecisions of floating-point arithmetics
// inspired by:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
var fraction = ('' + number).split(DECIMAL_SEP);
var whole = fraction[0];
fraction = fraction[1] || '';
var i, pos = 0,
lgroup = pattern.lgSize,
group = pattern.gSize;
if (whole.length >= (lgroup + group)) {
pos = whole.length - lgroup;
for (i = 0; i < pos; i++) {
if ((pos - i) % group === 0 && i !== 0) {
formatedText += groupSep;
}
formatedText += whole.charAt(i);
}
}
for (i = pos; i < whole.length; i++) {
if ((whole.length - i) % lgroup === 0 && i !== 0) {
formatedText += groupSep;
}
formatedText += whole.charAt(i);
}
// format fraction part.
while (fraction.length < fractionSize) {
fraction += '0';
}
if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
if (i == (zeros = numStr.length)) {
// The digits are all zero.
digits = [0];
numberOfIntegerDigits = 1;
} else {
if (fractionSize > 0 && number < 1) {
formatedText = number.toFixed(fractionSize);
number = parseFloat(formatedText);
// Count the number of trailing zeros
zeros--;
while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
// Trailing zeros are insignificant so ignore them
numberOfIntegerDigits -= i;
digits = [];
// Convert string to array of digits without leading/trailing zeros.
for (j = 0; i <= zeros; i++, j++) {
digits[j] = +numStr.charAt(i);
}
}
if (number === 0) {
isNegative = false;
// If the number overflows the maximum allowed digits then use an exponent.
if (numberOfIntegerDigits > MAX_DIGITS) {
digits = digits.splice(0, MAX_DIGITS - 1);
exponent = numberOfIntegerDigits - 1;
numberOfIntegerDigits = 1;
}
parts.push(isNegative ? pattern.negPre : pattern.posPre,
formatedText,
isNegative ? pattern.negSuf : pattern.posSuf);
return parts.join('');
return { d: digits, e: exponent, i: numberOfIntegerDigits };
}
/**
* Round the parsed number to the specified number of decimal places
* This function changed the parsedNumber in-place
*/
function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
var digits = parsedNumber.d;
var fractionLen = digits.length - parsedNumber.i;
// determine fractionSize if it is not specified; `+fractionSize` converts it to a number
fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
// The index of the digit to where rounding is to occur
var roundAt = fractionSize + parsedNumber.i;
var digit = digits[roundAt];
if (roundAt > 0) {
digits.splice(roundAt);
} else {
// We rounded to zero so reset the parsedNumber
parsedNumber.i = 1;
digits.length = roundAt = fractionSize + 1;
for (var i=0; i < roundAt; i++) digits[i] = 0;
}
if (digit >= 5) digits[roundAt - 1]++;
// Pad out with zeros to get the required fraction length
for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
// Do any carrying, e.g. a digit was rounded up to 10
var carry = digits.reduceRight(function(carry, d, i, digits) {
d = d + carry;
digits[i] = d % 10;
return Math.floor(d / 10);
}, 0);
if (carry) {
digits.unshift(carry);
parsedNumber.i++;
}
}
/**
* Format a number into a string
* @param {number} number The number to format
* @param {{
* minFrac, // the minimum number of digits required in the fraction part of the number
* maxFrac, // the maximum number of digits required in the fraction part of the number
* gSize, // number of digits in each group of separated digits
* lgSize, // number of digits in the last group of digits before the decimal separator
* negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
* posPre, // the string to go in front of a positive number
* negSuf, // the string to go after a negative number (e.g. `)`)
* posSuf // the string to go after a positive number
* }} pattern
* @param {string} groupSep The string to separate groups of number (e.g. `,`)
* @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
* @param {[type]} fractionSize The size of the fractional part of the number
* @return {string} The number formatted as a string
*/
function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
var isInfinity = !isFinite(number);
var isZero = false;
var numStr = Math.abs(number) + '',
formattedText = '',
parsedNumber;
if (isInfinity) {
formattedText = '\u221e';
} else {
parsedNumber = parse(numStr);
roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
var digits = parsedNumber.d;
var integerLen = parsedNumber.i;
var exponent = parsedNumber.e;
var decimals = [];
isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
// pad zeros for small numbers
while (integerLen < 0) {
digits.unshift(0);
integerLen++;
}
// extract decimals digits
if (integerLen > 0) {
decimals = digits.splice(integerLen);
} else {
decimals = digits;
digits = [0];
}
// format the integer digits with grouping separators
var groups = [];
if (digits.length > pattern.lgSize) {
groups.unshift(digits.splice(-pattern.lgSize).join(''));
}
while (digits.length > pattern.gSize) {
groups.unshift(digits.splice(-pattern.gSize).join(''));
}
if (digits.length) {
groups.unshift(digits.join(''));
}
formattedText = groups.join(groupSep);
// append the decimal digits
if (decimals.length) {
formattedText += decimalSep + decimals.join('');
}
if (exponent) {
formattedText += 'e+' + exponent;
}
}
if (number < 0 && !isZero) {
return pattern.negPre + formattedText + pattern.negSuf;
} else {
return pattern.posPre + formattedText + pattern.posSuf;
}
}
function padNumber(num, digits, trim) {
@@ -234,7 +338,7 @@ function padNumber(num, digits, trim) {
num = -num;
}
num = '' + num;
while (num.length < digits) num = '0' + num;
while (num.length < digits) num = ZERO_CHAR + num;
if (trim) {
num = num.substr(num.length - digits);
}
+1 -1
View File
@@ -111,7 +111,7 @@ function limitToFilter() {
if (!isArray(input) && !isString(input)) return input;
begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
begin = (begin < 0 && begin >= -input.length) ? input.length + begin : begin;
begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
if (limit >= 0) {
return input.slice(begin, begin + limit);
+93 -69
View File
@@ -9,8 +9,9 @@
* 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.
*
* @param {Array} array The array to sort.
* @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.
*
@@ -41,17 +42,6 @@
* `reverse` is not set, which means it defaults to `false`.
<example module="orderByExample">
<file name="index.html">
<script>
angular.module('orderByExample', [])
.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}];
}]);
</script>
<div ng-controller="ExampleController">
<table class="friend">
<tr>
@@ -67,6 +57,17 @@
</table>
</div>
</file>
<file name="script.js">
angular.module('orderByExample', [])
.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}];
}]);
</file>
</example>
*
* The predicate and reverse parameters can be controlled dynamically through scope properties,
@@ -74,49 +75,24 @@
* @example
<example module="orderByExample">
<file name="index.html">
<script>
angular.module('orderByExample', [])
.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';
$scope.reverse = true;
$scope.order = function(predicate) {
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.predicate = predicate;
};
}]);
</script>
<style type="text/css">
.sortorder:after {
content: '\25b2';
}
.sortorder.reverse:after {
content: '\25bc';
}
</style>
<div ng-controller="ExampleController">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
[ <a href="" ng-click="predicate=''">unsorted</a> ]
<button ng-click="predicate=''">Set to unsorted</button>
<table class="friend">
<tr>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('phone')">Phone Number</a>
<span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('age')">Age</a>
<span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
</th>
<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 | orderBy:predicate:reverse">
<td>{{friend.name}}</td>
@@ -126,6 +102,31 @@
</table>
</div>
</file>
<file name="script.js">
angular.module('orderByExample', [])
.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';
$scope.reverse = true;
$scope.order = function(predicate) {
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.predicate = predicate;
};
}]);
</file>
<file name="style.css">
.sortorder:after {
content: '\25b2';
}
.sortorder.reverse:after {
content: '\25bc';
}
</file>
</example>
*
* It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
@@ -137,21 +138,30 @@
* @example
<example module="orderByExample">
<file name="index.html">
<div ng-controller="ExampleController">
<table class="friend">
<tr>
<th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
(<a href="" ng-click="order('-name',false)">^</a>)</th>
<th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
<th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
</tr>
<tr ng-repeat="friend in friends">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<td>{{friend.age}}</td>
</tr>
</table>
</div>
<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">
@@ -165,19 +175,33 @@
{ name: 'Adam', phone: '555-5678', age: 35 },
{ name: 'Julie', phone: '555-8765', age: 29 }
];
$scope.order = function(predicate, reverse) {
$scope.friends = orderBy($scope.friends, predicate, reverse);
$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',false);
$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) {
if (!(isArrayLike(array))) return array;
if (array == null) return array;
if (!isArrayLike(array)) {
throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
}
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
+33 -40
View File
@@ -345,9 +345,9 @@ function $HttpProvider() {
* Configure `$http` service to return promises without the shorthand methods `success` and `error`.
* This should be used to make sure that applications work without these methods.
*
* Defaults to false. If no value is specified, returns the current configured value.
* Defaults to true. If no value is specified, returns the current configured value.
*
* @param {boolean=} value If true, `$http` will return a normal promise without the `success` and `error` methods.
* @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
*
* @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
* otherwise, returns the current configured value.
@@ -425,28 +425,18 @@ function $HttpProvider() {
*
*
* ## General usage
* The `$http` service is a function which takes a single argument — a configuration object —
* The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object}
* that is used to generate an HTTP request and returns a {@link ng.$q promise}.
*
* ```js
* // Simple GET request example :
* $http.get('/someUrl').
* then(function(response) {
* // Simple GET request example:
* $http({
* method: 'GET',
* url: '/someUrl'
* }).then(function successCallback(response) {
* // this callback will be called asynchronously
* // when the response is available
* }, function(response) {
* // called asynchronously if an error occurs
* // or server returns response with an error status.
* });
* ```
*
* ```js
* // Simple POST request example (passing data) :
* $http.post('/someUrl', {msg:'hello word!'}).
* then(function(response) {
* // this callback will be called asynchronously
* // when the response is available
* }, function(response) {
* }, function errorCallback(response) {
* // called asynchronously if an error occurs
* // or server returns response with an error status.
* });
@@ -466,25 +456,16 @@ function $HttpProvider() {
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
* called for such responses.
*
* ## Writing Unit Tests that use $http
* When unit testing (using {@link ngMock ngMock}), it is necessary to call
* {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
* request using trained responses.
*
* ```
* $httpBackend.expectGET(...);
* $http.get(...);
* $httpBackend.flush();
* ```
*
* ## Shortcut methods
*
* Shortcut methods are also available. All shortcut methods require passing in the URL, and
* request data must be passed in for POST/PUT requests.
* request data must be passed in for POST/PUT requests. An optional config can be passed as the
* last argument.
*
* ```js
* $http.get('/someUrl').then(successCallback);
* $http.post('/someUrl', data).then(successCallback);
* $http.get('/someUrl', config).then(successCallback, errorCallback);
* $http.post('/someUrl', data, config).then(successCallback, errorCallback);
* ```
*
* Complete list of shortcut methods:
@@ -498,6 +479,17 @@ function $HttpProvider() {
* - {@link ng.$http#patch $http.patch}
*
*
* ## Writing Unit Tests that use $http
* When unit testing (using {@link ngMock ngMock}), it is necessary to call
* {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
* request using trained responses.
*
* ```
* $httpBackend.expectGET(...);
* $http.get(...);
* $httpBackend.flush();
* ```
*
* ## Deprecation Notice
* <div class="alert alert-danger">
* The `$http` legacy promise methods `success` and `error` have been deprecated.
@@ -529,7 +521,7 @@ function $HttpProvider() {
*
* ```
* module.run(function($http) {
* $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
* $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
* });
* ```
*
@@ -655,7 +647,7 @@ function $HttpProvider() {
*
* There are two kinds of interceptors (and two kinds of rejection interceptors):
*
* * `request`: interceptors get called with a http `config` object. The function is free to
* * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
* modify the `config` object or create a new one. The function needs to return the `config`
* object directly, or a promise containing the `config` or a new `config` object.
* * `requestError`: interceptor gets called when a previous interceptor threw an error or
@@ -922,10 +914,14 @@ function $HttpProvider() {
*/
function $http(requestConfig) {
if (!angular.isObject(requestConfig)) {
if (!isObject(requestConfig)) {
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
}
if (!isString(requestConfig.url)) {
throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
}
var config = extend({
method: 'get',
transformRequest: defaults.transformRequest,
@@ -1007,11 +1003,8 @@ function $HttpProvider() {
function transformResponse(response) {
// make a copy since the response must be cacheable
var resp = extend({}, response);
if (!response.data) {
resp.data = response.data;
} else {
resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
}
resp.data = transformData(response.data, response.headers, response.status,
config.transformResponse);
return (isSuccess(response.status))
? resp
: $q.reject(resp);
+31 -5
View File
@@ -1,7 +1,32 @@
'use strict';
function createXhr() {
return new window.XMLHttpRequest();
/**
* @ngdoc service
* @name $xhrFactory
*
* @description
* Factory function used to create XMLHttpRequest objects.
*
* Replace or decorate this service to create your own custom XMLHttpRequest objects.
*
* ```
* angular.module('myApp', [])
* .factory('$xhrFactory', function() {
* return function createXhr(method, url) {
* return new window.XMLHttpRequest({mozSystem: true});
* };
* });
* ```
*
* @param {string} method HTTP method of the request (GET, POST, PUT, ..)
* @param {string} url URL of the request.
*/
function $xhrFactoryProvider() {
this.$get = function() {
return function createXhr() {
return new window.XMLHttpRequest();
};
};
}
/**
@@ -9,6 +34,7 @@ function createXhr() {
* @name $httpBackend
* @requires $window
* @requires $document
* @requires $xhrFactory
*
* @description
* HTTP backend used by the {@link ng.$http service} that delegates to
@@ -21,8 +47,8 @@ function createXhr() {
* $httpBackend} which can be trained with responses.
*/
function $HttpBackendProvider() {
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
}];
}
@@ -46,7 +72,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
});
} else {
var xhr = createXhr();
var xhr = createXhr(method, url);
xhr.open(method, url, true);
forEach(headers, function(value, key) {
+30
View File
@@ -20,6 +20,14 @@ $interpolateMinErr.interr = function(text, err) {
*
* Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
*
* <div class="alert alert-danger">
* This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
* template within a Python Jinja template (or any other template language). Mixing templating
* languages is **very dangerous**. The embedding template language will not safely escape Angular
* expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
* security bugs!
* </div>
*
* @example
<example module="customInterpolationApp">
<file name="index.html">
@@ -120,6 +128,15 @@ function $InterpolateProvider() {
return value;
}
//TODO: this is the same as the constantWatchDelegate in parse.js
function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
var unwatch;
return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
unwatch();
return constantInterp(scope);
}, listener, objectEquality);
}
/**
* @ngdoc service
* @name $interpolate
@@ -215,6 +232,19 @@ function $InterpolateProvider() {
* - `context`: evaluation context for all expressions embedded in the interpolated text
*/
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
// Provide a quick exit and simplified result function for text with no interpolation
if (!text.length || text.indexOf(startSymbol) === -1) {
var constantInterp;
if (!mustHaveExpression) {
var unescapedText = unescapeText(text);
constantInterp = valueFn(unescapedText);
constantInterp.exp = text;
constantInterp.expressions = [];
constantInterp.$$watchDelegate = constantWatchDelegate;
}
return constantInterp;
}
allOrNothing = !!allOrNothing;
var startIndex,
endIndex,
+15 -6
View File
@@ -2,8 +2,8 @@
function $IntervalProvider() {
this.$get = ['$rootScope', '$window', '$q', '$$q',
function($rootScope, $window, $q, $$q) {
this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
function($rootScope, $window, $q, $$q, $browser) {
var intervals = {};
@@ -144,11 +144,12 @@ function $IntervalProvider() {
count = isDefined(count) ? count : 0;
promise.then(null, null, (!hasParams) ? fn : function() {
fn.apply(null, args);
});
promise.$$intervalId = setInterval(function tick() {
if (skipApply) {
$browser.defer(callback);
} else {
$rootScope.$evalAsync(callback);
}
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
@@ -164,6 +165,14 @@ function $IntervalProvider() {
intervals[promise.$$intervalId] = deferred;
return promise;
function callback() {
if (!hasParams) {
fn(iteration);
} else {
fn.apply(null, args);
}
}
}
+5 -5
View File
@@ -574,9 +574,9 @@ var locationPrototype = {
* @description
* This method is getter / setter.
*
* Return hash fragment when called without any parameter.
* Returns the hash fragment when called without any parameters.
*
* Change hash fragment when called with parameter and return `$location`.
* Changes the hash fragment when called with a parameter and returns `$location`.
*
*
* ```js
@@ -597,8 +597,8 @@ var locationPrototype = {
* @name $location#replace
*
* @description
* If called, all changes to $location during current `$digest` will be replacing current history
* record, instead of adding new one.
* If called, all changes to $location during the current `$digest` will replace the current history
* record, instead of adding a new one.
*/
replace: function() {
this.$$replace = true;
@@ -918,7 +918,7 @@ function $LocationProvider() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
var defaultPrevented;
newUrl = trimEmptyHash(newUrl);
$location.$$parse(newUrl);
$location.$$state = newState;
+83 -22
View File
@@ -38,20 +38,30 @@ var $parseMinErr = minErr('$parse');
function ensureSafeMemberName(name, fullExpression) {
if (name === "__defineGetter__" || name === "__defineSetter__"
|| name === "__lookupGetter__" || name === "__lookupSetter__"
|| name === "__proto__") {
throw $parseMinErr('isecfld',
'Attempting to access a disallowed field in Angular expressions! '
+ 'Expression: {0}', fullExpression);
}
return name;
}
function getStringValue(name, fullExpression) {
// From the JavaScript docs:
// Property names must be strings. This means that non-string objects cannot be used
// as keys in an object. Any non-string object, including a number, is typecasted
// into a string via the toString method.
//
// So, to ensure that we are checking the same `name` that JavaScript would use,
// we cast it to a string, if possible
name = (isObject(name) && name.toString) ? name.toString() : name;
if (name === "__defineGetter__" || name === "__defineSetter__"
|| name === "__lookupGetter__" || name === "__lookupSetter__"
|| name === "__proto__") {
throw $parseMinErr('isecfld',
'Attempting to access a disallowed field in Angular expressions! '
// we cast it to a string, if possible.
// Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
// this is, this will handle objects that misbehave.
name = name + '';
if (!isString(name)) {
throw $parseMinErr('iseccst',
'Cannot convert object to primitive value! '
+ 'Expression: {0}', fullExpression);
}
return name;
@@ -102,6 +112,16 @@ function ensureSafeFunction(obj, fullExpression) {
}
}
function ensureSafeAssignContext(obj, fullExpression) {
if (obj) {
if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
throw $parseMinErr('isecaf',
'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
}
}
}
var OPERATORS = createMap();
forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
@@ -305,6 +325,7 @@ AST.ArrayExpression = 'ArrayExpression';
AST.Property = 'Property';
AST.ObjectExpression = 'ObjectExpression';
AST.ThisExpression = 'ThisExpression';
AST.LocalsExpression = 'LocalsExpression';
// Internal use only
AST.NGValueParameter = 'NGValueParameter';
@@ -605,7 +626,8 @@ AST.prototype = {
'false': { type: AST.Literal, value: false },
'null': { type: AST.Literal, value: null },
'undefined': {type: AST.Literal, value: undefined },
'this': {type: AST.ThisExpression }
'this': {type: AST.ThisExpression },
'$locals': {type: AST.LocalsExpression }
}
};
@@ -725,6 +747,10 @@ function findConstantAndWatchExpressions(ast, $filter) {
ast.constant = false;
ast.toWatch = [];
break;
case AST.LocalsExpression:
ast.constant = false;
ast.toWatch = [];
break;
}
}
@@ -816,6 +842,8 @@ ASTCompiler.prototype = {
'ensureSafeMemberName',
'ensureSafeObject',
'ensureSafeFunction',
'getStringValue',
'ensureSafeAssignContext',
'ifDefined',
'plus',
'text',
@@ -824,6 +852,8 @@ ASTCompiler.prototype = {
ensureSafeMemberName,
ensureSafeObject,
ensureSafeFunction,
getStringValue,
ensureSafeAssignContext,
ifDefined,
plusFn,
expression);
@@ -964,9 +994,13 @@ ASTCompiler.prototype = {
intoId = intoId || this.nextId();
self.recurse(ast.object, left, undefined, function() {
self.if_(self.notNull(left), function() {
if (create && create !== 1) {
self.addEnsureSafeAssignContext(left);
}
if (ast.computed) {
right = self.nextId();
self.recurse(ast.property, right);
self.getStringValue(right);
self.addEnsureSafeMemberName(right);
if (create && create !== 1) {
self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
@@ -1044,12 +1078,13 @@ ASTCompiler.prototype = {
right = this.nextId();
left = {};
if (!isAssignable(ast.left)) {
throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
}
this.recurse(ast.left, undefined, left, function() {
self.if_(self.notNull(left.context), function() {
self.recurse(ast.right, right);
self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
self.addEnsureSafeAssignContext(left.context);
expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
self.assign(intoId, expression);
recursionFn(intoId || expression);
@@ -1085,6 +1120,10 @@ ASTCompiler.prototype = {
this.assign(intoId, 's');
recursionFn('s');
break;
case AST.LocalsExpression:
this.assign(intoId, 'l');
recursionFn('l');
break;
case AST.NGValueParameter:
this.assign(intoId, 'v');
recursionFn('v');
@@ -1175,6 +1214,10 @@ ASTCompiler.prototype = {
this.current().body.push(this.ensureSafeFunction(item), ';');
},
addEnsureSafeAssignContext: function(item) {
this.current().body.push(this.ensureSafeAssignContext(item), ';');
},
ensureSafeObject: function(item) {
return 'ensureSafeObject(' + item + ',text)';
},
@@ -1187,6 +1230,14 @@ ASTCompiler.prototype = {
return 'ensureSafeFunction(' + item + ',text)';
},
getStringValue: function(item) {
this.assign(item, 'getStringValue(' + item + ',text)');
},
ensureSafeAssignContext: function(item) {
return 'ensureSafeAssignContext(' + item + ',text)';
},
lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
var self = this;
return function() {
@@ -1364,6 +1415,7 @@ ASTInterpreter.prototype = {
var lhs = left(scope, locals, assign, inputs);
var rhs = right(scope, locals, assign, inputs);
ensureSafeObject(lhs.value, self.expression);
ensureSafeAssignContext(lhs.context);
lhs.context[lhs.name] = rhs;
return context ? {value: rhs} : rhs;
};
@@ -1399,6 +1451,10 @@ ASTInterpreter.prototype = {
return function(scope) {
return context ? {value: scope} : scope;
};
case AST.LocalsExpression:
return function(scope, locals) {
return context ? {value: locals} : locals;
};
case AST.NGValueParameter:
return function(scope, locals, assign, inputs) {
return context ? {value: assign} : assign;
@@ -1561,9 +1617,13 @@ ASTInterpreter.prototype = {
var value;
if (lhs != null) {
rhs = right(scope, locals, assign, inputs);
rhs = getStringValue(rhs);
ensureSafeMemberName(rhs, expression);
if (create && create !== 1 && lhs && !(lhs[rhs])) {
lhs[rhs] = {};
if (create && create !== 1) {
ensureSafeAssignContext(lhs);
if (lhs && !(lhs[rhs])) {
lhs[rhs] = {};
}
}
value = lhs[rhs];
ensureSafeObject(value, expression);
@@ -1578,8 +1638,11 @@ ASTInterpreter.prototype = {
nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
return function(scope, locals, assign, inputs) {
var lhs = left(scope, locals, assign, inputs);
if (create && create !== 1 && lhs && !(lhs[right])) {
lhs[right] = {};
if (create && create !== 1) {
ensureSafeAssignContext(lhs);
if (lhs && !(lhs[right])) {
lhs[right] = {};
}
}
var value = lhs != null ? lhs[right] : undefined;
if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
@@ -1735,7 +1798,7 @@ function $ParseProvider() {
return addInterceptor(exp, interceptorFn);
default:
return noop;
return addInterceptor(noop, interceptorFn);
}
};
@@ -1854,25 +1917,22 @@ function $ParseProvider() {
function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
var unwatch;
return unwatch = scope.$watch(function constantWatch(scope) {
return parsedExpression(scope);
}, function constantListener(value, old, scope) {
if (isFunction(listener)) {
listener.apply(this, arguments);
}
unwatch();
}, objectEquality);
return parsedExpression(scope);
}, listener, objectEquality);
}
function addInterceptor(parsedExpression, interceptorFn) {
if (!interceptorFn) return parsedExpression;
var watchDelegate = parsedExpression.$$watchDelegate;
var useInputs = false;
var regularWatch =
watchDelegate !== oneTimeLiteralWatchDelegate &&
watchDelegate !== oneTimeWatchDelegate;
var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
var value = parsedExpression(scope, locals, assign, inputs);
var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
return interceptorFn(value, scope, locals);
} : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
var value = parsedExpression(scope, locals, assign, inputs);
@@ -1890,6 +1950,7 @@ function $ParseProvider() {
// If there is an interceptor, but no watchDelegate then treat the interceptor like
// we treat filters - it is assumed to be a pure function unless flagged with $stateful
fn.$$watchDelegate = inputsWatchDelegate;
useInputs = !parsedExpression.inputs;
fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
}
+24 -22
View File
@@ -53,6 +53,8 @@
*
* Note: progress/notify callbacks are not currently supported via the ES6-style interface.
*
* Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
*
* However, the more traditional CommonJS-style usage is still available, and documented below.
*
* [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
@@ -241,18 +243,6 @@ function $$QProvider() {
*/
function qFactory(nextTick, exceptionHandler) {
var $qMinErr = minErr('$q', TypeError);
function callOnce(self, resolveFn, rejectFn) {
var called = false;
function wrap(fn) {
return function(value) {
if (called) return;
called = true;
fn.call(self, value);
};
}
return [wrap(resolveFn), wrap(rejectFn)];
}
/**
* @ngdoc method
@@ -265,7 +255,12 @@ function qFactory(nextTick, exceptionHandler) {
* @returns {Deferred} Returns a new instance of deferred.
*/
var defer = function() {
return new Deferred();
var d = new Deferred();
//Necessary to support unbound execution :/
d.resolve = simpleBind(d, d.resolve);
d.reject = simpleBind(d, d.reject);
d.notify = simpleBind(d, d.notify);
return d;
};
function Promise() {
@@ -338,10 +333,6 @@ function qFactory(nextTick, exceptionHandler) {
function Deferred() {
this.promise = new Promise();
//Necessary to support unbound execution :/
this.resolve = simpleBind(this, this.resolve);
this.reject = simpleBind(this, this.reject);
this.notify = simpleBind(this, this.notify);
}
extend(Deferred.prototype, {
@@ -359,23 +350,34 @@ function qFactory(nextTick, exceptionHandler) {
},
$$resolve: function(val) {
var then, fns;
fns = callOnce(this, this.$$resolve, this.$$reject);
var then;
var that = this;
var done = false;
try {
if ((isObject(val) || isFunction(val))) then = val && val.then;
if (isFunction(then)) {
this.promise.$$state.status = -1;
then.call(val, fns[0], fns[1], this.notify);
then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
} else {
this.promise.$$state.value = val;
this.promise.$$state.status = 1;
scheduleProcessQueue(this.promise.$$state);
}
} catch (e) {
fns[1](e);
rejectPromise(e);
exceptionHandler(e);
}
function resolvePromise(val) {
if (done) return;
done = true;
that.$$resolve(val);
}
function rejectPromise(val) {
if (done) return;
done = true;
that.$$reject(val);
}
},
reject: function(reason) {
+35 -19
View File
@@ -14,15 +14,15 @@
* exposed as $$____ properties
*
* Loop operations are optimized by using while(count--) { ... }
* - this means that in order to keep the same order of execution as addition we have to add
* - This means that in order to keep the same order of execution as addition we have to add
* items to the array at the beginning (unshift) instead of at the end (push)
*
* Child scopes are created and removed often
* - Using an array would be slow since inserts in middle are expensive so we use linked list
* - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
*
* There are few watches then a lot of observers. This is why you don't want the observer to be
* implemented in the same way as watch. Watch requires return of initialization function which
* are expensive to construct.
* There are fewer watches than observers. This is why you don't want the observer to be implemented
* in the same way as watch. Watch requires return of the initialization function which is expensive
* to construct.
*/
@@ -64,7 +64,7 @@
* Every application has a single root {@link ng.$rootScope.Scope scope}.
* All other scopes are descendant scopes of the root scope. Scopes provide separation
* between the model and the view, via a mechanism for watching the model for changes.
* They also provide an event emission/broadcast and subscription facility. See the
* They also provide event emission/broadcast and subscription facility. See the
* {@link guide/scope developer guide on scopes}.
*/
function $RootScopeProvider() {
@@ -94,13 +94,36 @@ function $RootScopeProvider() {
return ChildScope;
}
this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
function($injector, $exceptionHandler, $parse, $browser) {
this.$get = ['$exceptionHandler', '$parse', '$browser',
function($exceptionHandler, $parse, $browser) {
function destroyChildScope($event) {
$event.currentScope.$$destroyed = true;
}
function cleanUpScope($scope) {
if (msie === 9) {
// There is a memory leak in IE9 if all child scopes are not disconnected
// completely when a scope is destroyed. So this code will recurse up through
// all this scopes children
//
// See issue https://github.com/angular/angular.js/issues/10706
$scope.$$childHead && cleanUpScope($scope.$$childHead);
$scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
}
// The code below works around IE9 and V8's memory leaks
//
// See:
// - https://code.google.com/p/v8/issues/detail?id=2073#c26
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
$scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
$scope.$$childTail = $scope.$root = $scope.$$watchers = null;
}
/**
* @ngdoc type
* @name $rootScope.Scope
@@ -356,7 +379,7 @@ function $RootScopeProvider() {
* - `newVal` contains the current value of the `watchExpression`
* - `oldVal` contains the previous value of the `watchExpression`
* - `scope` refers to the current scope
* @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
* @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
* comparing for reference equality.
* @returns {function()} Returns a deregistration function for this listener.
*/
@@ -897,16 +920,9 @@ function $RootScopeProvider() {
this.$on = this.$watch = this.$watchGroup = function() { return noop; };
this.$$listeners = {};
// All of the code below is bogus code that works around V8's memory leak via optimized code
// and inline caches.
//
// see:
// - https://code.google.com/p/v8/issues/detail?id=2073#c26
// - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
// - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
this.$$childTail = this.$root = this.$$watchers = null;
// Disconnect the next sibling to prevent `cleanUpScope` destroying those too
this.$$nextSibling = null;
cleanUpScope(this);
},
/**
+6 -1
View File
@@ -342,6 +342,11 @@ function $SceDelegateProvider() {
* returns the originally supplied value if the queried context type is a supertype of the
* created type. If this condition isn't satisfied, throws an exception.
*
* <div class="alert alert-danger">
* Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
* (XSS) vulnerability in your application.
* </div>
*
* @param {string} type The kind of context in which this value is to be used.
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
* `$sceDelegate.trustAs`} call.
@@ -484,7 +489,7 @@ function $SceDelegateProvider() {
* By default, Angular only loads templates from the same domain and protocol as the application
* document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
* $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
* protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
* protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
* them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
*
* *Please note*:
+56 -21
View File
@@ -3,26 +3,63 @@
var $compileMinErr = minErr('$compile');
/**
* @ngdoc service
* @name $templateRequest
*
* @ngdoc provider
* @name $templateRequestProvider
* @description
* The `$templateRequest` service runs security checks then downloads the provided template using
* `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
* fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
* exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
* contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
* when `tpl` is of type string and `$templateCache` has the matching entry.
* Used to configure the options passed to the {@link $http} service when making a template request.
*
* @param {string|TrustedResourceUrl} tpl The HTTP request template URL
* @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
*
* @return {Promise} a promise for the HTTP response data of the given URL.
*
* @property {number} totalPendingRequests total amount of pending template requests being downloaded.
* For example, it can be used for specifying the "Accept" header that is sent to the server, when
* requesting a template.
*/
function $TemplateRequestProvider() {
var httpOptions;
/**
* @ngdoc method
* @name $templateRequestProvider#httpOptions
* @description
* The options to be passed to the {@link $http} service when making the request.
* You can use this to override options such as the "Accept" header for template requests.
*
* The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
* options if not overridden here.
*
* @param {string=} value new value for the {@link $http} options.
* @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
*/
this.httpOptions = function(val) {
if (val) {
httpOptions = val;
return this;
}
return httpOptions;
};
/**
* @ngdoc service
* @name $templateRequest
*
* @description
* The `$templateRequest` service runs security checks then downloads the provided template using
* `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
* fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
* exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
* contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
* when `tpl` is of type string and `$templateCache` has the matching entry.
*
* If you want to pass custom options to the `$http` service, such as setting the Accept header you
* can configure this via {@link $templateRequestProvider#httpOptions}.
*
* @param {string|TrustedResourceUrl} tpl The HTTP request template URL
* @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
*
* @return {Promise} a promise for the HTTP response data of the given URL.
*
* @property {number} totalPendingRequests total amount of pending template requests being downloaded.
*/
this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
function handleRequestFn(tpl, ignoreRequestError) {
handleRequestFn.totalPendingRequests++;
@@ -45,12 +82,10 @@ function $TemplateRequestProvider() {
transformResponse = null;
}
var httpOptions = {
cache: $templateCache,
transformResponse: transformResponse
};
return $http.get(tpl, httpOptions)
return $http.get(tpl, extend({
cache: $templateCache,
transformResponse: transformResponse
}, httpOptions))
['finally'](function() {
handleRequestFn.totalPendingRequests--;
})
+2 -2
View File
@@ -33,8 +33,8 @@ function $TimeoutProvider() {
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
* @param {...*=} Pass additional parameters to the executed function.
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
* promise will be resolved with is the return value of the `fn` function.
* @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
* will be resolved with the return value of the `fn` function.
*
*/
function timeout(fn, delay, invokeApply) {
+2
View File
@@ -7,6 +7,7 @@
"angular": false,
"noop": false,
"copy": false,
"forEach": false,
"extend": false,
"jqLite": false,
@@ -28,6 +29,7 @@
"REMOVE_CLASS_SUFFIX": false,
"EVENT_CLASS_PREFIX": false,
"ACTIVE_CLASS_SUFFIX": false,
"PREPARE_CLASS_SUFFIX": false,
"TRANSITION_DURATION_PROP": false,
"TRANSITION_DELAY_PROP": false,
+57 -6
View File
@@ -186,8 +186,10 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
*
* * `event` - The DOM event (e.g. enter, leave, move). When used, a generated CSS class of `ng-EVENT` and `ng-EVENT-active` will be applied
* to the element during the animation. Multiple events can be provided when spaces are used as a separator. (Note that this will not perform any DOM operation.)
* * `structural` - Indicates that the `ng-` prefix will be added to the event class. Setting to `false` or omitting will turn `ng-EVENT` and
* `ng-EVENT-active` in `EVENT` and `EVENT-active`. Unused if `event` is omitted.
* * `easing` - The CSS easing value that will be applied to the transition or keyframe animation (or both).
* * `transition` - The raw CSS transition style that will be used (e.g. `1s linear all`).
* * `transitionStyle` - The raw CSS transition style that will be used (e.g. `1s linear all`).
* * `keyframeStyle` - The raw CSS keyframe animation style that will be used (e.g. `1s my_animation linear`).
* * `from` - The starting CSS styles (a key/value object) that will be applied at the start of the animation.
* * `to` - The ending CSS styles (a key/value object) that will be applied across the animation via a CSS transition.
@@ -202,8 +204,12 @@ var ANIMATE_TIMER_KEY = '$$animateCss';
* * `stagger` - A numeric time value representing the delay between successively animated elements
* ({@link ngAnimate#css-staggering-animations Click here to learn how CSS-based staggering works in ngAnimate.})
* * `staggerIndex` - The numeric index representing the stagger item (e.g. a value of 5 is equal to the sixth item in the stagger; therefore when a
* * `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
* `stagger` option value of `0.1` is used then there will be a stagger delay of `600ms`)
* * `applyClassesEarly` - Whether or not the classes being added or removed will be used when detecting the animation. This is set by `$animate` when enter/leave/move animations are fired to ensure that the CSS classes are resolved in time. (Note that this will prevent any transitions from occuring on the classes being added and removed.)
* * `cleanupStyles` - Whether or not the provided `from` and `to` styles will be removed once
* the animation is closed. This is useful for when the styles are used purely for the sake of
* the animation and do not have a lasting visual effect on the element (e.g. a colapse and open animation).
* By default this value is set to `false`.
*
* @return {object} an object with start and end methods and details about the animation.
*
@@ -324,6 +330,23 @@ function createLocalCacheLookup() {
};
}
// we do not reassign an already present style value since
// if we detect the style property value again we may be
// detecting styles that were added via the `from` styles.
// We make use of `isDefined` here since an empty string
// or null value (which is what getPropertyValue will return
// for a non-existing style) will still be marked as a valid
// value for the style (a falsy value implies that the style
// is to be removed at the end of the animation). If we had a simple
// "OR" statement then it would not be enough to catch that.
function registerRestorableStyles(backup, node, properties) {
forEach(properties, function(prop) {
backup[prop] = isDefined(backup[prop])
? backup[prop]
: node.style.getPropertyValue(prop);
});
}
var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
var gcsLookup = createLocalCacheLookup();
var gcsStaggerLookup = createLocalCacheLookup();
@@ -423,7 +446,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
return timings;
}
return function init(element, options) {
return function init(element, initialOptions) {
// we always make a copy of the options since
// there should never be any side effects on
// the input data when running `$animateCss`.
var options = copy(initialOptions);
var restoreStyles = {};
var node = getDomNode(element);
if (!node
|| !node.parentNode
@@ -598,7 +627,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
if (options.delay != null) {
var delayStyle = parseFloat(options.delay);
var delayStyle;
if (typeof options.delay !== "boolean") {
delayStyle = parseFloat(options.delay);
// number in options.delay means we have to recalculate the delay for the closing timeout
maxDelay = Math.max(delayStyle, 0);
}
if (flags.applyTransitionDelay) {
temporaryStyles.push(getCssDelayStyle(delayStyle));
@@ -625,7 +659,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
stagger.animationDuration === 0;
}
applyAnimationFromStyles(element, options);
if (options.from) {
if (options.cleanupStyles) {
registerRestorableStyles(restoreStyles, node, Object.keys(options.from));
}
applyAnimationFromStyles(element, options);
}
if (flags.blockTransition || flags.blockKeyframeAnimation) {
applyBlocking(maxDuration);
@@ -692,6 +731,13 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
applyAnimationClasses(element, options);
applyAnimationStyles(element, options);
if (Object.keys(restoreStyles).length) {
forEach(restoreStyles, function(value, prop) {
value ? node.style.setProperty(prop, value)
: node.style.removeProperty(prop);
});
}
// the reason why we have this option is to allow a synchronous closing callback
// that is fired as SOON as the animation ends (when the CSS is removed) or if
// the animation never takes off at all. A good example is a leave animation since
@@ -886,7 +932,12 @@ var $AnimateCssProvider = ['$animateProvider', function($animateProvider) {
}
element.on(events.join(' '), onAnimationProgress);
applyAnimationToStyles(element, options);
if (options.to) {
if (options.cleanupStyles) {
registerRestorableStyles(restoreStyles, node, Object.keys(options.to));
}
applyAnimationToStyles(element, options);
}
}
function onAnimationExpired() {
+13 -4
View File
@@ -9,16 +9,25 @@ var $$AnimateCssDriverProvider = ['$$animationProvider', function($$animationPro
var NG_OUT_ANCHOR_CLASS_NAME = 'ng-anchor-out';
var NG_IN_ANCHOR_CLASS_NAME = 'ng-anchor-in';
this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$$body', '$sniffer', '$$jqLite',
function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $$body, $sniffer, $$jqLite) {
function isDocumentFragment(node) {
return node.parentNode && node.parentNode.nodeType === 11;
}
this.$get = ['$animateCss', '$rootScope', '$$AnimateRunner', '$rootElement', '$sniffer', '$$jqLite', '$document',
function($animateCss, $rootScope, $$AnimateRunner, $rootElement, $sniffer, $$jqLite, $document) {
// only browsers that support these properties can render animations
if (!$sniffer.animations && !$sniffer.transitions) return noop;
var bodyNode = getDomNode($$body);
var bodyNode = $document[0].body;
var rootNode = getDomNode($rootElement);
var rootBodyElement = jqLite(bodyNode.parentNode === rootNode ? bodyNode : rootNode);
var rootBodyElement = jqLite(
// this is to avoid using something that exists outside of the body
// we also special case the doc fragement case because our unit test code
// appends the $rootElement to the body after the app has been bootstrapped
isDocumentFragment(rootNode) || bodyNode.contains(rootNode) ? rootNode : bodyNode
);
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
+58 -23
View File
@@ -66,15 +66,33 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return (nO.addClass && nO.addClass === cO.removeClass) || (nO.removeClass && nO.removeClass === cO.addClass);
});
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$body', '$$HashMap',
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
'$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
function($$rAF, $rootScope, $rootElement, $document, $$body, $$HashMap,
function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
$$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow) {
var activeAnimationsLookup = new $$HashMap();
var disabledElementsLookup = new $$HashMap();
var animationsEnabled = null;
function postDigestTaskFactory() {
var postDigestCalled = false;
return function(fn) {
// we only issue a call to postDigest before
// it has first passed. This prevents any callbacks
// from not firing once the animation has completed
// since it will be out of the digest cycle.
if (postDigestCalled) {
fn();
} else {
$rootScope.$$postDigest(function() {
postDigestCalled = true;
fn();
});
}
};
}
// Wait until all directive and route-related templates are downloaded and
// compiled. The $templateRequest.totalPendingRequests variable keeps track of
// all of the remote templates being currently downloaded. If there are no
@@ -121,8 +139,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return mergeAnimationOptions(element, options, {});
}
function findCallbacks(element, event) {
function findCallbacks(parent, element, event) {
var targetNode = getDomNode(element);
var targetParentNode = getDomNode(parent);
var matches = [];
var entries = callbackRegistry[event];
@@ -130,6 +149,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
forEach(entries, function(entry) {
if (entry.node.contains(targetNode)) {
matches.push(entry.callback);
} else if (event === 'leave' && entry.node.contains(targetParentNode)) {
matches.push(entry.callback);
}
});
}
@@ -137,14 +158,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return matches;
}
function triggerCallback(event, element, phase, data) {
$$rAF(function() {
forEach(findCallbacks(element, event), function(callback) {
callback(element, phase, data);
});
});
}
return {
on: function(event, container, callback) {
var node = extractElementNode(container);
@@ -225,7 +238,12 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
};
function queueAnimation(element, event, options) {
function queueAnimation(element, event, initialOptions) {
// we always make a copy of the options since
// there should never be any side effects on
// the input data when running `$animateCss`.
var options = copy(initialOptions);
var node, parent;
element = stripCommentsFromElement(element);
if (element) {
@@ -239,6 +257,9 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// These methods will become available after the digest has passed
var runner = new $$AnimateRunner();
// this is used to trigger callbacks in postDigest mode
var runInNextPostDigestOrNow = postDigestTaskFactory();
if (isArray(options.addClass)) {
options.addClass = options.addClass.join(' ');
}
@@ -459,7 +480,20 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return runner;
function notifyProgress(runner, event, phase, data) {
triggerCallback(event, element, phase, data);
runInNextPostDigestOrNow(function() {
var callbacks = findCallbacks(parent, element, event);
if (callbacks.length) {
// do not optimize this call here to RAF because
// we don't know how heavy the callback code here will
// be and if this code is buffered then this can
// lead to a performance regression.
$$rAF(function() {
forEach(callbacks, function(callback) {
callback(element, phase, data);
});
});
}
});
runner.progress(event, phase, data);
}
@@ -478,15 +512,15 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
forEach(children, function(child) {
var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME));
var animationDetails = activeAnimationsLookup.get(child);
switch (state) {
case RUNNING_STATE:
animationDetails.runner.end();
/* falls through */
case PRE_DIGEST_STATE:
if (animationDetails) {
if (animationDetails) {
switch (state) {
case RUNNING_STATE:
animationDetails.runner.end();
/* falls through */
case PRE_DIGEST_STATE:
activeAnimationsLookup.remove(child);
}
break;
break;
}
}
});
}
@@ -502,7 +536,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
function areAnimationsAllowed(element, parentElement, event) {
var bodyElementDetected = isMatchingElement(element, $$body) || element[0].nodeName === 'HTML';
var bodyElement = jqLite($document[0].body);
var bodyElementDetected = isMatchingElement(element, bodyElement) || element[0].nodeName === 'HTML';
var rootElementDetected = isMatchingElement(element, $rootElement);
var parentAnimationDetected = false;
var animateChildren;
@@ -558,7 +593,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
if (!bodyElementDetected) {
// we also need to ensure that the element is or will be apart of the body element
// otherwise it is pointless to even issue an animation to be rendered
bodyElementDetected = isMatchingElement(parentElement, $$body);
bodyElementDetected = isMatchingElement(parentElement, bodyElement);
}
parentElement = parentElement.parent();
-166
View File
@@ -1,166 +0,0 @@
'use strict';
var $$AnimateAsyncRunFactory = ['$$rAF', function($$rAF) {
var waitQueue = [];
function waitForTick(fn) {
waitQueue.push(fn);
if (waitQueue.length > 1) return;
$$rAF(function() {
for (var i = 0; i < waitQueue.length; i++) {
waitQueue[i]();
}
waitQueue = [];
});
}
return function() {
var passed = false;
waitForTick(function() {
passed = true;
});
return function(callback) {
passed ? callback() : waitForTick(callback);
};
};
}];
var $$AnimateRunnerFactory = ['$q', '$sniffer', '$$animateAsyncRun',
function($q, $sniffer, $$animateAsyncRun) {
var INITIAL_STATE = 0;
var DONE_PENDING_STATE = 1;
var DONE_COMPLETE_STATE = 2;
AnimateRunner.chain = function(chain, callback) {
var index = 0;
next();
function next() {
if (index === chain.length) {
callback(true);
return;
}
chain[index](function(response) {
if (response === false) {
callback(false);
return;
}
index++;
next();
});
}
};
AnimateRunner.all = function(runners, callback) {
var count = 0;
var status = true;
forEach(runners, function(runner) {
runner.done(onProgress);
});
function onProgress(response) {
status = status && response;
if (++count === runners.length) {
callback(status);
}
}
};
function AnimateRunner(host) {
this.setHost(host);
this._doneCallbacks = [];
this._runInAnimationFrame = $$animateAsyncRun();
this._state = 0;
}
AnimateRunner.prototype = {
setHost: function(host) {
this.host = host || {};
},
done: function(fn) {
if (this._state === DONE_COMPLETE_STATE) {
fn();
} else {
this._doneCallbacks.push(fn);
}
},
progress: noop,
getPromise: function() {
if (!this.promise) {
var self = this;
this.promise = $q(function(resolve, reject) {
self.done(function(status) {
status === false ? reject() : resolve();
});
});
}
return this.promise;
},
then: function(resolveHandler, rejectHandler) {
return this.getPromise().then(resolveHandler, rejectHandler);
},
'catch': function(handler) {
return this.getPromise()['catch'](handler);
},
'finally': function(handler) {
return this.getPromise()['finally'](handler);
},
pause: function() {
if (this.host.pause) {
this.host.pause();
}
},
resume: function() {
if (this.host.resume) {
this.host.resume();
}
},
end: function() {
if (this.host.end) {
this.host.end();
}
this._resolve(true);
},
cancel: function() {
if (this.host.cancel) {
this.host.cancel();
}
this._resolve(false);
},
complete: function(response) {
var self = this;
if (self._state === INITIAL_STATE) {
self._state = DONE_PENDING_STATE;
self._runInAnimationFrame(function() {
self._resolve(response);
});
}
},
_resolve: function(response) {
if (this._state !== DONE_COMPLETE_STATE) {
forEach(this._doneCallbacks, function(fn) {
fn(response);
});
this._doneCallbacks.length = 0;
this._state = DONE_COMPLETE_STATE;
}
}
};
return AnimateRunner;
}];
+10
View File
@@ -135,6 +135,12 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
options.tempClasses = null;
}
var prepareClassName;
if (isStructural) {
prepareClassName = 'ng-' + event + PREPARE_CLASS_SUFFIX;
$$jqLite.addClass(element, prepareClassName);
}
animationQueue.push({
// this data is used by the postDigest code and passed into
// the driver step function
@@ -357,6 +363,10 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
if (tempClasses) {
$$jqLite.addClass(element, tempClasses);
}
if (prepareClassName) {
$$jqLite.removeClass(element, prepareClassName);
prepareClassName = null;
}
}
function updateAnimationRunners(animation, newRunner) {
-7
View File
@@ -1,7 +0,0 @@
'use strict';
function $$BodyProvider() {
this.$get = ['$document', function($document) {
return jqLite($document[0].body);
}];
}
+41 -20
View File
@@ -2,11 +2,10 @@
/* global angularAnimateModule: true,
$$BodyProvider,
ngAnimateSwapDirective,
$$AnimateAsyncRunFactory,
$$rAFSchedulerFactory,
$$AnimateChildrenDirective,
$$AnimateRunnerFactory,
$$AnimateQueueProvider,
$$AnimationProvider,
$AnimateCssProvider,
@@ -255,6 +254,34 @@
* the CSS class once an animation has completed.)
*
*
* ### The `ng-[event]-prepare` class
*
* This is a special class that can be used to prevent unwanted flickering / flash of content before
* the actual animation starts. The class is added as soon as an animation is initialized, but removed
* before the actual animation starts (after waiting for a $digest).
* It is also only added for *structural* animations (`enter`, `move`, and `leave`).
*
* In practice, flickering can appear when nesting elements with structural animations such as `ngIf`
* into elements that have class-based animations such as `ngClass`.
*
* ```html
* <div ng-class="{red: myProp}">
* <div ng-class="{blue: myProp}">
* <div class="message" ng-if="myProp"></div>
* </div>
* </div>
* ```
*
* It is possible that during the `enter` animation, the `.message` div will be briefly visible before it starts animating.
* In that case, you can add styles to the CSS that make sure the element stays hidden before the animation starts:
*
* ```css
* .message.ng-enter-prepare {
* opacity: 0;
* }
*
* ```
*
* ## JavaScript-based Animations
*
* ngAnimate also allows for animations to be consumed by JavaScript code. The approach is similar to CSS-based animations (where there is a shared
@@ -291,7 +318,7 @@
* jQuery(element).fadeOut(1000, doneFn);
* }
* }
* }]
* }]);
* ```
*
* The nice thing about JS-based animations is that we can inject other services and make use of advanced animation libraries such as
@@ -322,7 +349,7 @@
* // do some cool animation and call the doneFn
* }
* }
* }]
* }]);
* ```
*
* ## CSS + JS Animations Together
@@ -344,7 +371,7 @@
* jQuery(element).slideIn(1000, doneFn);
* }
* }
* }]
* }]);
* ```
*
* ```css
@@ -364,16 +391,15 @@
* ```js
* myModule.animation('.slide', ['$animateCss', function($animateCss) {
* return {
* enter: function(element, doneFn) {
* enter: function(element) {
* // this will trigger `.slide.ng-enter` and `.slide.ng-enter-active`.
* var runner = $animateCss(element, {
* return $animateCss(element, {
* event: 'enter',
* structural: true
* }).start();
* runner.done(doneFn);
* });
* }
* }
* }]
* }]);
* ```
*
* The nice thing here is that we can save bandwidth by sticking to our CSS-based animation code and we don't need to rely on a 3rd-party animation framework.
@@ -385,19 +411,17 @@
* ```js
* myModule.animation('.slide', ['$animateCss', function($animateCss) {
* return {
* enter: function(element, doneFn) {
* var runner = $animateCss(element, {
* enter: function(element) {
* return $animateCss(element, {
* event: 'enter',
* structural: true,
* addClass: 'maroon-setting',
* from: { height:0 },
* to: { height: 200 }
* }).start();
*
* runner.done(doneFn);
* });
* }
* }
* }]
* }]);
* ```
*
* Now we can fill in the rest via our transition CSS code:
@@ -742,14 +766,11 @@
* Click here {@link ng.$animate to learn more about animations with `$animate`}.
*/
angular.module('ngAnimate', [])
.provider('$$body', $$BodyProvider)
.directive('ngAnimateSwap', ngAnimateSwapDirective)
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
.factory('$$rAFScheduler', $$rAFSchedulerFactory)
.factory('$$AnimateRunner', $$AnimateRunnerFactory)
.factory('$$animateAsyncRun', $$AnimateAsyncRunFactory)
.provider('$$animateQueue', $$AnimateQueueProvider)
.provider('$$animation', $$AnimationProvider)
+110
View File
@@ -0,0 +1,110 @@
'use strict';
/**
* @ngdoc directive
* @name ngAnimateSwap
* @restrict A
* @scope
*
* @description
*
* ngAnimateSwap is a animation-oriented directive that allows for the container to
* be removed and entered in whenever the associated expression changes. A
* common usecase for this directive is a rotating banner component which
* contains one image being present at a time. When the active image changes
* then the old image will perform a `leave` animation and the new element
* will be inserted via an `enter` animation.
*
* @example
* <example name="ngAnimateSwap-directive" module="ngAnimateSwapExample"
* deps="angular-animate.js"
* animations="true" fixBase="true">
* <file name="index.html">
* <div class="container" ng-controller="AppCtrl">
* <div ng-animate-swap="number" class="cell swap-animation" ng-class="colorClass(number)">
* {{ number }}
* </div>
* </div>
* </file>
* <file name="script.js">
* angular.module('ngAnimateSwapExample', ['ngAnimate'])
* .controller('AppCtrl', ['$scope', '$interval', function($scope, $interval) {
* $scope.number = 0;
* $interval(function() {
* $scope.number++;
* }, 1000);
*
* var colors = ['red','blue','green','yellow','orange'];
* $scope.colorClass = function(number) {
* return colors[number % colors.length];
* };
* }]);
* </file>
* <file name="animations.css">
* .container {
* height:250px;
* width:250px;
* position:relative;
* overflow:hidden;
* border:2px solid black;
* }
* .container .cell {
* font-size:150px;
* text-align:center;
* line-height:250px;
* position:absolute;
* top:0;
* left:0;
* right:0;
* border-bottom:2px solid black;
* }
* .swap-animation.ng-enter, .swap-animation.ng-leave {
* transition:0.5s linear all;
* }
* .swap-animation.ng-enter {
* top:-250px;
* }
* .swap-animation.ng-enter-active {
* top:0px;
* }
* .swap-animation.ng-leave {
* top:0px;
* }
* .swap-animation.ng-leave-active {
* top:250px;
* }
* .red { background:red; }
* .green { background:green; }
* .blue { background:blue; }
* .yellow { background:yellow; }
* .orange { background:orange; }
* </file>
* </example>
*/
var ngAnimateSwapDirective = ['$animate', '$rootScope', function($animate, $rootScope) {
return {
restrict: 'A',
transclude: 'element',
terminal: true,
priority: 600, // we use 600 here to ensure that the directive is caught before others
link: function(scope, $element, attrs, ctrl, $transclude) {
var previousElement, previousScope;
scope.$watchCollection(attrs.ngAnimateSwap || attrs['for'], function(value) {
if (previousElement) {
$animate.leave(previousElement);
}
if (previousScope) {
previousScope.$destroy();
previousScope = null;
}
if (value || value === 0) {
previousScope = scope.$new();
$transclude(previousScope, function(element) {
previousElement = element;
$animate.enter(element, null, $element);
});
}
});
}
};
}];
+2
View File
@@ -2,6 +2,7 @@
/* jshint ignore:start */
var noop = angular.noop;
var copy = angular.copy;
var extend = angular.extend;
var jqLite = angular.element;
var forEach = angular.forEach;
@@ -20,6 +21,7 @@ var ADD_CLASS_SUFFIX = '-add';
var REMOVE_CLASS_SUFFIX = '-remove';
var EVENT_CLASS_PREFIX = 'ng-';
var ACTIVE_CLASS_SUFFIX = '-active';
var PREPARE_CLASS_SUFFIX = '-prepare';
var NG_ANIMATE_CLASSNAME = 'ng-animate';
var NG_ANIMATE_CHILDREN_DATA = '$$ngAnimateChildren';
+2 -1
View File
@@ -235,7 +235,8 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
}
},
post: function(scope, elem, attr, ngModel) {
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem);
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem)
&& !isNodeOneOf(elem, nodeBlackList);
function ngAriaWatchModelValue() {
return ngModel.$modelValue;
+14
View File
@@ -62,6 +62,20 @@ $provide.value("$locale", {
"Nov",
"Des"
],
"STANDALONEMONTH": [
"Januarie",
"Februarie",
"Maart",
"April",
"Mei",
"Junie",
"Julie",
"Augustus",
"September",
"Oktober",
"November",
"Desember"
],
"WEEKENDRANGE": [
5,
6
+14
View File
@@ -62,6 +62,20 @@ $provide.value("$locale", {
"Nov",
"Des"
],
"STANDALONEMONTH": [
"Januarie",
"Februarie",
"Maart",
"April",
"Mei",
"Junie",
"Julie",
"Augustus",
"September",
"Oktober",
"November",
"Desember"
],
"WEEKENDRANGE": [
5,
6
+14
View File
@@ -62,6 +62,20 @@ $provide.value("$locale", {
"Nov",
"Des"
],
"STANDALONEMONTH": [
"Januarie",
"Februarie",
"Maart",
"April",
"Mei",
"Junie",
"Julie",
"Augustus",
"September",
"Oktober",
"November",
"Desember"
],
"WEEKENDRANGE": [
5,
6
+14
View File
@@ -80,6 +80,20 @@ $provide.value("$locale", {
"kaa",
"fwo"
],
"STANDALONEMONTH": [
"ndz\u0254\u0300\u014b\u0254\u0300n\u00f9m",
"ndz\u0254\u0300\u014b\u0254\u0300k\u0197\u0300z\u00f9\u0294",
"ndz\u0254\u0300\u014b\u0254\u0300t\u0197\u0300d\u0289\u0300gh\u00e0",
"ndz\u0254\u0300\u014b\u0254\u0300t\u01ceaf\u0289\u0304gh\u0101",
"ndz\u0254\u0300\u014b\u00e8s\u00e8e",
"ndz\u0254\u0300\u014b\u0254\u0300nz\u00f9gh\u00f2",
"ndz\u0254\u0300\u014b\u0254\u0300d\u00f9mlo",
"ndz\u0254\u0300\u014b\u0254\u0300kw\u00eef\u0254\u0300e",
"ndz\u0254\u0300\u014b\u0254\u0300t\u0197\u0300f\u0289\u0300gh\u00e0dzugh\u00f9",
"ndz\u0254\u0300\u014b\u0254\u0300gh\u01d4uwel\u0254\u0300m",
"ndz\u0254\u0300\u014b\u0254\u0300chwa\u0294\u00e0kaa wo",
"ndz\u0254\u0300\u014b\u00e8fw\u00f2o"
],
"WEEKENDRANGE": [
5,
6
+14
View File
@@ -80,6 +80,20 @@ $provide.value("$locale", {
"kaa",
"fwo"
],
"STANDALONEMONTH": [
"ndz\u0254\u0300\u014b\u0254\u0300n\u00f9m",
"ndz\u0254\u0300\u014b\u0254\u0300k\u0197\u0300z\u00f9\u0294",
"ndz\u0254\u0300\u014b\u0254\u0300t\u0197\u0300d\u0289\u0300gh\u00e0",
"ndz\u0254\u0300\u014b\u0254\u0300t\u01ceaf\u0289\u0304gh\u0101",
"ndz\u0254\u0300\u014b\u00e8s\u00e8e",
"ndz\u0254\u0300\u014b\u0254\u0300nz\u00f9gh\u00f2",
"ndz\u0254\u0300\u014b\u0254\u0300d\u00f9mlo",
"ndz\u0254\u0300\u014b\u0254\u0300kw\u00eef\u0254\u0300e",
"ndz\u0254\u0300\u014b\u0254\u0300t\u0197\u0300f\u0289\u0300gh\u00e0dzugh\u00f9",
"ndz\u0254\u0300\u014b\u0254\u0300gh\u01d4uwel\u0254\u0300m",
"ndz\u0254\u0300\u014b\u0254\u0300chwa\u0294\u00e0kaa wo",
"ndz\u0254\u0300\u014b\u00e8fw\u00f2o"
],
"WEEKENDRANGE": [
5,
6
+14
View File
@@ -80,6 +80,20 @@ $provide.value("$locale", {
"\u0186-O",
"M-\u0186"
],
"STANDALONEMONTH": [
"Sanda-\u0186p\u025bp\u0254n",
"Kwakwar-\u0186gyefuo",
"Eb\u0254w-\u0186benem",
"Eb\u0254bira-Oforisuo",
"Esusow Aketseaba-K\u0254t\u0254nimba",
"Obirade-Ay\u025bwohomumu",
"Ay\u025bwoho-Kitawonsa",
"Difuu-\u0186sandaa",
"Fankwa-\u0190b\u0254",
"\u0186b\u025bs\u025b-Ahinime",
"\u0186ber\u025bf\u025bw-Obubuo",
"Mumu-\u0186p\u025bnimba"
],
"WEEKENDRANGE": [
5,
6

Some files were not shown because too many files have changed in this diff Show More